@phin-tech/dottie 0.10.0 → 0.11.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/PARITY.md +3 -1
- package/README.md +22 -2
- package/dist/compile.js +33 -13
- package/dist/dottie-compile.mjs +46 -15
- package/dist/dottie-logic.min.js +2171 -0
- package/dist/dottie-player.js +550 -148
- package/dist/dottie-player.min.js +58 -56
- package/dist/dottie-sim.min.js +5 -4
- package/dist/dottie.js +550 -148
- package/dist/types/clicks.d.ts +13 -5
- package/dist/types/compile.d.ts +8 -1
- package/dist/types/expression.d.ts +51 -0
- package/dist/types/index.d.ts +1 -1
- 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 +4 -3
- package/dist/types/model.d.ts +10 -3
- package/dist/types/player.d.ts +18 -4
- package/dist/types/state.d.ts +14 -13
- package/package.json +3 -1
package/PARITY.md
CHANGED
|
@@ -24,6 +24,8 @@ fixture, so the gap is visible rather than silent.
|
|
|
24
24
|
| Ports and edge-following, rotated shapes, free ends | Supported | [edge/connectors](../fixtures/edge/connectors.dottie) | |
|
|
25
25
|
| Connectors and labels on outlines (ADR 0001) | Supported | [edge/extensions](../fixtures/edge/extensions.dottie) | Edge-following meets a `.path`'s actual outline, not its box (ray against 16-step flattened curves). Endpoints pinned with `portIndex` attach to the element's `ports`. A path's label goes in its `labelRect`. |
|
|
26
26
|
| Extension shapes as stored (ADR 0001) | Supported | [edge/extensions](../fixtures/edge/extensions.dottie) | Drawn from the stored path with no extension installed. The `extension` field is kept as metadata and nothing runs. Parameters, commands and handles are Dottie-only. |
|
|
27
|
+
| Click logic (`logic`, a click's `run`) | Supported | [edge/logic](../fixtures/edge/logic.dottie) | One engine for both: `logic.ts` (QuickJS in WebAssembly), run by `LogicEngine.swift` in JavaScriptCore. Runs only with `allow-logic` (ADR 0007). |
|
|
28
|
+
| Number variables, expressions in conditions and clicks, `{value}` in labels | Supported | [edge/counters](../fixtures/edge/counters.dottie), [expressions.json](../fixtures/expressions.json) | One grammar in `Expression.swift` and `expression.ts` (ADR 0007). |
|
|
27
29
|
| Containers and swimlanes | Supported | [edge/containers](../fixtures/edge/containers.dottie) | Drawn from the stored frame and title band (`labelRect`); the app keeps them fitted (ADR 0006). |
|
|
28
30
|
| Themes of a page's own, and palette colors by name | Supported | [edge/palette](../fixtures/edge/palette.dottie) | Names are resolved when a file is read, by the same rules in `Theme.swift` and `theme.ts` (ADR 0005). |
|
|
29
31
|
| Elbows that go round shapes (`avoid`) | Supported | [edge/elbow-around](../fixtures/edge/elbow-around.dottie) | The same grid router, point for point (`ElbowRouter.swift`, `elbow.ts`). |
|
|
@@ -57,6 +59,6 @@ fixture, so the gap is visible rather than silent.
|
|
|
57
59
|
| Clicks that set and toggle variables | Supported | [edge/state](../fixtures/edge/state.dottie) | Change what shows without touching the playhead |
|
|
58
60
|
| Looping scenes (`loop` on a marker) | Supported | [edge/state](../fixtures/edge/state.dottie) | No stop on entering, round from the scene's end to its start |
|
|
59
61
|
| Links out of the drawing (`url`) | Supported | [edge/links](../fixtures/edge/links.dottie) | A web or mail `url` opens when clicked if there is no `click`, and SVG export wraps it in `<a>`; a link to another drawing does nothing on the web |
|
|
60
|
-
| Links between shapes and pages | Partial | [edge/clicks](../fixtures/edge/clicks.dottie) | A link to a shape on another page shows that page; one on the same page does nothing. The HTML export
|
|
62
|
+
| Links between shapes and pages | Partial | [edge/clicks](../fixtures/edge/clicks.dottie) | A link to a shape on another page shows that page; one on the same page does nothing. The HTML export of one page has neither; with every page (`--all-pages`, `history`) it shows the page at its own address, and split a page per file (`--split`, `links`) it goes to that page's file |
|
|
61
63
|
| Editing | Missing | | |
|
|
62
64
|
| Ticking task boxes in notes | Missing | | Drawn, not clickable |
|
package/README.md
CHANGED
|
@@ -28,6 +28,8 @@ prefer reduced motion. A still page is just the drawing.
|
|
|
28
28
|
| `clean` | Draw precisely instead of sketchy |
|
|
29
29
|
| `no-controls`, `no-autoplay`, `no-loop` | |
|
|
30
30
|
| `label` | The accessible name; the page name by default |
|
|
31
|
+
| `history` | Each page a link shows gets its own address (`#` and the page's name), so Back and Forward go between pages and a link can open any of them. One such player to a page |
|
|
32
|
+
| `links` | JSON from the id of an element a link points at to the web page that shows it (`{"…": "details.html"}`), for a drawing exported one page per file: a link to another page goes there. Only relative `.html` paths are followed |
|
|
31
33
|
|
|
32
34
|
Elements the drawing makes clickable work with the mouse and the keyboard (Tab, then Enter or Space). A click plays
|
|
33
35
|
a named scene on to the next one, opens a link in a new tab (only `http`, `https` and `mailto`), or shows the page a
|
|
@@ -43,7 +45,8 @@ region a click took down. A looping scene plays round until a click or next leav
|
|
|
43
45
|
|
|
44
46
|
The element has `play()`, `pause()`, `nextScene()`, `prevScene()`, `activate(elementId)`, `currentTime`
|
|
45
47
|
(settable), `duration`, `paused`, `variables` and `setState({name: value})`, and fires `load`, `frame`, `action`,
|
|
46
|
-
`state` (with `{variables}`, after a change)
|
|
48
|
+
`state` (with `{variables}`, after a change), `page` (with `{index, name, first}`, when the page shown changes) and
|
|
49
|
+
`error` events. Its page color comes from the drawing's theme; set
|
|
47
50
|
`--dottie-radius` to round it, and style its parts with `::part(stage)` and `::part(controls)`.
|
|
48
51
|
|
|
49
52
|
Without modules, `dist/dottie-player.min.js` is a classic script that registers the element and sets
|
|
@@ -56,6 +59,21 @@ when it is showing. The classic script starts the worker from its own source (a
|
|
|
56
59
|
loads `dottie-player.min.js` from beside itself, so host the two together. Where a worker can't start, as under a
|
|
57
60
|
`worker-src` policy that refuses it, the player works it out in place as before. Small drawings always do.
|
|
58
61
|
|
|
62
|
+
**Click logic.** Variables can be numbers, and conditions and clicks are small expressions (`count >= 3`,
|
|
63
|
+
`"count + 1"`, `{count}` in a label). For more, a page can carry `logic`: JavaScript handlers a click `run`s, each
|
|
64
|
+
getting the variables and the click and returning new values. The player runs them only with `allow-logic`:
|
|
65
|
+
|
|
66
|
+
```html
|
|
67
|
+
<dottie-player src="game.dottie" allow-logic></dottie-player>
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Each click's handler runs in a fresh QuickJS sandbox compiled to WebAssembly, never in your page's JavaScript: it has
|
|
71
|
+
no `window`, `document`, network, timers, `Date` or `Math.random`, and a time and memory limit, so the same click on
|
|
72
|
+
the same state gives the same result everywhere. A handler that fails leaves the state as it was and fires
|
|
73
|
+
`logicerror` (with `{element, handler, error}`). Without `allow-logic`, a click does its own `set` and `toggle`
|
|
74
|
+
instead. The engine (`dottie-logic.min.js`, about 850 KB) is loaded from beside the player only when a page with
|
|
75
|
+
logic is clicked, so host it with the player; `dottie-compile --allow-logic` puts it in the compiled script instead.
|
|
76
|
+
|
|
59
77
|
**Fonts.** Text in Dottie's handwriting fonts (Caveat, Patrick Hand, Architects Daughter; SIL Open Font License)
|
|
60
78
|
is drawn in them: the player loads the ones a drawing uses from `fonts/` beside its own script, as `dist/fonts` on
|
|
61
79
|
npm and the CDNs. If you host the player yourself, copy that folder with it, or call `Dottie.setFontBase(url)`
|
|
@@ -78,7 +96,9 @@ handwriting fonts the drawing uses (15 to 30 KB each), so one file shows it anyw
|
|
|
78
96
|
```
|
|
79
97
|
|
|
80
98
|
`dottie-compile` takes `--page`, `--at`, `--clean`, `--no-controls`, `--no-autoplay`, `--no-loop` and `--label`;
|
|
81
|
-
only the chosen page (and its pictures) goes in.
|
|
99
|
+
only the chosen page (and its pictures) goes in. With `--all-pages` every page goes in, for a drawing people drill
|
|
100
|
+
into: a link to a shape on another page shows that page, each page gets its own address (`tour.html#Details`), and
|
|
101
|
+
Back goes back (`--page` is then the page to start on). The Dottie Mac app's command-line tool writes the same file:
|
|
82
102
|
`dottie render tour.dottie -f js`. From code: `import { compile } from "@phin-tech/dottie/compile"`.
|
|
83
103
|
|
|
84
104
|
## Use the library
|
package/dist/compile.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! dottie.js 0.
|
|
1
|
+
/*! dottie.js 0.11.1 | Apache-2.0 | https://github.com/phin-tech/dottie */
|
|
2
2
|
var __defProp = Object.defineProperty;
|
|
3
3
|
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
4
4
|
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
@@ -109,12 +109,12 @@ function resolvingPalette(page) {
|
|
|
109
109
|
}
|
|
110
110
|
|
|
111
111
|
// src/model.ts
|
|
112
|
-
var SUPPORTED_VERSION =
|
|
112
|
+
var SUPPORTED_VERSION = 3;
|
|
113
113
|
var KINDS = ["rectangle", "ellipse", "diamond", "text", "connector", "freehand", "path", "note", "image", "symbol"];
|
|
114
114
|
var EASES = ["linear", "easeIn", "easeOut", "easeInOut", "hold", "spring", "bounce", "elastic"];
|
|
115
115
|
var KNOWN_FIELDS = {
|
|
116
116
|
file: ["version", "pages", "images"],
|
|
117
|
-
page: ["id", "name", "layers", "elements", "theme", "customTheme", "guidesX", "guidesY", "guidesLocked", "timeline", "symbols", "variables", "flows"],
|
|
117
|
+
page: ["id", "name", "layers", "elements", "theme", "customTheme", "logic", "guidesX", "guidesY", "guidesLocked", "timeline", "symbols", "variables", "flows"],
|
|
118
118
|
flow: ["id", "from", "to", "every", "speed", "color", "size", "when", "spread", "volume", "overrides"],
|
|
119
119
|
flowOverride: ["when", "volume"],
|
|
120
120
|
symbol: ["id", "name", "width", "height", "layers", "elements", "timeline", "button", "params"],
|
|
@@ -183,7 +183,7 @@ var KNOWN_FIELDS = {
|
|
|
183
183
|
repeat: ["start", "end", "count"],
|
|
184
184
|
cameraKey: ["t", "x", "y", "width", "height", "ease"],
|
|
185
185
|
marker: ["t", "name", "loop"],
|
|
186
|
-
click: ["scene", "url", "set", "toggle", "reset"],
|
|
186
|
+
click: ["scene", "url", "set", "toggle", "reset", "run"],
|
|
187
187
|
hover: ["stroke", "fill", "opacity"],
|
|
188
188
|
override: ["when", "stroke", "fill", "textColor", "opacity", "strokeWidth", "dash", "text", "reroute", "offline", "cost", "capacity", "delay"]
|
|
189
189
|
};
|
|
@@ -430,7 +430,7 @@ var Reader = class {
|
|
|
430
430
|
variables(v) {
|
|
431
431
|
if (!isObj(v)) return null;
|
|
432
432
|
const out = {};
|
|
433
|
-
for (const [k, b] of Object.entries(v)) if (typeof b === "boolean") out[k] = b;
|
|
433
|
+
for (const [k, b] of Object.entries(v)) if (typeof b === "boolean" || typeof b === "number" && Number.isFinite(b)) out[k] = b;
|
|
434
434
|
return Object.keys(out).length > 0 ? out : null;
|
|
435
435
|
}
|
|
436
436
|
click(v) {
|
|
@@ -442,12 +442,22 @@ var Reader = class {
|
|
|
442
442
|
let set = null;
|
|
443
443
|
if (isObj(v.set)) {
|
|
444
444
|
set = {};
|
|
445
|
-
for (const [k, b] of Object.entries(v.set))
|
|
445
|
+
for (const [k, b] of Object.entries(v.set)) {
|
|
446
|
+
if (isName(k) && (typeof b === "boolean" || typeof b === "string" || typeof b === "number" && Number.isFinite(b))) set[k] = b;
|
|
447
|
+
}
|
|
446
448
|
if (Object.keys(set).length === 0) set = null;
|
|
447
449
|
}
|
|
448
450
|
const raw = typeof v.toggle === "string" ? [v.toggle] : Array.isArray(v.toggle) && v.toggle.every((x) => typeof x === "string") ? v.toggle : null;
|
|
449
451
|
const toggle = raw ? raw.filter(isName) : null;
|
|
450
|
-
|
|
452
|
+
const run = str(v.run);
|
|
453
|
+
return {
|
|
454
|
+
scene: str(v.scene) ?? null,
|
|
455
|
+
url: safe,
|
|
456
|
+
set,
|
|
457
|
+
toggle: toggle && toggle.length > 0 ? toggle : null,
|
|
458
|
+
reset: v.reset === true ? true : null,
|
|
459
|
+
run: run !== void 0 && isName(run) ? run : null
|
|
460
|
+
};
|
|
451
461
|
}
|
|
452
462
|
playback(v) {
|
|
453
463
|
const s = str(v);
|
|
@@ -610,6 +620,7 @@ var Reader = class {
|
|
|
610
620
|
elements: (Array.isArray(v.elements) ? v.elements : []).map((e) => this.element(e, images)).filter((e) => e !== null),
|
|
611
621
|
theme: str(v.theme) ?? null,
|
|
612
622
|
customTheme: this.customTheme(v.customTheme),
|
|
623
|
+
logic: typeof v.logic === "string" && v.logic.length > 0 ? v.logic : null,
|
|
613
624
|
timeline: this.timeline(v.timeline),
|
|
614
625
|
symbols: Array.isArray(v.symbols) ? v.symbols.map((x) => this.symbol(x, images)).filter((x) => x !== null) : null,
|
|
615
626
|
variables: this.variables(v.variables),
|
|
@@ -751,6 +762,11 @@ function playerAttributes(options) {
|
|
|
751
762
|
if (options.noAutoplay) out["no-autoplay"] = "";
|
|
752
763
|
if (options.noLoop) out["no-loop"] = "";
|
|
753
764
|
if (options.label !== void 0) out.label = options.label;
|
|
765
|
+
if (options.logicEngine) out["allow-logic"] = "";
|
|
766
|
+
if (options.allPages) {
|
|
767
|
+
out.history = "";
|
|
768
|
+
if (options.page !== void 0) out.page = String(options.page);
|
|
769
|
+
}
|
|
754
770
|
return out;
|
|
755
771
|
}
|
|
756
772
|
function compile(source, runtime, options = {}) {
|
|
@@ -765,28 +781,32 @@ function compile(source, runtime, options = {}) {
|
|
|
765
781
|
const file = parseDottie(raw);
|
|
766
782
|
const json = raw;
|
|
767
783
|
const i = pageIndex(file, options.page === void 0 ? null : String(options.page));
|
|
768
|
-
const
|
|
784
|
+
const chosen = options.allPages ? json.pages.map((_, k) => k) : [i];
|
|
785
|
+
const pages = chosen.map((k) => json.pages[k]);
|
|
769
786
|
const refs = /* @__PURE__ */ new Set();
|
|
770
|
-
JSON.stringify(
|
|
787
|
+
JSON.stringify(pages, (k, v) => {
|
|
771
788
|
if (k === "ref" && typeof v === "string") refs.add(v);
|
|
772
789
|
return v;
|
|
773
790
|
});
|
|
774
791
|
const images = Object.fromEntries(Object.entries(json.images ?? {}).filter(([k]) => refs.has(k)));
|
|
775
|
-
const data = { version: json.version, pages
|
|
792
|
+
const data = { version: json.version, pages };
|
|
776
793
|
if (Object.keys(images).length) data.images = images;
|
|
777
794
|
const fonts = {};
|
|
778
|
-
|
|
795
|
+
const families2 = new Set(chosen.flatMap((k) => options.fontData ? usedFonts(file.pages[k]) : []));
|
|
796
|
+
for (const family of families2) {
|
|
779
797
|
for (const face of fontFaces(family)) {
|
|
780
798
|
const b64 = options.fontData(family, face);
|
|
781
799
|
if (b64) (fonts[family] ?? (fonts[family] = {}))[face] = b64;
|
|
782
800
|
}
|
|
783
801
|
}
|
|
802
|
+
const logic = chosen.some((k) => file.pages[k].logic);
|
|
784
803
|
const fill = {
|
|
785
804
|
DOTTIE_DATA: () => scriptJSON(data),
|
|
786
|
-
DOTTIE_OPTIONS: () => scriptJSON(playerAttributes(options)),
|
|
805
|
+
DOTTIE_OPTIONS: () => scriptJSON(playerAttributes(logic ? options : { ...options, logicEngine: void 0 })),
|
|
787
806
|
DOTTIE_FONTS: () => scriptJSON(fonts)
|
|
788
807
|
};
|
|
789
|
-
|
|
808
|
+
const engine = options.logicEngine && logic ? options.logicEngine.trimEnd() + "\n" : "";
|
|
809
|
+
return engine + runtime.trimEnd() + "\n" + LOADER.replace(/\bDOTTIE_(?:DATA|OPTIONS|FONTS)\b/g, (m) => fill[m]());
|
|
790
810
|
}
|
|
791
811
|
export {
|
|
792
812
|
LOADER,
|
package/dist/dottie-compile.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
/*! dottie.js 0.
|
|
2
|
+
/*! dottie.js 0.11.1 | Apache-2.0 | https://github.com/phin-tech/dottie */
|
|
3
3
|
var __defProp = Object.defineProperty;
|
|
4
4
|
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
5
5
|
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
@@ -117,12 +117,12 @@ function resolvingPalette(page) {
|
|
|
117
117
|
}
|
|
118
118
|
|
|
119
119
|
// src/model.ts
|
|
120
|
-
var SUPPORTED_VERSION =
|
|
120
|
+
var SUPPORTED_VERSION = 3;
|
|
121
121
|
var KINDS = ["rectangle", "ellipse", "diamond", "text", "connector", "freehand", "path", "note", "image", "symbol"];
|
|
122
122
|
var EASES = ["linear", "easeIn", "easeOut", "easeInOut", "hold", "spring", "bounce", "elastic"];
|
|
123
123
|
var KNOWN_FIELDS = {
|
|
124
124
|
file: ["version", "pages", "images"],
|
|
125
|
-
page: ["id", "name", "layers", "elements", "theme", "customTheme", "guidesX", "guidesY", "guidesLocked", "timeline", "symbols", "variables", "flows"],
|
|
125
|
+
page: ["id", "name", "layers", "elements", "theme", "customTheme", "logic", "guidesX", "guidesY", "guidesLocked", "timeline", "symbols", "variables", "flows"],
|
|
126
126
|
flow: ["id", "from", "to", "every", "speed", "color", "size", "when", "spread", "volume", "overrides"],
|
|
127
127
|
flowOverride: ["when", "volume"],
|
|
128
128
|
symbol: ["id", "name", "width", "height", "layers", "elements", "timeline", "button", "params"],
|
|
@@ -191,7 +191,7 @@ var KNOWN_FIELDS = {
|
|
|
191
191
|
repeat: ["start", "end", "count"],
|
|
192
192
|
cameraKey: ["t", "x", "y", "width", "height", "ease"],
|
|
193
193
|
marker: ["t", "name", "loop"],
|
|
194
|
-
click: ["scene", "url", "set", "toggle", "reset"],
|
|
194
|
+
click: ["scene", "url", "set", "toggle", "reset", "run"],
|
|
195
195
|
hover: ["stroke", "fill", "opacity"],
|
|
196
196
|
override: ["when", "stroke", "fill", "textColor", "opacity", "strokeWidth", "dash", "text", "reroute", "offline", "cost", "capacity", "delay"]
|
|
197
197
|
};
|
|
@@ -438,7 +438,7 @@ var Reader = class {
|
|
|
438
438
|
variables(v) {
|
|
439
439
|
if (!isObj(v)) return null;
|
|
440
440
|
const out = {};
|
|
441
|
-
for (const [k, b] of Object.entries(v)) if (typeof b === "boolean") out[k] = b;
|
|
441
|
+
for (const [k, b] of Object.entries(v)) if (typeof b === "boolean" || typeof b === "number" && Number.isFinite(b)) out[k] = b;
|
|
442
442
|
return Object.keys(out).length > 0 ? out : null;
|
|
443
443
|
}
|
|
444
444
|
click(v) {
|
|
@@ -450,12 +450,22 @@ var Reader = class {
|
|
|
450
450
|
let set = null;
|
|
451
451
|
if (isObj(v.set)) {
|
|
452
452
|
set = {};
|
|
453
|
-
for (const [k, b] of Object.entries(v.set))
|
|
453
|
+
for (const [k, b] of Object.entries(v.set)) {
|
|
454
|
+
if (isName(k) && (typeof b === "boolean" || typeof b === "string" || typeof b === "number" && Number.isFinite(b))) set[k] = b;
|
|
455
|
+
}
|
|
454
456
|
if (Object.keys(set).length === 0) set = null;
|
|
455
457
|
}
|
|
456
458
|
const raw = typeof v.toggle === "string" ? [v.toggle] : Array.isArray(v.toggle) && v.toggle.every((x) => typeof x === "string") ? v.toggle : null;
|
|
457
459
|
const toggle = raw ? raw.filter(isName) : null;
|
|
458
|
-
|
|
460
|
+
const run = str(v.run);
|
|
461
|
+
return {
|
|
462
|
+
scene: str(v.scene) ?? null,
|
|
463
|
+
url: safe,
|
|
464
|
+
set,
|
|
465
|
+
toggle: toggle && toggle.length > 0 ? toggle : null,
|
|
466
|
+
reset: v.reset === true ? true : null,
|
|
467
|
+
run: run !== void 0 && isName(run) ? run : null
|
|
468
|
+
};
|
|
459
469
|
}
|
|
460
470
|
playback(v) {
|
|
461
471
|
const s = str(v);
|
|
@@ -618,6 +628,7 @@ var Reader = class {
|
|
|
618
628
|
elements: (Array.isArray(v.elements) ? v.elements : []).map((e) => this.element(e, images)).filter((e) => e !== null),
|
|
619
629
|
theme: str(v.theme) ?? null,
|
|
620
630
|
customTheme: this.customTheme(v.customTheme),
|
|
631
|
+
logic: typeof v.logic === "string" && v.logic.length > 0 ? v.logic : null,
|
|
621
632
|
timeline: this.timeline(v.timeline),
|
|
622
633
|
symbols: Array.isArray(v.symbols) ? v.symbols.map((x) => this.symbol(x, images)).filter((x) => x !== null) : null,
|
|
623
634
|
variables: this.variables(v.variables),
|
|
@@ -759,6 +770,11 @@ function playerAttributes(options2) {
|
|
|
759
770
|
if (options2.noAutoplay) out["no-autoplay"] = "";
|
|
760
771
|
if (options2.noLoop) out["no-loop"] = "";
|
|
761
772
|
if (options2.label !== void 0) out.label = options2.label;
|
|
773
|
+
if (options2.logicEngine) out["allow-logic"] = "";
|
|
774
|
+
if (options2.allPages) {
|
|
775
|
+
out.history = "";
|
|
776
|
+
if (options2.page !== void 0) out.page = String(options2.page);
|
|
777
|
+
}
|
|
762
778
|
return out;
|
|
763
779
|
}
|
|
764
780
|
function compile(source2, runtime, options2 = {}) {
|
|
@@ -773,32 +789,36 @@ function compile(source2, runtime, options2 = {}) {
|
|
|
773
789
|
const file = parseDottie(raw);
|
|
774
790
|
const json = raw;
|
|
775
791
|
const i = pageIndex(file, options2.page === void 0 ? null : String(options2.page));
|
|
776
|
-
const
|
|
792
|
+
const chosen = options2.allPages ? json.pages.map((_, k) => k) : [i];
|
|
793
|
+
const pages = chosen.map((k) => json.pages[k]);
|
|
777
794
|
const refs = /* @__PURE__ */ new Set();
|
|
778
|
-
JSON.stringify(
|
|
795
|
+
JSON.stringify(pages, (k, v) => {
|
|
779
796
|
if (k === "ref" && typeof v === "string") refs.add(v);
|
|
780
797
|
return v;
|
|
781
798
|
});
|
|
782
799
|
const images = Object.fromEntries(Object.entries(json.images ?? {}).filter(([k]) => refs.has(k)));
|
|
783
|
-
const data = { version: json.version, pages
|
|
800
|
+
const data = { version: json.version, pages };
|
|
784
801
|
if (Object.keys(images).length) data.images = images;
|
|
785
802
|
const fonts = {};
|
|
786
|
-
|
|
803
|
+
const families2 = new Set(chosen.flatMap((k) => options2.fontData ? usedFonts(file.pages[k]) : []));
|
|
804
|
+
for (const family of families2) {
|
|
787
805
|
for (const face of fontFaces(family)) {
|
|
788
806
|
const b64 = options2.fontData(family, face);
|
|
789
807
|
if (b64) (fonts[family] ?? (fonts[family] = {}))[face] = b64;
|
|
790
808
|
}
|
|
791
809
|
}
|
|
810
|
+
const logic = chosen.some((k) => file.pages[k].logic);
|
|
792
811
|
const fill = {
|
|
793
812
|
DOTTIE_DATA: () => scriptJSON(data),
|
|
794
|
-
DOTTIE_OPTIONS: () => scriptJSON(playerAttributes(options2)),
|
|
813
|
+
DOTTIE_OPTIONS: () => scriptJSON(playerAttributes(logic ? options2 : { ...options2, logicEngine: void 0 })),
|
|
795
814
|
DOTTIE_FONTS: () => scriptJSON(fonts)
|
|
796
815
|
};
|
|
797
|
-
|
|
816
|
+
const engine = options2.logicEngine && logic ? options2.logicEngine.trimEnd() + "\n" : "";
|
|
817
|
+
return engine + runtime.trimEnd() + "\n" + LOADER.replace(/\bDOTTIE_(?:DATA|OPTIONS|FONTS)\b/g, (m) => fill[m]());
|
|
798
818
|
}
|
|
799
819
|
|
|
800
820
|
// src/index.ts
|
|
801
|
-
var VERSION = "0.
|
|
821
|
+
var VERSION = "0.11.1";
|
|
802
822
|
|
|
803
823
|
// src/cli.ts
|
|
804
824
|
var usage = `dottie-compile ${VERSION}: a Dottie drawing as one script that plays it on any page.
|
|
@@ -812,9 +832,12 @@ USAGE
|
|
|
812
832
|
OPTIONS
|
|
813
833
|
-o, --output <path> Where to write it ("-" for stdout); default drawing.js next to the input
|
|
814
834
|
-p, --page <page> Page name or 1-based number; only that page is included (default the first)
|
|
835
|
+
--all-pages Include every page, with links between them working and Back going back (with
|
|
836
|
+
--page, the page to start on)
|
|
815
837
|
--at <seconds> Show that moment as a still
|
|
816
838
|
--clean Draw precisely instead of sketchy
|
|
817
839
|
--no-controls, --no-autoplay, --no-loop
|
|
840
|
+
--allow-logic let the page's click logic run (ADR 0007); it carries the engine (about 850 KB)
|
|
818
841
|
--label <text> The player's accessible name
|
|
819
842
|
`;
|
|
820
843
|
function fail(message, code = 2) {
|
|
@@ -824,6 +847,7 @@ function fail(message, code = 2) {
|
|
|
824
847
|
}
|
|
825
848
|
var args = process.argv.slice(2);
|
|
826
849
|
var options = {};
|
|
850
|
+
var allowLogic = false;
|
|
827
851
|
var input;
|
|
828
852
|
var output;
|
|
829
853
|
for (let i = 0; i < args.length; i++) {
|
|
@@ -853,6 +877,9 @@ for (let i = 0; i < args.length; i++) {
|
|
|
853
877
|
options.at = t;
|
|
854
878
|
break;
|
|
855
879
|
}
|
|
880
|
+
case "--all-pages":
|
|
881
|
+
options.allPages = true;
|
|
882
|
+
break;
|
|
856
883
|
case "--clean":
|
|
857
884
|
options.clean = true;
|
|
858
885
|
break;
|
|
@@ -865,6 +892,9 @@ for (let i = 0; i < args.length; i++) {
|
|
|
865
892
|
case "--no-loop":
|
|
866
893
|
options.noLoop = true;
|
|
867
894
|
break;
|
|
895
|
+
case "--allow-logic":
|
|
896
|
+
allowLogic = true;
|
|
897
|
+
break;
|
|
868
898
|
case "--label":
|
|
869
899
|
options.label = value();
|
|
870
900
|
break;
|
|
@@ -895,7 +925,8 @@ try {
|
|
|
895
925
|
return void 0;
|
|
896
926
|
}
|
|
897
927
|
};
|
|
898
|
-
const
|
|
928
|
+
const logicEngine = allowLogic ? readFileSync(new URL("./dottie-logic.min.js", import.meta.url), "utf8") : void 0;
|
|
929
|
+
const js = compile(source, runtime, { ...options, fontData, logicEngine });
|
|
899
930
|
if (output === "-") process.stdout.write(js);
|
|
900
931
|
else {
|
|
901
932
|
const target = output ?? input.replace(/\.dottie$/i, "") + ".js";
|