tutuca 0.11.0 → 0.11.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (116) hide show
  1. package/dist/tutuca-cli.js +264 -361
  2. package/dist/tutuca-components.js +175 -175
  3. package/dist/tutuca-dev.ext.js +250 -323
  4. package/dist/tutuca-dev.js +250 -323
  5. package/dist/tutuca-dev.min.js +4 -4
  6. package/dist/tutuca-extra.ext.js +55 -46
  7. package/dist/tutuca-extra.js +55 -46
  8. package/dist/tutuca-extra.min.js +3 -3
  9. package/dist/tutuca-storybook.js +29 -29
  10. package/dist/tutuca.ext.js +55 -46
  11. package/dist/tutuca.js +55 -46
  12. package/dist/tutuca.min.js +3 -3
  13. package/package.json +4 -3
  14. package/skill/margaui/SKILL.md +0 -105
  15. package/skill/margaui/components/accordion.md +0 -127
  16. package/skill/margaui/components/alert.md +0 -174
  17. package/skill/margaui/components/aura.md +0 -97
  18. package/skill/margaui/components/avatar.md +0 -220
  19. package/skill/margaui/components/badge.md +0 -193
  20. package/skill/margaui/components/breadcrumbs.md +0 -103
  21. package/skill/margaui/components/button.md +0 -322
  22. package/skill/margaui/components/calendar.md +0 -67
  23. package/skill/margaui/components/card.md +0 -373
  24. package/skill/margaui/components/carousel.md +0 -387
  25. package/skill/margaui/components/chat.md +0 -171
  26. package/skill/margaui/components/checkbox.md +0 -101
  27. package/skill/margaui/components/collapse.md +0 -172
  28. package/skill/margaui/components/countdown.md +0 -165
  29. package/skill/margaui/components/diff.md +0 -53
  30. package/skill/margaui/components/divider.md +0 -107
  31. package/skill/margaui/components/dock.md +0 -173
  32. package/skill/margaui/components/drawer.md +0 -184
  33. package/skill/margaui/components/dropdown.md +0 -388
  34. package/skill/margaui/components/fab.md +0 -346
  35. package/skill/margaui/components/fieldset.md +0 -88
  36. package/skill/margaui/components/file-input.md +0 -84
  37. package/skill/margaui/components/filter.md +0 -52
  38. package/skill/margaui/components/footer.md +0 -583
  39. package/skill/margaui/components/hero.md +0 -135
  40. package/skill/margaui/components/hover-3d.md +0 -129
  41. package/skill/margaui/components/hover-gallery.md +0 -49
  42. package/skill/margaui/components/indicator.md +0 -265
  43. package/skill/margaui/components/input.md +0 -389
  44. package/skill/margaui/components/join.md +0 -100
  45. package/skill/margaui/components/kbd.md +0 -127
  46. package/skill/margaui/components/label.md +0 -102
  47. package/skill/margaui/components/link.md +0 -96
  48. package/skill/margaui/components/list.md +0 -182
  49. package/skill/margaui/components/loading.md +0 -105
  50. package/skill/margaui/components/mask.md +0 -168
  51. package/skill/margaui/components/megamenu.md +0 -131
  52. package/skill/margaui/components/menu.md +0 -887
  53. package/skill/margaui/components/mockup-browser.md +0 -39
  54. package/skill/margaui/components/mockup-code.md +0 -81
  55. package/skill/margaui/components/mockup-phone.md +0 -39
  56. package/skill/margaui/components/mockup-window.md +0 -33
  57. package/skill/margaui/components/modal.md +0 -196
  58. package/skill/margaui/components/navbar.md +0 -282
  59. package/skill/margaui/components/otp.md +0 -171
  60. package/skill/margaui/components/pagination.md +0 -122
  61. package/skill/margaui/components/progress.md +0 -135
  62. package/skill/margaui/components/radial-progress.md +0 -67
  63. package/skill/margaui/components/radio.md +0 -133
  64. package/skill/margaui/components/range.md +0 -134
  65. package/skill/margaui/components/rating.md +0 -170
  66. package/skill/margaui/components/select.md +0 -225
  67. package/skill/margaui/components/skeleton.md +0 -64
  68. package/skill/margaui/components/stack.md +0 -142
  69. package/skill/margaui/components/stat.md +0 -254
  70. package/skill/margaui/components/status.md +0 -73
  71. package/skill/margaui/components/steps.md +0 -138
  72. package/skill/margaui/components/swap.md +0 -152
  73. package/skill/margaui/components/tab.md +0 -248
  74. package/skill/margaui/components/table.md +0 -1018
  75. package/skill/margaui/components/text-rotate.md +0 -91
  76. package/skill/margaui/components/textarea.md +0 -85
  77. package/skill/margaui/components/theme-controller.md +0 -266
  78. package/skill/margaui/components/timeline.md +0 -1356
  79. package/skill/margaui/components/toast.md +0 -165
  80. package/skill/margaui/components/toggle.md +0 -135
  81. package/skill/margaui/components/tooltip.md +0 -181
  82. package/skill/margaui/components/validator.md +0 -163
  83. package/skill/tutuca/SKILL.md +0 -56
  84. package/skill/tutuca/advanced.md +0 -212
  85. package/skill/tutuca/cli.md +0 -239
  86. package/skill/tutuca/component-design.md +0 -168
  87. package/skill/tutuca/core.md +0 -908
  88. package/skill/tutuca/iteration.md +0 -207
  89. package/skill/tutuca/macros.md +0 -86
  90. package/skill/tutuca/margaui.md +0 -175
  91. package/skill/tutuca/messages-and-intents.md +0 -399
  92. package/skill/tutuca/patterns/README.md +0 -48
  93. package/skill/tutuca/patterns/add-a-story.md +0 -26
  94. package/skill/tutuca/patterns/bind-text-and-attributes.md +0 -30
  95. package/skill/tutuca/patterns/conditional-attribute-value.md +0 -29
  96. package/skill/tutuca/patterns/coordinate-components.md +0 -54
  97. package/skill/tutuca/patterns/edit-through-a-dynamic-target.md +0 -27
  98. package/skill/tutuca/patterns/enrich-each-item.md +0 -25
  99. package/skill/tutuca/patterns/file-input.md +0 -39
  100. package/skill/tutuca/patterns/filter-a-list.md +0 -25
  101. package/skill/tutuca/patterns/filter-and-paginate.md +0 -60
  102. package/skill/tutuca/patterns/handle-events.md +0 -38
  103. package/skill/tutuca/patterns/iterate-a-list.md +0 -18
  104. package/skill/tutuca/patterns/paginate-a-list.md +0 -29
  105. package/skill/tutuca/patterns/render-a-child-component.md +0 -21
  106. package/skill/tutuca/patterns/reuse-markup-with-macros.md +0 -36
  107. package/skill/tutuca/patterns/share-state-across-the-tree.md +0 -38
  108. package/skill/tutuca/patterns/show-or-hide-content.md +0 -23
  109. package/skill/tutuca/patterns/switch-between-views.md +0 -30
  110. package/skill/tutuca/patterns/tabbed-interface.md +0 -43
  111. package/skill/tutuca/semantics.md +0 -195
  112. package/skill/tutuca/storybook.md +0 -270
  113. package/skill/tutuca/styles.md +0 -48
  114. package/skill/tutuca/testing.md +0 -346
  115. package/skill/tutuca-source/SKILL.md +0 -33
  116. package/skill/tutuca-source/tutuca.ext.js +0 -4235
@@ -1,4235 +0,0 @@
1
- var __defProp = Object.defineProperty;
2
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
- var __getOwnPropNames = Object.getOwnPropertyNames;
4
- var __hasOwnProp = Object.prototype.hasOwnProperty;
5
- var __copyProps = (to, from, except, desc) => {
6
- if (from && typeof from === "object" || typeof from === "function") {
7
- for (let key of __getOwnPropNames(from))
8
- if (!__hasOwnProp.call(to, key) && key !== except)
9
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
10
- }
11
- return to;
12
- };
13
- var __reExport = (target, mod, secondTarget) => (__copyProps(target, mod, "default"), secondTarget && __copyProps(secondTarget, mod, "default"));
14
-
15
- // src/collection.js
16
- var isPlainObject = (value) => {
17
- if (value === null || typeof value !== "object") return false;
18
- const proto = Object.getPrototypeOf(value);
19
- return proto === Object.prototype || proto === null;
20
- };
21
- var seqSize = (seq) => {
22
- if (seq == null) return 0;
23
- if (typeof seq.length === "number") return seq.length;
24
- if (typeof seq.size === "number") return seq.size;
25
- return isPlainObject(seq) ? Object.keys(seq).length : 0;
26
- };
27
- var seqGet = (seq, key, dval = null) => {
28
- if (seq == null) return dval;
29
- if (seq instanceof Map) return seq.has(key) ? seq.get(key) : dval;
30
- if (seq instanceof Set) return seq.has(key) ? key : dval;
31
- if (Object.hasOwn(seq, key)) return seq[key];
32
- if (typeof seq.get === "function") return seq.get(key, dval);
33
- return dval;
34
- };
35
- var isIndexedSeq = (seq) => Array.isArray(seq);
36
- var isKeyedSeq = (seq) => seq instanceof Map || isPlainObject(seq);
37
- var isSetSeq = (seq) => seq instanceof Set;
38
- function* seqEntries(seq) {
39
- if (seq instanceof Map) yield* seq.entries();
40
- else if (seq instanceof Set) for (const value of seq) yield [value, value];
41
- else if (isPlainObject(seq)) yield* Object.entries(seq);
42
- else if (Array.isArray(seq)) for (let i = 0; i < seq.length; i++) yield [i, seq[i]];
43
- }
44
-
45
- // src/immer.js
46
- var immer_exports = {};
47
- __reExport(immer_exports, immer_star);
48
- import { enableMapSet } from "immer";
49
- import * as immer_star from "immer";
50
- enableMapSet();
51
-
52
- // src/path.js
53
- var NONE = /* @__PURE__ */ Symbol("NONE");
54
- var readKey = (value, key, dval = null) => {
55
- if (value == null) return dval;
56
- if (value instanceof Map) return value.has(key) ? value.get(key) : dval;
57
- if (value instanceof Set) return value.has(key) ? key : dval;
58
- return Object.hasOwn(value, key) ? value[key] : dval;
59
- };
60
- var writeKey = (value, key, next) => {
61
- if (value instanceof Map) value.set(key, next);
62
- else if (value instanceof Set) {
63
- value.delete(key);
64
- value.add(next);
65
- } else value[key] = next;
66
- };
67
- var readSeqKey = (value, key, dval = null) => {
68
- const direct = readKey(value, key, NONE);
69
- if (direct !== NONE) return direct;
70
- return typeof value?.get === "function" ? value.get(key, dval) : dval;
71
- };
72
- var writeSeqKey = (value, key, next) => {
73
- if (value instanceof Map || value instanceof Set || Array.isArray(value) || Object.hasOwn(value, key))
74
- writeKey(value, key, next);
75
- else if (typeof value?.set === "function") value.set(key, next);
76
- else writeKey(value, key, next);
77
- };
78
- var Step = class {
79
- lookup(_v, dval = null) {
80
- return dval;
81
- }
82
- setValue(root, _v) {
83
- return root;
84
- }
85
- setDraftValue(_root, _v) {
86
- }
87
- enterFrame(stack, _prev, next) {
88
- return stack.enter(next, {}, true);
89
- }
90
- toAbstractPathStep() {
91
- return this;
92
- }
93
- // Freeze any field-resolved key against the value `v` entering this step (see
94
- // `Path.pinKeys`). Most steps carry no live key and pin to themselves.
95
- pinKey(_v) {
96
- return this;
97
- }
98
- // A generic `{ field, key? }` descriptor for this step, or null for frame-only
99
- // steps (binds) that address no field. Used by `Path.toKeys()` so tooling can
100
- // introspect a path without reaching into Step subclasses.
101
- toKey() {
102
- return null;
103
- }
104
- };
105
- var BindStep = class _BindStep extends Step {
106
- constructor(binds) {
107
- super();
108
- this.binds = binds;
109
- }
110
- lookup(v, _dval) {
111
- return v;
112
- }
113
- setValue(_root, v) {
114
- return v;
115
- }
116
- enterFrame(stack, _prev, next) {
117
- return stack.enter(next, { ...this.binds }, false);
118
- }
119
- withIndex(i) {
120
- return new _BindStep({ ...this.binds, key: i });
121
- }
122
- withKey(key) {
123
- return new _BindStep({ ...this.binds, key });
124
- }
125
- toAbstractPathStep() {
126
- return null;
127
- }
128
- };
129
- var ScopeBindStep = class _ScopeBindStep extends BindStep {
130
- constructor(val, binds = {}) {
131
- super(binds);
132
- this.val = val;
133
- }
134
- enterFrame(stack, _prev, next) {
135
- const dyn = this.val.evalAsHandler(stack)?.call(stack.it) ?? {};
136
- return stack.enter(next, { ...this.binds, ...dyn }, false);
137
- }
138
- withIndex(i) {
139
- return new _ScopeBindStep(this.val, { ...this.binds, key: i });
140
- }
141
- withKey(key) {
142
- return new _ScopeBindStep(this.val, { ...this.binds, key });
143
- }
144
- };
145
- var FieldStep = class extends Step {
146
- constructor(field) {
147
- super();
148
- this.field = field;
149
- }
150
- lookup(v, dval = null) {
151
- return readKey(v, this.field, dval);
152
- }
153
- setValue(root, v) {
154
- return (0, immer_exports.produce)(root, (draft) => this.setDraftValue(draft, v));
155
- }
156
- setDraftValue(root, v) {
157
- writeKey(root, this.field, v);
158
- }
159
- withIndex(i) {
160
- return new SeqStep(this.field, i);
161
- }
162
- withKey(k) {
163
- return new SeqStep(this.field, k);
164
- }
165
- toKey() {
166
- return { field: this.field };
167
- }
168
- };
169
- var SeqStep = class extends Step {
170
- constructor(field, key) {
171
- super();
172
- this.field = field;
173
- this.key = key;
174
- }
175
- lookup(v, dval = null) {
176
- return readSeqKey(readKey(v, this.field, null), this.key, dval);
177
- }
178
- setValue(root, v) {
179
- return (0, immer_exports.produce)(root, (draft) => this.setDraftValue(draft, v));
180
- }
181
- setDraftValue(root, v) {
182
- const seq = readKey(root, this.field, null);
183
- if (seq != null) writeSeqKey(seq, this.key, v);
184
- }
185
- enterFrame(stack, _prev, next) {
186
- return stack.enter(next, { key: this.key }, true);
187
- }
188
- toKey() {
189
- return { field: this.field, key: this.key };
190
- }
191
- };
192
- var SeqAccessStep = class extends Step {
193
- constructor(seqField, keyField) {
194
- super();
195
- this.seqField = seqField;
196
- this.keyField = keyField;
197
- }
198
- lookup(v, dval = null) {
199
- const seq = readKey(v, this.seqField, NONE);
200
- const key = readKey(v, this.keyField, NONE);
201
- return key !== NONE && seq !== NONE ? readSeqKey(seq, key, dval) : dval;
202
- }
203
- setValue(root, v) {
204
- return (0, immer_exports.produce)(root, (draft) => this.setDraftValue(draft, v));
205
- }
206
- setDraftValue(root, v) {
207
- const seq = readKey(root, this.seqField, NONE);
208
- const key = readKey(root, this.keyField, NONE);
209
- if (seq !== NONE && key !== NONE) writeSeqKey(seq, key, v);
210
- }
211
- // Resolve `keyField` against `v` now and freeze it as a literal-key `SeqStep`, so a
212
- // later lookup/setValue lands on this same item even if `keyField` changes meanwhile.
213
- pinKey(v) {
214
- const key = readKey(v, this.keyField, NONE);
215
- return key === NONE ? this : new SeqStep(this.seqField, key);
216
- }
217
- // The key is a *field reference* resolved live, so it is unknown without a value;
218
- // report the seq field (no key) rather than dropping the step, which would shift
219
- // the indices of later keys. Call `Path.pinKeys(root)` first to get a concrete key.
220
- toKey() {
221
- return { field: this.seqField };
222
- }
223
- };
224
- var EachBindStep = class extends Step {
225
- constructor(iterInfo, key) {
226
- super();
227
- this.iterInfo = iterInfo;
228
- this.key = key;
229
- }
230
- lookup(v, _dval) {
231
- return v;
232
- }
233
- setValue(_root, v) {
234
- return v;
235
- }
236
- // Replay the renderer's per-item binds (key, value + any @enrich-with binds)
237
- // so a rebuilt stack matches the one @each rendered with.
238
- enterFrame(stack, _prev, next) {
239
- return stack.enter(next, this.iterInfo.enrichBinds(stack, this.key), false);
240
- }
241
- toAbstractPathStep() {
242
- return null;
243
- }
244
- };
245
- var EachRenderItStep = class extends SeqStep {
246
- enterFrame(stack, _prev, next) {
247
- return stack.enter(next, { key: this.key, value: next }, false).enter(next, {}, true);
248
- }
249
- toAbstractPathStep() {
250
- return new SeqStep(this.field, this.key);
251
- }
252
- };
253
- function warnRawDynStep(op, step) {
254
- console.warn(`Path.${op} reached a DynStep: call toTransactionPath() first`, step);
255
- }
256
- var DynStep = class extends Step {
257
- constructor(producerCompId, producerSteps) {
258
- super();
259
- this.producerCompId = producerCompId;
260
- this.producerSteps = producerSteps;
261
- this.interiorCids = /* @__PURE__ */ new Set();
262
- }
263
- // Steps spliced into the transaction path in place of this marker.
264
- teleportSteps() {
265
- return this.producerSteps;
266
- }
267
- lookup(_v, dval = null) {
268
- warnRawDynStep("lookup", this);
269
- return dval;
270
- }
271
- setValue(root, _v) {
272
- warnRawDynStep("setValue", this);
273
- return root;
274
- }
275
- enterFrame(stack, _prev, _next) {
276
- warnRawDynStep("enterFrame", this);
277
- return stack;
278
- }
279
- };
280
- var DynEachStep = class extends DynStep {
281
- constructor(producerCompId, producerSteps, key) {
282
- super(producerCompId, producerSteps);
283
- this.key = key;
284
- }
285
- teleportSteps() {
286
- const { producerSteps, key } = this;
287
- if (producerSteps.length === 0) return producerSteps;
288
- const last = producerSteps[producerSteps.length - 1];
289
- if (!(last instanceof FieldStep)) {
290
- console.warn("DynEachStep: seq-access dynamic cannot be iterated", this);
291
- return producerSteps;
292
- }
293
- return producerSteps.slice(0, -1).concat(new SeqStep(last.field, key));
294
- }
295
- };
296
- var Path = class _Path {
297
- constructor(steps = []) {
298
- this.steps = steps;
299
- }
300
- concat(steps) {
301
- return new _Path(this.steps.concat(steps));
302
- }
303
- popStep() {
304
- return new _Path(this.steps.slice(0, -1));
305
- }
306
- // The dispatch path: frame-only steps removed, one step per crossed component
307
- // (DynStep included). `popStep` over it bubbles through every component.
308
- compact() {
309
- const out = [];
310
- for (const step of this.steps) {
311
- const s = step.toAbstractPathStep();
312
- if (s !== null) {
313
- if (s !== step) s._originCid = step._originCid;
314
- out.push(s);
315
- }
316
- }
317
- return new _Path(out);
318
- }
319
- // The abstract path used to apply a transaction: every DynStep is teleported —
320
- // the steps interior to its producer..consumer span are dropped and the
321
- // producer's own path spliced in — so a mutation lands on the data's real
322
- // location. A path with no DynStep is returned unchanged.
323
- toTransactionPath() {
324
- let hasDyn = false;
325
- for (const step of this.steps)
326
- if (step instanceof DynStep) {
327
- hasDyn = true;
328
- break;
329
- }
330
- if (!hasDyn) return this;
331
- const out = [];
332
- for (const step of this.steps) {
333
- if (step instanceof DynStep) {
334
- while (out.length > 0 && step.interiorCids.has(out[out.length - 1]._originCid)) out.pop();
335
- for (const ts of step.teleportSteps()) {
336
- ts._originCid = step.producerCompId;
337
- out.push(ts);
338
- }
339
- } else out.push(step);
340
- }
341
- return new _Path(out);
342
- }
343
- // Resolve every field-keyed step (e.g. `SeqAccessStep`) against `root`, freezing the
344
- // key as it is *now* so a later lookup/setValue lands on the same item even if the
345
- // keyField changed meanwhile (e.g. the selected tab moved while a request was in
346
- // flight). Returns a new Path with those steps replaced; `this` if nothing pinned.
347
- // Must be called on a transaction path (no DynSteps — call toTransactionPath first).
348
- pinKeys(root) {
349
- let curVal = root;
350
- let out = null;
351
- for (let i = 0; i < this.steps.length; i++) {
352
- const step = this.steps[i];
353
- const pinned = step.pinKey(curVal);
354
- if (pinned !== step) (out ??= this.steps.slice())[i] = pinned;
355
- curVal = step.lookup(curVal, NONE);
356
- if (curVal === NONE) break;
357
- }
358
- return out ? new _Path(out) : this;
359
- }
360
- lookup(v, dval = null) {
361
- let curVal = v;
362
- for (const step of this.steps) {
363
- curVal = step.lookup(curVal, NONE);
364
- if (curVal === NONE) return dval;
365
- }
366
- return curVal;
367
- }
368
- // The values entered along the path, root→leaf (root included): index 0 is `root`,
369
- // the last entry is the leaf this path resolves to. Stops early at the first
370
- // unresolvable step. Call on a transaction path (no DynSteps). Used to walk the
371
- // component instances on a dispatch path (filter via Components.getCompFor).
372
- resolveChain(root) {
373
- const out = [root];
374
- let curVal = root;
375
- for (const step of this.steps) {
376
- curVal = step.lookup(curVal, NONE);
377
- if (curVal === NONE) break;
378
- out.push(curVal);
379
- }
380
- return out;
381
- }
382
- // A flat `[{ field, key? }]` list of the addressing steps, skipping frame-only
383
- // steps (binds). Generic path introspection so tooling (e.g. the storybook
384
- // activity log) can identify which subtree a transaction touched without
385
- // depending on Step internals. Call on a transaction path (no DynSteps).
386
- toKeys() {
387
- const out = [];
388
- for (const step of this.steps) {
389
- const k = step.toKey();
390
- if (k !== null) out.push(k);
391
- }
392
- return out;
393
- }
394
- setValue(root, v) {
395
- if (this.steps.length === 0) return v;
396
- return (0, immer_exports.produce)(root, (draft) => {
397
- let parent = draft;
398
- for (let i = 0; i < this.steps.length - 1; i++) {
399
- parent = this.steps[i].lookup(parent, NONE);
400
- if (parent === NONE) return;
401
- }
402
- this.steps.at(-1).setDraftValue(parent, v);
403
- });
404
- }
405
- buildStack(stack) {
406
- let prev = stack.it;
407
- for (const step of this.steps) {
408
- const next = step.lookup(prev, NONE);
409
- if (next === NONE) {
410
- console.warn("bad PathItem", { root: stack.it, step, path: this });
411
- return null;
412
- }
413
- stack = step.enterFrame(stack, prev, next);
414
- prev = next;
415
- }
416
- return stack;
417
- }
418
- static fromNodeAndEventName(node, eventName, rootNode, maxDepth, comps, stopOnNoEvent = true) {
419
- const pathSteps = [];
420
- const pendingDyns = [];
421
- const bubbles = BUBBLING_EVENTS.has(eventName);
422
- let depth = 0;
423
- let eventIds = [];
424
- let handlers = null;
425
- let nodeIds = [];
426
- let isLeafComponent = true;
427
- const crossComponent = (cidNum, vid) => {
428
- const comp = comps.getComponentForId(cidNum);
429
- let pushStep = true;
430
- if (handlers === null && (isLeafComponent || bubbles)) {
431
- handlers = findHandlers(comp, eventIds, vid, eventName);
432
- if (handlers === null) {
433
- if (isLeafComponent && stopOnNoEvent && !bubbles) return false;
434
- } else if (!isLeafComponent) {
435
- pathSteps.length = 0;
436
- pendingDyns.length = 0;
437
- pushStep = false;
438
- }
439
- }
440
- isLeafComponent = false;
441
- for (const dyn of pendingDyns) dyn.interiorCids.add(cidNum);
442
- if (pushStep) {
443
- const step = resolvePathStep(comp, nodeIds, vid);
444
- if (step) {
445
- step._originCid = cidNum;
446
- pathSteps.push(step);
447
- if (step instanceof DynStep) {
448
- step.interiorCids.add(cidNum);
449
- pendingDyns.push(step);
450
- }
451
- }
452
- }
453
- for (let i = pendingDyns.length - 1; i >= 0; i--)
454
- if (pendingDyns[i].producerCompId === cidNum) pendingDyns.splice(i, 1);
455
- eventIds = [];
456
- nodeIds = [];
457
- return true;
458
- };
459
- while (node && node !== rootNode && depth < maxDepth) {
460
- if (node?.dataset) {
461
- const { eid, cid, vid } = node.dataset;
462
- if (eid !== void 0) eventIds.push(eid);
463
- const metas = metaChain(node.previousSibling);
464
- let sawComp = false;
465
- for (const m of metas) {
466
- if (m.$ === "Comp") {
467
- sawComp = true;
468
- if (!crossComponent(m.cid, m.vid)) return NO_EVENT_INFO;
469
- nodeIds.push({ nid: m.nid });
470
- } else {
471
- nodeIds.push({ nid: m.nid, si: m.si, sk: m.sk });
472
- }
473
- }
474
- if (!sawComp && cid !== void 0 && !crossComponent(+cid, vid)) return NO_EVENT_INFO;
475
- }
476
- depth += 1;
477
- node = node.parentNode;
478
- }
479
- if (pendingDyns.length > 0)
480
- console.warn("event reconstruction: dynamic-var producer not found", pendingDyns);
481
- return [new _Path(pathSteps.reverse()), handlers];
482
- }
483
- static fromEvent(e, rNode, maxDepth, comps, stopOnNoEvent = true) {
484
- const { type, target } = e;
485
- return _Path.fromNodeAndEventName(target, type, rNode, maxDepth, comps, stopOnNoEvent);
486
- }
487
- };
488
- function metaChain(n) {
489
- const out = [];
490
- while (n?.nodeType === 8 && n.textContent[0] === "§") {
491
- try {
492
- out.push(JSON.parse(n.textContent.slice(1, -1)));
493
- } catch (err) {
494
- console.warn(err, n);
495
- }
496
- n = n.previousSibling;
497
- }
498
- return out;
499
- }
500
- function findHandlers(comp, eventIds, vid, eventName) {
501
- for (const eid of eventIds) {
502
- const handlers = comp.getEventForId(+eid, vid).getHandlersFor(eventName);
503
- if (handlers !== null) return handlers;
504
- }
505
- return null;
506
- }
507
- var StepCtx = class _StepCtx {
508
- constructor(comp, nodeIds, idx, vid) {
509
- this.comp = comp;
510
- this.nodeIds = nodeIds;
511
- this.idx = idx;
512
- this.vid = vid;
513
- }
514
- get meta() {
515
- return this.nodeIds[this.idx];
516
- }
517
- get key() {
518
- const m = this.meta;
519
- return m.si !== void 0 ? +m.si : m.sk;
520
- }
521
- get hasKey() {
522
- const m = this.meta;
523
- return m.si !== void 0 || m.sk !== void 0;
524
- }
525
- next() {
526
- const { idx, nodeIds } = this;
527
- return idx + 1 < nodeIds.length ? new _StepCtx(this.comp, nodeIds, idx + 1, this.vid) : null;
528
- }
529
- resolveNode() {
530
- return this.comp.getNodeForId(+this.meta.nid, this.vid);
531
- }
532
- applyKey(pi) {
533
- if (pi === null) return null;
534
- const m = this.meta;
535
- if (m.si !== void 0) return pi.withIndex(+m.si);
536
- if (m.sk !== void 0) return pi.withKey(m.sk);
537
- return pi;
538
- }
539
- };
540
- function resolvePathStep(comp, nodeIds, vid) {
541
- for (let i = 0; i < nodeIds.length; i++) {
542
- const ctx = new StepCtx(comp, nodeIds, i, vid);
543
- const step = ctx.resolveNode().toPathStep(ctx);
544
- if (step !== null) return step;
545
- }
546
- return null;
547
- }
548
- var NO_EVENT_INFO = [null, null];
549
- var BUBBLING_EVENTS = /* @__PURE__ */ new Set(["drop"]);
550
- var PathBuilder = class {
551
- constructor() {
552
- this.pathChanges = [];
553
- }
554
- add(pathChange) {
555
- this.pathChanges.push(pathChange);
556
- return this;
557
- }
558
- field(name) {
559
- return this.add(new FieldStep(name));
560
- }
561
- index(name, index) {
562
- return this.add(new SeqStep(name, index));
563
- }
564
- key(name, key) {
565
- return this.add(new SeqStep(name, key));
566
- }
567
- };
568
-
569
- // src/value.js
570
- var VALID_VAL_ID_RE = /^[a-zA-Z][a-zA-Z0-9_]*\??$/;
571
- var isValidValId = (name) => VALID_VAL_ID_RE.test(name);
572
- var VALID_FLOAT_RE = /^-?[0-9]+(\.[0-9]+)?$/;
573
- var STR_TPL_SPLIT_RE = /(\{[^}]+\})/g;
574
- var mkVal = (name, Cls) => isValidValId(name) ? new Cls(name) : null;
575
- var VAL_TOKEN_RE = /\$'(?:[^'\\]|\\.)*'|'(?:[^'\\]|\\.)*'|\S+/g;
576
- var tokenizeValue = (s) => s.match(VAL_TOKEN_RE) ?? [];
577
- var unescapeStr = (s) => s.replace(/\\(['\\])/g, "$1");
578
- var K_CONST = 1;
579
- var K_STRTPL = 2;
580
- var K_FIELD = 4;
581
- var K_BIND = 8;
582
- var K_DYN = 16;
583
- var K_NAME = 32;
584
- var K_TYPE = 64;
585
- var K_SEQ = 256;
586
- var K_METHOD = 1024;
587
- var G_BOOL = K_FIELD | K_METHOD | K_BIND | K_DYN | K_CONST;
588
- var G_TEXT = G_BOOL | K_STRTPL;
589
- var G_COMPONENT = K_FIELD | K_SEQ | K_DYN;
590
- var G_SEQUENCE = K_FIELD | K_DYN;
591
- var G_PROVIDE = K_FIELD | K_SEQ;
592
- var G_FIELD = K_FIELD | K_METHOD | K_CONST | K_SEQ;
593
- var G_VALUE = K_FIELD | K_METHOD | K_BIND | K_DYN | K_NAME | K_TYPE | K_CONST;
594
- var G_ALL = G_VALUE | K_STRTPL | K_SEQ;
595
- function sizeOf(v) {
596
- if (v == null) return null;
597
- const s = v.size;
598
- if (typeof s === "number") return s;
599
- const l = v.length;
600
- if (typeof l === "number") return l;
601
- return isPlainObject(v) ? Object.keys(v).length : null;
602
- }
603
- var predTruthy = (v) => {
604
- const n = sizeOf(v);
605
- return n === null ? !!v : n > 0;
606
- };
607
- var PREDICATES = {
608
- "empty?": { name: "empty?", arity: 1, fn: (v) => v == null || sizeOf(v) === 0 },
609
- "truthy?": { name: "truthy?", arity: 1, fn: predTruthy },
610
- "falsy?": { name: "falsy?", arity: 1, fn: (v) => !predTruthy(v) },
611
- "null?": { name: "null?", arity: 1, fn: (v) => v == null },
612
- "equals?": { name: "equals?", arity: 2, fn: (a, b) => Object.is(a, b) }
613
- };
614
- function parseToken(s, px) {
615
- const c0 = s.charCodeAt(0);
616
- if (c0 === 39)
617
- return s.length >= 2 && s.charCodeAt(s.length - 1) === 39 ? new ConstVal(unescapeStr(s.slice(1, -1))) : null;
618
- if (c0 === 36 && s.charCodeAt(1) === 39)
619
- return s.length >= 3 && s.charCodeAt(s.length - 1) === 39 ? StrTplVal.parse(s.slice(2, -1), px) : null;
620
- if (s.indexOf("[") !== -1 || s.indexOf("]") !== -1) return _parseSeqAccess(s, px);
621
- if (s.indexOf("{") !== -1 || s.indexOf("}") !== -1) return null;
622
- switch (c0) {
623
- case 94: {
624
- const name = s.slice(1);
625
- const newS = px.frame.macroVars?.[name];
626
- if (newS !== void 0) {
627
- const tokens = tokenizeValue(newS.trim());
628
- if (tokens.length !== 1) return null;
629
- const val = parseToken(tokens[0], px);
630
- if (val instanceof ConstVal) val.fromMacroVar = true;
631
- return val;
632
- }
633
- px.onParseIssue("bad-value", { role: "macro-var", name, value: s });
634
- return null;
635
- }
636
- case 36:
637
- return mkVal(s.slice(1), MethodVal);
638
- case 64: {
639
- const dot = s.indexOf(".", 1);
640
- if (dot === -1) return mkVal(s.slice(1), BindVal);
641
- const name = s.slice(1, dot);
642
- const member = s.slice(dot + 1);
643
- return isValidValId(name) && isValidValId(member) ? new BindMemberVal(name, member) : null;
644
- }
645
- case 42:
646
- return mkVal(s.slice(1), DynVal);
647
- case 46:
648
- return mkVal(s.slice(1), FieldVal);
649
- }
650
- const num = VALID_FLOAT_RE.test(s) ? parseFloat(s) : null;
651
- if (Number.isFinite(num)) return new ConstVal(num);
652
- if (s === "true" || s === "false") return new ConstVal(s === "true");
653
- if (c0 >= 97 && c0 <= 122) return mkVal(s, NameVal);
654
- if (c0 >= 65 && c0 <= 90) return mkVal(s, TypeVal);
655
- return null;
656
- }
657
- function _parseSeqAccess(s, px) {
658
- const open = s.indexOf("[");
659
- const close = s.indexOf("]");
660
- if (open < 1 || close !== s.length - 1 || close < open || s.indexOf("[", open + 1) !== -1)
661
- return null;
662
- const left = parseToken(s.slice(0, open), px);
663
- const right = parseToken(s.slice(open + 1, close), px);
664
- return left instanceof FieldVal && right instanceof FieldVal ? new SeqAccessVal(left, right) : null;
665
- }
666
- function _parseSingle(s, px, group) {
667
- const tokens = tokenizeValue(s.trim());
668
- if (tokens.length !== 1) return null;
669
- const val = parseToken(tokens[0], px);
670
- return val !== null && kindOf(val) & group ? val : null;
671
- }
672
- function parseBool(s, px) {
673
- const t = s.trim();
674
- const tokens = tokenizeValue(t);
675
- if (tokens.length !== 1) return tokens.length === 0 ? null : _parsePredicate(t, tokens, px);
676
- const val = parseToken(tokens[0], px);
677
- return val !== null && kindOf(val) & G_BOOL ? val : null;
678
- }
679
- function parseText(s, px) {
680
- return _parseSingle(s, px, G_TEXT);
681
- }
682
- function parseComponent(s, px) {
683
- return _parseSingle(s, px, G_COMPONENT);
684
- }
685
- function parseSequence(s, px) {
686
- return _parseSingle(s, px, G_SEQUENCE);
687
- }
688
- function parseField(s, px) {
689
- return _parseSingle(s, px, G_FIELD);
690
- }
691
- function parseProvide(s, px) {
692
- return _parseSingle(s, px, G_PROVIDE);
693
- }
694
- function parseMacroAttr(s, px) {
695
- return _parseSingle(s, px, G_ALL);
696
- }
697
- function parseReceiveHandler(s, px) {
698
- return _parseHandler(s, px, "receive", true, true, false);
699
- }
700
- function parseAlterHandler(s, px) {
701
- const r = _parseHandler(s, px, "alter", false, false, true);
702
- return r === null ? null : r.handlerVal;
703
- }
704
- function _parseHandler(s, px, namespace, allowArgs, report, allowMethod) {
705
- const tokens = tokenizeValue(s.trim());
706
- const headTok = tokens[0] ?? "";
707
- const head = headTok === "" ? null : parseToken(headTok, px);
708
- const hk = kindOf(head);
709
- let handlerVal;
710
- if (hk & K_METHOD && allowMethod) handlerVal = head;
711
- else if (hk & K_METHOD) {
712
- if (report)
713
- px.onParseIssue("event-method-handler", {
714
- name: head.name,
715
- role: "handler-name",
716
- value: headTok
717
- });
718
- return null;
719
- } else if (hk & K_NAME) handlerVal = new HandlerNameVal(head.name, namespace);
720
- else {
721
- if (report) px.onParseIssue("bad-value", { role: "handler-name", value: headTok });
722
- return null;
723
- }
724
- if (!allowArgs) return tokens.length === 1 ? { handlerVal, args: [] } : null;
725
- const args = new Array(tokens.length - 1);
726
- for (let i = 1; i < tokens.length; i++) {
727
- const val = parseToken(tokens[i], px);
728
- if (val !== null && kindOf(val) & G_VALUE) args[i - 1] = val;
729
- else {
730
- if (report) px.onParseIssue("bad-value", { role: "handler-arg", value: tokens[i] });
731
- args[i - 1] = NULL_CONST_VAL;
732
- }
733
- }
734
- return { handlerVal, args };
735
- }
736
- function _parsePredicate(s, tokens, px) {
737
- const predName = tokens[0];
738
- const pred = PREDICATES[predName];
739
- if (pred === void 0) {
740
- px.onParseIssue("bad-value", { role: "predicate", value: predName });
741
- return null;
742
- }
743
- const arity = tokens.length - 1;
744
- if (arity !== pred.arity) {
745
- px.onParseIssue("bad-value", { role: "predicate-arity", value: s, predicate: predName });
746
- return null;
747
- }
748
- const args = new Array(arity);
749
- for (let i = 0; i < arity; i++) {
750
- const tok = tokens[i + 1];
751
- const val = parseToken(tok, px);
752
- if (val === null || !(kindOf(val) & G_BOOL)) {
753
- px.onParseIssue("bad-value", { role: "predicate-arg", value: tok });
754
- return null;
755
- }
756
- args[i] = val;
757
- }
758
- return new PredicateVal(pred, args);
759
- }
760
- function kindOf(val) {
761
- if (val === null) return 0;
762
- if (val instanceof ConstVal) return val.kind;
763
- if (val instanceof StrTplVal) return val.kind;
764
- if (val instanceof SeqAccessVal) return K_SEQ;
765
- if (val instanceof FieldVal) return K_FIELD;
766
- if (val instanceof MethodVal) return K_METHOD;
767
- if (val instanceof BindVal) return K_BIND;
768
- if (val instanceof DynVal) return K_DYN;
769
- if (val instanceof TypeVal) return K_TYPE;
770
- if (val instanceof NameVal) return K_NAME;
771
- return 0;
772
- }
773
- var BaseVal = class {
774
- render(_stack, _rx) {
775
- }
776
- eval(_stack) {
777
- }
778
- toPathItem() {
779
- return null;
780
- }
781
- // Value of this expression when it sits in handler position (`@on.<event>`,
782
- // `@when`, `@enrich-with`, `@loop-with`): the dispatch machinery calls the
783
- // result with the event args. Defaults to `eval`; `MethodVal` overrides it
784
- // to hand back the raw function instead of invoking it.
785
- evalAsHandler(stack) {
786
- return this.eval(stack);
787
- }
788
- };
789
- var ConstVal = class extends BaseVal {
790
- constructor(val, kind = K_CONST) {
791
- super();
792
- this.val = val;
793
- this.kind = kind;
794
- }
795
- render(_stack, _rx) {
796
- return this.val;
797
- }
798
- eval(_stack) {
799
- return this.val;
800
- }
801
- toString() {
802
- const v = this.val;
803
- return typeof v === "string" ? `'${v.replace(/(['\\])/g, "\\$1")}'` : `${v}`;
804
- }
805
- };
806
- var NULL_CONST_VAL = new ConstVal(null);
807
- var PredicateVal = class extends BaseVal {
808
- constructor(pred, args) {
809
- super();
810
- this.pred = pred;
811
- this.args = args;
812
- }
813
- eval(stack) {
814
- const n = this.args.length;
815
- const vals = new Array(n);
816
- for (let i = 0; i < n; i++) vals[i] = this.args[i].eval(stack);
817
- return this.pred.fn(...vals);
818
- }
819
- toString() {
820
- return `${this.pred.name} ${this.args.map(String).join(" ")}`;
821
- }
822
- };
823
- var VarVal = class extends BaseVal {
824
- };
825
- var StrTplVal = class _StrTplVal extends VarVal {
826
- constructor(vals) {
827
- super();
828
- this.vals = vals;
829
- this.kind = this.isLiteral() ? K_CONST : K_STRTPL;
830
- }
831
- // True when this template carries no dynamic placeholder: every part is a
832
- // plain `ConstVal` and none was bound from a macro variable (the macro
833
- // placeholder is real in the body even when it resolved to a constant).
834
- isLiteral() {
835
- for (const v of this.vals) if (!(v instanceof ConstVal) || v.fromMacroVar) return false;
836
- return true;
837
- }
838
- render(stack, _rx) {
839
- return this.eval(stack);
840
- }
841
- eval(stack) {
842
- const strs = new Array(this.vals.length);
843
- for (let i = 0; i < this.vals.length; i++) strs[i] = this.vals[i]?.eval(stack, "");
844
- return strs.join("");
845
- }
846
- // When this is a placeholderless literal (see `isLiteral`), returns the
847
- // string it spells out, in `'…'` source form; otherwise null. The linter
848
- // uses this to nudge `$'foo'` → `'foo'` (PLACEHOLDERLESS_TEMPLATE_STRING).
849
- toLiteralSource() {
850
- if (!this.isLiteral()) return null;
851
- let out = "";
852
- for (const v of this.vals) out += v.val;
853
- return new ConstVal(out).toString();
854
- }
855
- // `s` is the interior of a `$'…'` template (the text between `$'` and `'`).
856
- // The interior is unescaped once (`\'`, `\\`) then split on `{…}` groups:
857
- // text between them becomes a `ConstVal`, expressions inside braces are
858
- // parsed via `parseText`. A constant-only template is left as a `StrTplVal`
859
- // (not folded to a `ConstVal`) so the linter can flag it — see
860
- // `toLiteralSource` and the PLACEHOLDERLESS_TEMPLATE_STRING lint rule.
861
- static parse(s, px) {
862
- const parts = unescapeStr(s).split(STR_TPL_SPLIT_RE);
863
- const vals = new Array(parts.length);
864
- for (let i = 0; i < parts.length; i++) {
865
- const part = parts[i];
866
- const isExpr = part[0] === "{" && part.at(-1) === "}";
867
- vals[i] = isExpr ? parseText(part.slice(1, -1), px) : new ConstVal(part);
868
- }
869
- let lo = 0;
870
- let hi = vals.length;
871
- const isTrimmable = (v) => v instanceof ConstVal && v.val === "" && !v.fromMacroVar;
872
- while (lo < hi && isTrimmable(vals[lo])) lo++;
873
- while (hi > lo && isTrimmable(vals[hi - 1])) hi--;
874
- return new _StrTplVal(lo === 0 && hi === vals.length ? vals : vals.slice(lo, hi));
875
- }
876
- };
877
- var NameVal = class extends VarVal {
878
- constructor(name) {
879
- super();
880
- this.name = name;
881
- }
882
- eval(stack) {
883
- return stack.lookupName(this.name);
884
- }
885
- toString() {
886
- return this.name;
887
- }
888
- };
889
- var HandlerNameVal = class extends NameVal {
890
- constructor(name, namespace) {
891
- super(name);
892
- this.namespace = namespace;
893
- }
894
- eval(stack) {
895
- return stack.getHandlerFor(this.name, this.namespace) ?? mk404Handler(this.namespace, this.name);
896
- }
897
- };
898
- var mk404Handler = (type, name) => function(...args) {
899
- console.warn("handler not found", { type, name, args }, this);
900
- return this;
901
- };
902
- var TypeVal = class extends NameVal {
903
- eval(stack) {
904
- return stack.lookupType(this.name);
905
- }
906
- };
907
- var RenderVal = class extends BaseVal {
908
- render(stack, _rx) {
909
- return this.eval(stack);
910
- }
911
- };
912
- var RenderNameVal = class extends RenderVal {
913
- constructor(name) {
914
- super();
915
- this.name = name;
916
- }
917
- };
918
- var BindVal = class extends RenderNameVal {
919
- eval(stack) {
920
- return stack.lookupBind(this.name);
921
- }
922
- toString() {
923
- return `@${this.name}`;
924
- }
925
- };
926
- var BindMemberVal = class extends BindVal {
927
- constructor(name, member) {
928
- super(name);
929
- this.member = member;
930
- }
931
- eval(stack) {
932
- const v = stack.lookupBind(this.name);
933
- return seqGet(v, this.member, null);
934
- }
935
- toString() {
936
- return `@${this.name}.${this.member}`;
937
- }
938
- };
939
- var DynVal = class extends RenderNameVal {
940
- eval(stack) {
941
- return stack.lookupDynamic(this.name);
942
- }
943
- toString() {
944
- return `*${this.name}`;
945
- }
946
- };
947
- var FieldVal = class extends RenderNameVal {
948
- eval(stack) {
949
- return stack.lookupFieldRaw(this.name);
950
- }
951
- toPathItem() {
952
- return new FieldStep(this.name);
953
- }
954
- toString() {
955
- return `.${this.name}`;
956
- }
957
- };
958
- var MethodVal = class extends RenderNameVal {
959
- eval(stack) {
960
- return stack.lookupMethod(this.name);
961
- }
962
- evalAsHandler(stack) {
963
- return stack.lookupFieldRaw(this.name);
964
- }
965
- toString() {
966
- return `$${this.name}`;
967
- }
968
- };
969
- var SeqAccessVal = class extends RenderVal {
970
- constructor(seqVal, keyVal) {
971
- super();
972
- this.seqVal = seqVal;
973
- this.keyVal = keyVal;
974
- }
975
- toPathItem() {
976
- return new SeqAccessStep(this.seqVal.name, this.keyVal.name);
977
- }
978
- eval(stack) {
979
- const key = this.keyVal.eval(stack);
980
- return seqGet(this.seqVal.eval(stack), key, null);
981
- }
982
- toString() {
983
- return `${this.seqVal}[${this.keyVal}]`;
984
- }
985
- };
986
-
987
- // src/attribute.js
988
- var Attributes = class {
989
- constructor(items) {
990
- this.items = items;
991
- }
992
- eval(_stack) {
993
- return {};
994
- }
995
- static parse(attributes, px, parseAll = false) {
996
- return getAttrParser(px).parse(attributes, parseAll);
997
- }
998
- isConstant() {
999
- return false;
1000
- }
1001
- };
1002
- var booleanAttrsRaw = "itemscope,allowfullscreen,formnovalidate,ismap,nomodule,novalidate,readonly,async,autofocus,autoplay,controls,default,defer,disabled,hidden,inert,loop,open,required,reversed,scoped,seamless,checked,muted,multiple,selected";
1003
- var booleanAttrs = new Set(booleanAttrsRaw.split(","));
1004
- var AttrParser = class {
1005
- constructor(px) {
1006
- this.clear(px);
1007
- }
1008
- clear(px) {
1009
- this.px = px;
1010
- this.attrs = null;
1011
- this.hasDynamic = false;
1012
- this.wrapperAttrs = null;
1013
- this.textChild = null;
1014
- this.eachAttr = null;
1015
- this.ifAttr = null;
1016
- this.events = null;
1017
- }
1018
- parseAttr(name, value, parseAll = false) {
1019
- const val = parseAll ? parseMacroAttr(value, this.px) : parseText(value, this.px);
1020
- if (val !== null) {
1021
- this.attrs ??= [];
1022
- this.attrs.push(new Attr(name, val));
1023
- this.hasDynamic ||= !(val instanceof ConstVal);
1024
- } else this.px.onParseIssue("bad-value", { role: "attr", attr: name, value });
1025
- }
1026
- pushWrapper(name, raw, val) {
1027
- const node = { name, val, raw };
1028
- this.wrapperAttrs ??= [];
1029
- this.wrapperAttrs.push(node);
1030
- return node;
1031
- }
1032
- parseIf(directiveName, value) {
1033
- const dynVal = parseBool(value, this.px);
1034
- if (dynVal) {
1035
- this.ifAttr = new IfAttr(directiveName.slice(3), dynVal);
1036
- this.attrs ??= [];
1037
- this.attrs.push(this.ifAttr);
1038
- this.hasDynamic = true;
1039
- } else {
1040
- const info = { role: "if", attr: directiveName.slice(3), value };
1041
- this.px.onParseIssue("bad-value", info);
1042
- }
1043
- }
1044
- parseThen(s) {
1045
- if (this.ifAttr) this.ifAttr.thenVal = parseText(s, this.px) ?? NOT_SET_VAL;
1046
- }
1047
- parseElse(value) {
1048
- if (this.ifAttr) this.ifAttr.elseVal = parseText(value, this.px) ?? NOT_SET_VAL;
1049
- }
1050
- parseEvent(directiveName, value) {
1051
- const [eventName, ...modifiers] = directiveName.slice(3).split("+");
1052
- const handler = EventHandler.parse(value, this.px);
1053
- if (handler) {
1054
- if (this.events === null) {
1055
- this.events = this.px.registerEvents();
1056
- this.attrs ??= [];
1057
- this.attrs.push(new ConstAttr("data-eid", new ConstVal(this.events.id)));
1058
- }
1059
- this.events.add(eventName, handler, modifiers);
1060
- }
1061
- }
1062
- _parseDirectiveValue(directiveName, s, parserFn) {
1063
- const val = parserFn(s, this.px);
1064
- if (val === null) {
1065
- const info = { role: "directive", directive: directiveName, value: s };
1066
- this.px.onParseIssue("bad-value", info);
1067
- }
1068
- return val;
1069
- }
1070
- parseDirective(s, directiveName) {
1071
- switch (directiveName) {
1072
- case "dangerouslysetinnerhtml":
1073
- this.attrs ??= [];
1074
- this.attrs.push(new RawHtmlAttr(this._parseDirectiveValue(directiveName, s, parseText)));
1075
- this.hasDynamic = true;
1076
- return;
1077
- case "push-view":
1078
- this.pushWrapper("push-view", s, this._parseDirectiveValue(directiveName, s, parseText));
1079
- return;
1080
- case "text":
1081
- this.textChild = this._parseDirectiveValue(directiveName, s, parseText);
1082
- return;
1083
- case "show":
1084
- this.pushWrapper("show", s, this._parseDirectiveValue(directiveName, s, parseBool));
1085
- return;
1086
- case "hide":
1087
- this.pushWrapper("hide", s, this._parseDirectiveValue(directiveName, s, parseBool));
1088
- return;
1089
- case "each": {
1090
- const val = this._parseDirectiveValue(directiveName, s, parseSequence);
1091
- this.eachAttr = this.pushWrapper("each", s, val);
1092
- return;
1093
- }
1094
- case "enrich-with":
1095
- if (this.eachAttr !== null)
1096
- this.eachAttr.enrichWithVal = this._parseDirectiveValue(
1097
- directiveName,
1098
- s,
1099
- parseAlterHandler
1100
- );
1101
- else
1102
- this.pushWrapper(
1103
- "scope",
1104
- s,
1105
- this._parseDirectiveValue(directiveName, s, parseAlterHandler)
1106
- );
1107
- return;
1108
- case "when":
1109
- this._parseWhen(s);
1110
- return;
1111
- case "loop-with":
1112
- this._parseLoopWith(s);
1113
- return;
1114
- case "then":
1115
- this.parseThen(s);
1116
- return;
1117
- case "else":
1118
- this.parseElse(s);
1119
- return;
1120
- }
1121
- if (directiveName.startsWith("on.")) this.parseEvent(directiveName, s);
1122
- else if (directiveName.startsWith("if.")) this.parseIf(directiveName, s);
1123
- else if (directiveName.startsWith("then.")) this.parseThen(s);
1124
- else if (directiveName.startsWith("else.")) this.parseElse(s);
1125
- else {
1126
- const info = { name: directiveName, value: s };
1127
- this.px.onParseIssue("unknown-directive", info);
1128
- }
1129
- }
1130
- _parseWhen(s) {
1131
- if (this.eachAttr !== null)
1132
- this.eachAttr.whenVal = this._parseDirectiveValue("when", s, parseAlterHandler);
1133
- }
1134
- _parseLoopWith(s) {
1135
- if (this.eachAttr !== null)
1136
- this.eachAttr.loopWithVal = this._parseDirectiveValue("loop-with", s, parseAlterHandler);
1137
- }
1138
- parse(attributes, parseAll = false) {
1139
- for (const { name, value } of attributes) {
1140
- const charCode = name.charCodeAt(0);
1141
- if (charCode === 58)
1142
- this.parseAttr(name === ":viewbox" ? "viewBox" : name.slice(1), value, parseAll);
1143
- else if (charCode === 64) this.parseDirective(value, name.slice(1));
1144
- else {
1145
- this.attrs ??= [];
1146
- const constVal = value === "" && booleanAttrs.has(name) ? true : value;
1147
- this.attrs.push(new ConstAttr(name, new ConstVal(constVal)));
1148
- }
1149
- }
1150
- const { attrs, hasDynamic } = this;
1151
- const pAttrs = hasDynamic ? new DynAttrs(attrs) : ConstAttrs.fromAttrs(attrs ?? []);
1152
- return [pAttrs, this.wrapperAttrs, this.textChild];
1153
- }
1154
- };
1155
- var ConstAttrs = class _ConstAttrs extends Attributes {
1156
- eval(_stack) {
1157
- return this.items;
1158
- }
1159
- static fromAttrs(attrs) {
1160
- const attrsObj = {};
1161
- for (const attr of attrs) attrsObj[attr.name] = attr.val.eval(null);
1162
- return new _ConstAttrs(attrsObj);
1163
- }
1164
- setDataAttr(key, val) {
1165
- this.items[key] = val;
1166
- }
1167
- toMacroVars() {
1168
- const r = {};
1169
- for (const name in this.items) r[name] = new ConstVal(`${this.items[name]}`).toString();
1170
- return r;
1171
- }
1172
- isConstant() {
1173
- return true;
1174
- }
1175
- };
1176
- var DynAttrs = class extends Attributes {
1177
- eval(stack) {
1178
- const attrs = {};
1179
- for (let i = 0; i < this.items.length; i++) {
1180
- const attr = this.items[i];
1181
- attrs[attr.name] = attr.eval(stack);
1182
- }
1183
- return attrs;
1184
- }
1185
- setDataAttr(key, val) {
1186
- this.items.push(new ConstAttr(key, new ConstVal(val)));
1187
- }
1188
- toMacroVars() {
1189
- const r = {};
1190
- for (const attr of this.items) r[attr.name] = attr.val.toString();
1191
- return r;
1192
- }
1193
- };
1194
- var BaseAttr = class {
1195
- constructor(name) {
1196
- this.name = name;
1197
- }
1198
- };
1199
- var Attr = class extends BaseAttr {
1200
- constructor(name, val) {
1201
- super(name);
1202
- this.val = val;
1203
- }
1204
- eval(stack) {
1205
- return this.val.eval(stack);
1206
- }
1207
- };
1208
- var ConstAttr = class extends Attr {
1209
- };
1210
- var RawHtmlAttr = class extends Attr {
1211
- constructor(val) {
1212
- super("dangerouslySetInnerHTML", val ?? NULL_CONST_VAL);
1213
- }
1214
- eval(stack) {
1215
- return { __html: `${this.val.eval(stack)}` };
1216
- }
1217
- };
1218
- var NOT_SET_VAL = NULL_CONST_VAL;
1219
- var IfAttr = class extends BaseAttr {
1220
- constructor(name, condVal) {
1221
- super(name);
1222
- this.condVal = condVal;
1223
- this.thenVal = this.elseVal = NOT_SET_VAL;
1224
- }
1225
- get anyBranchIsSet() {
1226
- return this.thenVal !== NOT_SET_VAL || this.elseVal !== NOT_SET_VAL;
1227
- }
1228
- eval(stack) {
1229
- return this.condVal.eval(stack) ? this.thenVal.eval(stack) : this.elseVal.eval(stack);
1230
- }
1231
- };
1232
- var _attrParser = null;
1233
- function getAttrParser(px) {
1234
- _attrParser ??= new AttrParser(px);
1235
- _attrParser.clear(px);
1236
- return _attrParser;
1237
- }
1238
- var EventHandler = class _EventHandler {
1239
- constructor(handlerVal, args = []) {
1240
- this.handlerVal = handlerVal;
1241
- this.args = args;
1242
- }
1243
- getHandlerAndArgs(stack, _event) {
1244
- const argValues = new Array(this.args.length);
1245
- for (let i = 0; i < argValues.length; i++) argValues[i] = this.args[i].eval(stack);
1246
- return [this.handlerVal.evalAsHandler(stack), argValues];
1247
- }
1248
- static parse(s, px) {
1249
- const r = parseReceiveHandler(s, px);
1250
- return r === null ? null : new _EventHandler(r.handlerVal, r.args);
1251
- }
1252
- };
1253
- var IntentHandler = class {
1254
- constructor(name, fn) {
1255
- this.name = name;
1256
- this.fn = fn;
1257
- }
1258
- };
1259
-
1260
- // src/cache.js
1261
- var isWeakKey = (k) => k !== null && (typeof k === "object" || typeof k === "function");
1262
- var NullDomCache = class {
1263
- get(_keys, _cacheKey) {
1264
- }
1265
- set(_keys, _cacheKey, _v) {
1266
- }
1267
- evict() {
1268
- return { hit: 0, miss: 0, badKey: 0 };
1269
- }
1270
- };
1271
- var WeakMapDomCache = class {
1272
- constructor() {
1273
- this.hit = this.miss = this.badKey = 0;
1274
- this.keysByLen = /* @__PURE__ */ new Map();
1275
- }
1276
- _returnValue(r) {
1277
- if (r === void 0) this.miss += 1;
1278
- else this.hit += 1;
1279
- return r;
1280
- }
1281
- get(keys, cacheKey) {
1282
- const len = keys.length;
1283
- let cur = this.keysByLen.get(len);
1284
- if (!cur) return this._returnValue(void 0);
1285
- for (let i = 0; i < len - 1; i++) {
1286
- cur = cur.get(keys[i]);
1287
- if (!cur) return this._returnValue(void 0);
1288
- }
1289
- return this._returnValue(cur.get(keys[len - 1])?.[cacheKey]);
1290
- }
1291
- set(keys, cacheKey, v) {
1292
- const len = keys.length;
1293
- let cur = this.keysByLen.get(len);
1294
- if (!cur) {
1295
- cur = /* @__PURE__ */ new WeakMap();
1296
- this.keysByLen.set(len, cur);
1297
- }
1298
- for (let i = 0; i < len - 1; i++) {
1299
- const key = keys[i];
1300
- let next = cur.get(key);
1301
- if (!next) {
1302
- if (!isWeakKey(key)) {
1303
- this.badKey += 1;
1304
- return;
1305
- }
1306
- next = /* @__PURE__ */ new WeakMap();
1307
- cur.set(key, next);
1308
- }
1309
- cur = next;
1310
- }
1311
- const lastKey = keys[len - 1];
1312
- const leaf = cur.get(lastKey);
1313
- if (leaf) leaf[cacheKey] = v;
1314
- else if (isWeakKey(lastKey)) cur.set(lastKey, { [cacheKey]: v });
1315
- else this.badKey += 1;
1316
- }
1317
- evict() {
1318
- const { hit, miss, badKey } = this;
1319
- this.hit = this.miss = this.badKey = 0;
1320
- this.keysByLen = /* @__PURE__ */ new Map();
1321
- return { hit, miss, badKey };
1322
- }
1323
- };
1324
-
1325
- // src/vdom.js
1326
- var HTML_NS = "http://www.w3.org/1999/xhtml";
1327
- var SVG_NS = "http://www.w3.org/2000/svg";
1328
- var MATH_NS = "http://www.w3.org/1998/Math/MathML";
1329
- var isNamespaced = (node) => {
1330
- const ns = node.namespaceURI;
1331
- return ns !== null && ns !== HTML_NS;
1332
- };
1333
- var isForeignObject = (tag) => tag.length === 13 && tag.toLowerCase() === "foreignobject";
1334
- var effectiveNs = (vnode, opts) => vnode.namespace ?? opts.namespace ?? null;
1335
- function childOpts(vnode, ns, opts) {
1336
- const target = ns === SVG_NS && isForeignObject(vnode.tag) ? null : ns;
1337
- return target === (opts.namespace ?? null) ? opts : { ...opts, namespace: target };
1338
- }
1339
- var NEVER_ASSIGN = /* @__PURE__ */ new Set([
1340
- "width",
1341
- "height",
1342
- "href",
1343
- "list",
1344
- "form",
1345
- "tabIndex",
1346
- "download",
1347
- "rowSpan",
1348
- "colSpan",
1349
- "role",
1350
- "popover"
1351
- ]);
1352
- var PROP_ATTR_NAME = { className: "class", htmlFor: "for" };
1353
- function applyProperties(node, props) {
1354
- const namespaced = isNamespaced(node);
1355
- for (const name in props) setProp(node, name, props[name], namespaced);
1356
- }
1357
- function setProp(node, name, value, namespaced) {
1358
- if (name === "dangerouslySetInnerHTML") {
1359
- if (value === void 0) node.replaceChildren();
1360
- else {
1361
- const html2 = value.__html ?? "";
1362
- if (html2 !== node.innerHTML) node.innerHTML = html2;
1363
- }
1364
- return;
1365
- }
1366
- if (typeof value === "function") return;
1367
- const usesProp = !namespaced && !NEVER_ASSIGN.has(name) && name in node;
1368
- if (usesProp && value != null) {
1369
- try {
1370
- node[name] = value;
1371
- return;
1372
- } catch {
1373
- }
1374
- }
1375
- if (value == null || value === false && name[4] !== "-") {
1376
- if (usesProp) {
1377
- try {
1378
- node[name] = "";
1379
- } catch {
1380
- }
1381
- node.removeAttribute(PROP_ATTR_NAME[name] ?? name);
1382
- } else node.removeAttribute(name);
1383
- } else node.setAttribute(name, value);
1384
- }
1385
- function applyValueLast(node, value) {
1386
- if (node.tagName === "PROGRESS" && (value == null || value === 0)) {
1387
- node.removeAttribute("value");
1388
- } else {
1389
- setProp(node, "value", value, isNamespaced(node));
1390
- }
1391
- }
1392
- var VBase = class {
1393
- };
1394
- var getKey = (child) => child instanceof VNode ? child.key : void 0;
1395
- var isIterable = (obj) => obj != null && typeof obj !== "string" && typeof obj[Symbol.iterator] === "function";
1396
- function childsEqual(a, b) {
1397
- if (a === b) return true;
1398
- for (let i = 0; i < a.length; i++) if (!a[i].isEqualTo(b[i])) return false;
1399
- return true;
1400
- }
1401
- function appendChildNodes(parent, childs, opts) {
1402
- for (const child of childs) parent.appendChild(child.toDom(opts));
1403
- }
1404
- function addChild(normalizedChildren, child) {
1405
- if (child == null) return;
1406
- if (isIterable(child)) {
1407
- for (const c of child) addChild(normalizedChildren, c);
1408
- } else if (child instanceof VBase) {
1409
- if (child instanceof VFragment) normalizedChildren.push(...child.childs);
1410
- else normalizedChildren.push(child);
1411
- } else normalizedChildren.push(new VText(child));
1412
- }
1413
- var VText = class _VText extends VBase {
1414
- constructor(text) {
1415
- super();
1416
- this.text = String(text);
1417
- }
1418
- get nodeType() {
1419
- return 3;
1420
- }
1421
- isEqualTo(other) {
1422
- return other instanceof _VText && this.text === other.text;
1423
- }
1424
- toDom(opts) {
1425
- return opts.document.createTextNode(this.text);
1426
- }
1427
- };
1428
- var VComment = class _VComment extends VBase {
1429
- constructor(text) {
1430
- super();
1431
- this.text = text;
1432
- }
1433
- get nodeType() {
1434
- return 8;
1435
- }
1436
- isEqualTo(other) {
1437
- return other instanceof _VComment && this.text === other.text;
1438
- }
1439
- toDom(opts) {
1440
- return opts.document.createComment(this.text);
1441
- }
1442
- };
1443
- var VFragment = class _VFragment extends VBase {
1444
- constructor(childs) {
1445
- super();
1446
- this.childs = [];
1447
- addChild(this.childs, childs);
1448
- }
1449
- get nodeType() {
1450
- return 11;
1451
- }
1452
- isEqualTo(other) {
1453
- if (!(other instanceof _VFragment) || this.childs.length !== other.childs.length) return false;
1454
- return childsEqual(this.childs, other.childs);
1455
- }
1456
- toDom(opts) {
1457
- const fragment = opts.document.createDocumentFragment();
1458
- appendChildNodes(fragment, this.childs, opts);
1459
- return fragment;
1460
- }
1461
- };
1462
- var VNode = class _VNode extends VBase {
1463
- constructor(tag, attrs, childs, key, namespace) {
1464
- super();
1465
- this.tag = tag;
1466
- this.attrs = attrs ?? {};
1467
- this.childs = childs ?? [];
1468
- this.key = key != null ? String(key) : void 0;
1469
- this.namespace = typeof namespace === "string" ? namespace : null;
1470
- }
1471
- get nodeType() {
1472
- return 1;
1473
- }
1474
- isSameKind(other) {
1475
- return this.tag === other.tag && this.namespace === other.namespace && this.key === other.key;
1476
- }
1477
- isEqualTo(other) {
1478
- if (this === other) return true;
1479
- if (!(other instanceof _VNode) || !this.isSameKind(other) || this.childs.length !== other.childs.length) {
1480
- return false;
1481
- }
1482
- if (this.attrs !== other.attrs) {
1483
- for (const key in this.attrs) if (this.attrs[key] !== other.attrs[key]) return false;
1484
- for (const key in other.attrs) if (!Object.hasOwn(this.attrs, key)) return false;
1485
- }
1486
- return childsEqual(this.childs, other.childs);
1487
- }
1488
- toDom(opts) {
1489
- const doc = opts.document;
1490
- const ns = effectiveNs(this, opts);
1491
- const tag = ns !== null && this.tag === this.tag.toUpperCase() ? this.tag.toLowerCase() : this.tag;
1492
- const attrs = this.attrs;
1493
- const createOpts = attrs.is != null ? { is: attrs.is } : void 0;
1494
- const node = ns === null ? doc.createElement(tag, createOpts) : doc.createElementNS(ns, tag, createOpts);
1495
- const cOpts = childOpts(this, ns, opts);
1496
- if ("value" in attrs || "checked" in attrs) {
1497
- const { value, checked, ...rest } = attrs;
1498
- applyProperties(node, rest);
1499
- appendChildNodes(node, this.childs, cOpts);
1500
- if (value !== void 0) applyValueLast(node, value);
1501
- if (checked !== void 0) setProp(node, "checked", checked, false);
1502
- } else {
1503
- applyProperties(node, attrs);
1504
- appendChildNodes(node, this.childs, cOpts);
1505
- }
1506
- return node;
1507
- }
1508
- };
1509
- function diffProps(a, b) {
1510
- if (a === b) return null;
1511
- let diff = null;
1512
- for (const aKey in a) {
1513
- if (!Object.hasOwn(b, aKey)) {
1514
- diff ??= {};
1515
- diff[aKey] = void 0;
1516
- } else if (a[aKey] !== b[aKey]) {
1517
- diff ??= {};
1518
- diff[aKey] = b[aKey];
1519
- }
1520
- }
1521
- for (const bKey in b) {
1522
- if (!Object.hasOwn(a, bKey)) {
1523
- diff ??= {};
1524
- diff[bKey] = b[bKey];
1525
- }
1526
- }
1527
- return diff;
1528
- }
1529
- function morphNode(domNode, source, target, opts) {
1530
- if (source === target || source.isEqualTo(target)) return domNode;
1531
- const type = source.nodeType;
1532
- if (type === target.nodeType) {
1533
- if (type === 3 || type === 8) {
1534
- domNode.data = target.text;
1535
- return domNode;
1536
- }
1537
- if (type === 1 && source.isSameKind(target)) {
1538
- const propsDiff = diffProps(source.attrs, target.attrs);
1539
- const hasValue = propsDiff != null && "value" in propsDiff;
1540
- const hasChecked = propsDiff != null && "checked" in propsDiff;
1541
- if (propsDiff) {
1542
- if (hasValue || hasChecked) {
1543
- const { value: _v, checked: _c, ...rest } = propsDiff;
1544
- applyProperties(domNode, rest);
1545
- } else applyProperties(domNode, propsDiff);
1546
- }
1547
- if (!target.attrs.dangerouslySetInnerHTML) {
1548
- const ns = effectiveNs(target, opts);
1549
- morphChildren(domNode, source.childs, target.childs, childOpts(target, ns, opts));
1550
- }
1551
- if (hasValue) applyValueLast(domNode, propsDiff.value);
1552
- else if (source.tag === "SELECT" && target.attrs.value !== void 0)
1553
- applyValueLast(domNode, target.attrs.value);
1554
- if (hasChecked) setProp(domNode, "checked", propsDiff.checked, false);
1555
- return domNode;
1556
- }
1557
- if (type === 11) {
1558
- morphChildren(domNode, source.childs, target.childs, opts);
1559
- return domNode;
1560
- }
1561
- }
1562
- const newNode = target.toDom(opts);
1563
- domNode.parentNode?.replaceChild(newNode, domNode);
1564
- return newNode;
1565
- }
1566
- function morphChildren(parentDom, oldChilds, newChilds, opts) {
1567
- if (oldChilds.length === 0) {
1568
- appendChildNodes(parentDom, newChilds, opts);
1569
- return;
1570
- }
1571
- if (newChilds.length === 0) {
1572
- parentDom.replaceChildren();
1573
- return;
1574
- }
1575
- if (oldChilds.length === newChilds.length) {
1576
- let hasKey = false;
1577
- for (let i = 0; i < oldChilds.length; i++) {
1578
- if (getKey(oldChilds[i]) != null || getKey(newChilds[i]) != null) {
1579
- hasKey = true;
1580
- break;
1581
- }
1582
- }
1583
- if (!hasKey) {
1584
- let dom = parentDom.firstChild;
1585
- for (let i = 0; i < oldChilds.length; i++) {
1586
- const next = dom.nextSibling;
1587
- morphNode(dom, oldChilds[i], newChilds[i], opts);
1588
- dom = next;
1589
- }
1590
- return;
1591
- }
1592
- }
1593
- const domNodes = Array.from(parentDom.childNodes);
1594
- const oldKeyMap = /* @__PURE__ */ Object.create(null);
1595
- for (let i = 0; i < oldChilds.length; i++) {
1596
- const key = getKey(oldChilds[i]);
1597
- if (key != null) oldKeyMap[key] = i;
1598
- }
1599
- const used = new Uint8Array(oldChilds.length);
1600
- let unkeyedCursor = 0;
1601
- for (let j = 0; j < newChilds.length; j++) {
1602
- const newChild = newChilds[j];
1603
- const newKey = getKey(newChild);
1604
- let oldIdx = -1;
1605
- if (newKey != null) {
1606
- if (newKey in oldKeyMap && !used[oldKeyMap[newKey]]) oldIdx = oldKeyMap[newKey];
1607
- } else {
1608
- while (unkeyedCursor < oldChilds.length) {
1609
- if (!used[unkeyedCursor] && getKey(oldChilds[unkeyedCursor]) == null) {
1610
- oldIdx = unkeyedCursor++;
1611
- break;
1612
- }
1613
- unkeyedCursor++;
1614
- }
1615
- }
1616
- if (oldIdx >= 0) {
1617
- used[oldIdx] = 1;
1618
- const newDom = morphNode(domNodes[oldIdx], oldChilds[oldIdx], newChild, opts);
1619
- const ref = parentDom.childNodes[j] ?? null;
1620
- if (newDom !== ref) parentDom.insertBefore(newDom, ref);
1621
- } else {
1622
- const ref = parentDom.childNodes[j] ?? null;
1623
- parentDom.insertBefore(newChild.toDom(opts), ref);
1624
- }
1625
- }
1626
- for (let i = oldChilds.length - 1; i >= 0; i--)
1627
- if (!used[i] && domNodes[i].parentNode === parentDom) parentDom.removeChild(domNodes[i]);
1628
- }
1629
- function render(vnode, container, options, prev) {
1630
- const isFragment = vnode instanceof VFragment;
1631
- if (prev && prev.vnode instanceof VFragment === isFragment) {
1632
- const oldDom = isFragment ? container : prev.dom;
1633
- const newDom = morphNode(oldDom, prev.vnode, vnode, options);
1634
- return { vnode, dom: isFragment ? container : newDom };
1635
- }
1636
- const domNode = vnode.toDom(options);
1637
- container.replaceChildren(domNode);
1638
- return { vnode, dom: isFragment ? container : domNode };
1639
- }
1640
- function h(tagName, properties, children, namespace) {
1641
- const props = {};
1642
- let key;
1643
- if (properties) {
1644
- for (const propName in properties) {
1645
- const propVal = properties[propName];
1646
- if (propName === "key") key = propVal;
1647
- else if (propName === "namespace") namespace = namespace ?? propVal;
1648
- else props[propName] = propVal;
1649
- }
1650
- }
1651
- if (namespace == null) {
1652
- const lower = tagName.toLowerCase();
1653
- if (lower === "svg") {
1654
- namespace = SVG_NS;
1655
- tagName = "svg";
1656
- } else if (lower === "math") {
1657
- namespace = MATH_NS;
1658
- tagName = "math";
1659
- }
1660
- }
1661
- const c = tagName.charCodeAt(0);
1662
- const tag = namespace == null && c >= 97 && c <= 122 && tagName === tagName.toLowerCase() ? tagName.toUpperCase() : tagName;
1663
- const normalizedChildren = [];
1664
- addChild(normalizedChildren, children);
1665
- return new VNode(tag, props, normalizedChildren, key, namespace);
1666
- }
1667
-
1668
- // src/renderer.js
1669
- var DATASET_ATTRS = ["nid", "cid", "eid", "vid", "si", "sk"];
1670
- var Renderer = class {
1671
- constructor(comps) {
1672
- this.comps = comps;
1673
- this.cache = new WeakMapDomCache();
1674
- this.renderTag = h;
1675
- }
1676
- renderFragment(childs) {
1677
- return new VFragment(childs);
1678
- }
1679
- renderComment(text) {
1680
- return new VComment(text);
1681
- }
1682
- setNullCache() {
1683
- this.cache = new NullDomCache();
1684
- }
1685
- renderToDOM(stack, val) {
1686
- const rootNode = document.createElement("div");
1687
- const rOpts = { document };
1688
- render(h("DIV", null, [this.renderRoot(stack, val)]), rootNode, rOpts);
1689
- return rootNode.childNodes[0];
1690
- }
1691
- renderToString(stack, val, cleanAttrs = true) {
1692
- const dom = this.renderToDOM(stack, val);
1693
- if (cleanAttrs) {
1694
- const nodes = dom.querySelectorAll("[data-nid],[data-cid],[data-eid]");
1695
- for (const { dataset } of nodes) for (const name of DATASET_ATTRS) delete dataset[name];
1696
- }
1697
- return dom.innerHTML;
1698
- }
1699
- renderRoot(stack, val, viewName = null) {
1700
- const comp = this.comps.getCompFor(val);
1701
- if (comp === null) return null;
1702
- return this._rValComp(stack, val, comp, comp.getView(viewName).anode, "ROOT", viewName);
1703
- }
1704
- renderIt(stack, node, key, viewName) {
1705
- const comp = this.comps.getCompFor(stack.it);
1706
- return comp ? this._rValComp(stack, stack.it, comp, node, key, viewName) : null;
1707
- }
1708
- // `node` is the parse node of the render site (`<x render>` / `render-it` /
1709
- // `render-each`, or the view's root anode for the app root). It keys the
1710
- // cache as a globally-unique object: node ids alone are unique only within a
1711
- // single view, so the same value rendered by two components (e.g. through a
1712
- // shared dynamic-var sequence) would otherwise collide in the cache.
1713
- _rValComp(stack, val, comp, node, key, viewName) {
1714
- const cacheKey = `${viewName ?? ""}${stack.viewsId ?? ""}${key}`;
1715
- const cachePath = [node, val];
1716
- stack._pushDynBindValuesToArray(cachePath, comp);
1717
- const cachedNode = this.cache.get(cachePath, cacheKey);
1718
- if (cachedNode) return cachedNode;
1719
- const view = viewName ? comp.getView(viewName) : stack.lookupBestView(comp.views, "main");
1720
- const body = this.renderView(view, stack);
1721
- if (body == null) return null;
1722
- const meta = this._renderMetadata({
1723
- $: "Comp",
1724
- nid: node?.nodeId ?? null,
1725
- cid: comp.id,
1726
- vid: view.name
1727
- });
1728
- const dom = new VFragment([meta, body]);
1729
- this.cache.set(cachePath, cacheKey, dom);
1730
- return dom;
1731
- }
1732
- pushEachEntry(r, nid, attrName, key, dom) {
1733
- r.push(this._renderMetadata({ $: "Each", nid, [attrName]: key }), dom);
1734
- }
1735
- renderEachWhen(stack, iterInfo, view, nid) {
1736
- const { seq, filter, loopWith, enricher } = iterInfo.eval(stack);
1737
- const r = [];
1738
- const it = stack.it;
1739
- const { iterData, start, end, keys } = unpackLoopResult(
1740
- loopWith.call(it, seq, makeLoopCtx(stack, filter)),
1741
- seq
1742
- );
1743
- const renderOne = (key, value, attrName) => {
1744
- const cachePath = enricher ? [view, it, value] : [view, value];
1745
- const binds = { key, value };
1746
- const cacheKey = `${stack.viewsId ?? ""}${nid}${key}`;
1747
- if (enricher) callEnricher(enricher, it, binds, key, value, iterData);
1748
- const cachedNode = this.cache.get(cachePath, cacheKey);
1749
- if (cachedNode) this.pushEachEntry(r, nid, attrName, key, cachedNode);
1750
- else {
1751
- const dom = this.renderView(view, stack.enter(value, binds, false));
1752
- if (dom != null) this.pushEachEntry(r, nid, attrName, key, dom);
1753
- this.cache.set(cachePath, cacheKey, dom);
1754
- }
1755
- };
1756
- if (keys) keysIter(seq, renderOne, keys);
1757
- else
1758
- getSeqInfo(seq)(
1759
- seq,
1760
- (key, value, attrName) => {
1761
- if (filter.call(it, key, value, iterData)) renderOne(key, value, attrName);
1762
- },
1763
- start,
1764
- end
1765
- );
1766
- return r;
1767
- }
1768
- renderView(view, stack) {
1769
- let n = stack.binds[1];
1770
- while (n !== null) {
1771
- const b = n[0];
1772
- if (b.isFrame) {
1773
- if (stack.it !== b.it) break;
1774
- console.error("recursion detected", stack.it, b.it);
1775
- return new VComment("RECURSION AVOIDED");
1776
- }
1777
- n = n[1];
1778
- }
1779
- return view.render(stack, this);
1780
- }
1781
- _renderMetadata(info) {
1782
- return new VComment(`§${JSON.stringify(info)}§`);
1783
- }
1784
- // Prefix a loop-less @enrich-with subtree with a boundary meta so event-path
1785
- // reconstruction replays its binds (mirrors the §Each§ / §Comp§ metas).
1786
- renderScopeMeta(nid, dom) {
1787
- return new VFragment([this._renderMetadata({ $: "Scope", nid }), dom]);
1788
- }
1789
- };
1790
- var getSeqInfo = (seq) => isIndexedSeq(seq) ? nativeIndexedIter : isKeyedSeq(seq) || isSetSeq(seq) ? nativeKeyedIter : seq?.[SEQ_INFO] ?? unkIter;
1791
- var normalizeRange = (start, end, size) => {
1792
- let s = start == null ? 0 : start < 0 ? size + start : start;
1793
- let e = end == null ? size : end < 0 ? size + end : end;
1794
- s = s < 0 ? 0 : s > size ? size : s;
1795
- e = e < 0 ? 0 : e > size ? size : e;
1796
- return [s, e < s ? s : e];
1797
- };
1798
- var callEnricher = (enricher, it, binds, key, value, iterData) => {
1799
- enricher.call(it, binds, key, value, iterData);
1800
- console.assert(
1801
- binds.key === key && binds.value === value,
1802
- "@enrich-with handlers must not overwrite binds.key or binds.value"
1803
- );
1804
- binds.key = key;
1805
- binds.value = value;
1806
- };
1807
- var filterAlwaysTrue = (_v, _k, _seq) => true;
1808
- var nullLoopWith = (seq) => ({ iterData: { seq } });
1809
- var unpackLoopResult = (result, seq) => {
1810
- const r = result ?? {};
1811
- return { iterData: r.iterData ?? { seq }, start: r.start, end: r.end, keys: r.keys };
1812
- };
1813
- var keysIter = (seq, visit, keys) => {
1814
- const attrName = isIndexedSeq(seq) ? "si" : "sk";
1815
- for (const key of keys) visit(key, seqGet(seq, key), attrName);
1816
- };
1817
- var makeLoopCtx = (stack, filter) => ({
1818
- lookup: (name) => stack.lookupBind(name),
1819
- filter: (key, value, iterData) => filter.call(stack.it, key, value, iterData)
1820
- });
1821
- var nativeIndexedIter = (seq, visit, start, end) => {
1822
- const [s, e] = normalizeRange(start, end, seqSize(seq));
1823
- for (let i = s; i < e; i++) visit(i, seq[i], "si");
1824
- };
1825
- var nativeKeyedIter = (seq, visit, start, end) => {
1826
- const [s, e] = normalizeRange(start, end, seqSize(seq));
1827
- let i = 0;
1828
- for (const [k, v] of seqEntries(seq)) {
1829
- if (i >= e) break;
1830
- if (i >= s) visit(k, v, "sk");
1831
- i++;
1832
- }
1833
- };
1834
- var unkIter = () => {
1835
- };
1836
- var SEQ_INFO = /* @__PURE__ */ Symbol.for("tutuca.seqInfo");
1837
-
1838
- // src/util/env.js
1839
- var isMac = (globalThis.navigator?.userAgent ?? "").toLowerCase().includes("mac");
1840
-
1841
- // src/anode.js
1842
- function resolveDynProducer(comp, name) {
1843
- let producerComp, producerProvide;
1844
- const lk = comp?.lookup?.[name];
1845
- if (lk != null) {
1846
- producerComp = comp.scope?.lookupComponent(lk.compName);
1847
- producerProvide = producerComp?.provide?.[lk.provideName];
1848
- } else {
1849
- const p = comp?.provide?.[name];
1850
- if (p == null) return null;
1851
- producerComp = comp;
1852
- producerProvide = p;
1853
- }
1854
- if (producerComp == null || producerProvide == null) return null;
1855
- const pi = producerProvide.val?.toPathItem?.() ?? null;
1856
- return { producerCompId: producerComp.id, producerSteps: pi ? [pi] : [] };
1857
- }
1858
- var BaseNode = class {
1859
- render(_stack, _rx) {
1860
- return null;
1861
- }
1862
- setDataAttr(key, val) {
1863
- console.warn("setDataAttr not implemented for", this, { key, val });
1864
- }
1865
- isConstant() {
1866
- return false;
1867
- }
1868
- isWhiteSpace() {
1869
- return false;
1870
- }
1871
- optimize() {
1872
- }
1873
- };
1874
- var TextNode = class extends BaseNode {
1875
- constructor(val) {
1876
- super();
1877
- this.val = val;
1878
- }
1879
- render(_stack, _rx) {
1880
- return this.val;
1881
- }
1882
- isWhiteSpace() {
1883
- for (let i = 0; i < this.val.length; i++) {
1884
- const c = this.val.charCodeAt(i);
1885
- if (!(c === 32 || c === 10 || c === 9 || c === 13)) return false;
1886
- }
1887
- return true;
1888
- }
1889
- hasNewLine() {
1890
- for (let i = 0; i < this.val.length; i++) {
1891
- const c = this.val.charCodeAt(i);
1892
- if (c === 10 || c === 13) return true;
1893
- }
1894
- return false;
1895
- }
1896
- condenseWhiteSpace(replacement = "") {
1897
- this.val = replacement;
1898
- }
1899
- isConstant() {
1900
- return true;
1901
- }
1902
- setDataAttr(_key, _val) {
1903
- }
1904
- };
1905
- var CommentNode = class extends TextNode {
1906
- render(_stack, rx) {
1907
- return rx.renderComment(this.val);
1908
- }
1909
- };
1910
- function optimizeChilds(childs) {
1911
- for (let i = 0; i < childs.length; i++) {
1912
- const child = childs[i];
1913
- if (child.isConstant()) childs[i] = new RenderOnceNode(child);
1914
- else child.optimize();
1915
- }
1916
- }
1917
- function optimizeNode(node) {
1918
- if (node.isConstant()) return new RenderOnceNode(node);
1919
- node.optimize();
1920
- return node;
1921
- }
1922
- var ChildsNode = class extends BaseNode {
1923
- constructor(childs) {
1924
- super();
1925
- this.childs = childs;
1926
- }
1927
- isConstant() {
1928
- return this.childs.every((v) => v.isConstant());
1929
- }
1930
- optimize() {
1931
- optimizeChilds(this.childs);
1932
- }
1933
- };
1934
- var DomNode = class extends ChildsNode {
1935
- constructor(tagName, attrs, childs, namespace = null) {
1936
- super(childs);
1937
- this.tagName = tagName;
1938
- this.attrs = attrs;
1939
- this.namespace = namespace;
1940
- }
1941
- render(stack, rx) {
1942
- const childNodes = new Array(this.childs.length);
1943
- for (let i = 0; i < childNodes.length; i++)
1944
- childNodes[i] = this.childs[i]?.render?.(stack, rx) ?? null;
1945
- return rx.renderTag(this.tagName, this.attrs.eval(stack), childNodes, this.namespace);
1946
- }
1947
- setDataAttr(key, val) {
1948
- this.attrs.setDataAttr(key, val);
1949
- }
1950
- isConstant() {
1951
- return this.attrs.isConstant() && super.isConstant();
1952
- }
1953
- };
1954
- var FragmentNode = class extends ChildsNode {
1955
- render(stack, rx) {
1956
- return rx.renderFragment(this.childs.map((c) => c?.render(stack, rx)));
1957
- }
1958
- setDataAttr(key, val) {
1959
- for (const child of this.childs) child.setDataAttr(key, val);
1960
- }
1961
- };
1962
- var maybeFragment = (xs) => xs.length === 1 ? xs[0] : new FragmentNode(xs);
1963
- var VALID_NODE_RE = /^[a-zA-Z][a-zA-Z0-9-]*$/;
1964
- var ANode = class _ANode extends BaseNode {
1965
- constructor(nodeId, val) {
1966
- super();
1967
- this.nodeId = nodeId;
1968
- this.val = val;
1969
- }
1970
- toPathStep(ctx) {
1971
- return ctx.applyKey(this.val?.toPathItem?.() ?? null);
1972
- }
1973
- static parse(html2, px) {
1974
- const nodes = px.parseHTML(html2);
1975
- if (nodes.length === 0) return new CommentNode("Empty View in ANode.parse");
1976
- if (nodes.length === 1) return _ANode.fromDOM(nodes[0], px);
1977
- const childs = [];
1978
- for (let i = 0; i < nodes.length; i++) {
1979
- const child = _ANode.fromDOM(nodes[i], px);
1980
- if (child !== null) childs.push(child);
1981
- }
1982
- const trimmed = condenseChildsWhites(childs);
1983
- if (trimmed.length === 0) return new CommentNode("Empty View in ANode.parse");
1984
- return maybeFragment(trimmed);
1985
- }
1986
- static fromDOM(node, px) {
1987
- if (node instanceof px.Text) return new TextNode(node.textContent);
1988
- else if (node instanceof px.Comment) return new CommentNode(node.textContent);
1989
- const { childNodes, attributes: attrs, tagName: tag } = node;
1990
- const childs = [];
1991
- for (let i = 0; i < childNodes.length; i++) {
1992
- const child = _ANode.fromDOM(childNodes[i], px);
1993
- if (child !== null) childs.push(child);
1994
- }
1995
- const prevTag = px.currentTag;
1996
- px.currentTag = tag;
1997
- try {
1998
- const isPseudoX = attrs[0]?.name === "@x";
1999
- if (tag === "X" || isPseudoX) return parseXOp(attrs, childs, isPseudoX ? 1 : 0, px);
2000
- else if (tag.charCodeAt(1) === 58 && (tag.charCodeAt(0) === 88 || tag.charCodeAt(0) === 120)) {
2001
- const macroName = tag.slice(2).toLowerCase();
2002
- if (macroName === "slot") {
2003
- const slotName = attrs.getNamedItem("name")?.value ?? "_";
2004
- return px.frame.macroSlots[slotName] ?? maybeFragment(childs);
2005
- }
2006
- const [nAttrs, wrappers] = Attributes.parse(attrs, px, true);
2007
- px.onAttributes(nAttrs, wrappers, null, true, tag);
2008
- return wrap(px.newMacroNode(macroName, nAttrs.toMacroVars(), childs), px, wrappers);
2009
- } else if (VALID_NODE_RE.test(tag)) {
2010
- const [nAttrs, wrappers, textChild] = Attributes.parse(attrs, px);
2011
- px.onAttributes(nAttrs, wrappers, textChild, false, tag);
2012
- if (textChild) childs.unshift(new RenderTextNode(null, textChild));
2013
- const domChilds = tag !== "PRE" ? condenseChildsWhites(childs) : childs;
2014
- const ns = node.namespaceURI;
2015
- const namespace = ns && ns !== HTML_NS ? ns : null;
2016
- return wrap(new DomNode(tag, nAttrs, domChilds, namespace), px, wrappers);
2017
- }
2018
- return new CommentNode(`Error: InvalidTagName ${tag}`);
2019
- } finally {
2020
- px.currentTag = prevTag;
2021
- }
2022
- }
2023
- };
2024
- function parseXOp(attrs, childs, opIdx, px) {
2025
- if (attrs.length <= opIdx) return maybeFragment(childs);
2026
- const { name, value } = attrs[opIdx];
2027
- if (X_OPS[name]?.ignoresChildren && hasMeaningfulChilds(childs))
2028
- px.onParseIssue("x-op-ignores-children", { op: name });
2029
- const asAttr = attrs.getNamedItem("as")?.value ?? null;
2030
- const as = asAttr === null ? null : parseViewName(asAttr, px);
2031
- let node;
2032
- switch (name) {
2033
- case "slot":
2034
- node = new SlotNode(null, new ConstVal(value), maybeFragment(childs));
2035
- break;
2036
- case "text":
2037
- node = px.addNodeIf(RenderTextNode, parseXOpVal(name, value, px, parseText));
2038
- break;
2039
- case "render":
2040
- node = px.addNodeIf(RenderNode, parseXOpVal(name, value, px, parseComponent), as);
2041
- break;
2042
- case "render-it":
2043
- node = px.addNode(RenderItNode, as);
2044
- break;
2045
- case "render-each":
2046
- node = parseRenderEach(px, value, as, attrs);
2047
- break;
2048
- case "show": {
2049
- const val = parseXOpVal(name, value, px, parseBool);
2050
- node = px.addNodeIf(ShowNode, val, maybeFragment(childs));
2051
- break;
2052
- }
2053
- case "hide": {
2054
- const val = parseXOpVal(name, value, px, parseBool);
2055
- node = px.addNodeIf(HideNode, val, maybeFragment(childs));
2056
- break;
2057
- }
2058
- default:
2059
- px.onParseIssue("unknown-x-op", { name, value });
2060
- return new CommentNode(`Error: InvalidSpecialTagOp ${name}=${value}`);
2061
- }
2062
- return processXExtras(node, attrs, name, opIdx + 1, px);
2063
- }
2064
- function parseXOpVal(opName, value, px, parserFn) {
2065
- const val = parserFn(value, px);
2066
- if (val === null) px.onParseIssue("bad-value", { role: "x-op", op: opName, value });
2067
- return val;
2068
- }
2069
- function parseViewName(s, px) {
2070
- return parseText(s, px) ?? new ConstVal(s);
2071
- }
2072
- function processXExtras(node, attrs, opName, startIdx, px) {
2073
- const { consumed, wrappable } = X_OPS[opName];
2074
- const wrappers = [];
2075
- for (let i = startIdx; i < attrs.length; i++) {
2076
- const a = attrs[i];
2077
- const aName = a.name;
2078
- if (consumed.has(aName)) continue;
2079
- const atPrefixed = aName.charCodeAt(0) === 64;
2080
- const baseName = atPrefixed ? aName.slice(1) : aName;
2081
- const wrapper = wrappable ? X_OPS[baseName]?.wrapper : null;
2082
- if (wrapper) {
2083
- if (!atPrefixed) maybeDeprecateBareXDirective(px, opName, baseName);
2084
- wrappers.push([wrapper, parseBool(a.value, px)]);
2085
- continue;
2086
- }
2087
- const issueInfo = { op: opName, name: aName, value: a.value };
2088
- px.onParseIssue("unknown-x-attr", issueInfo);
2089
- }
2090
- for (let i = wrappers.length - 1; i >= 0; i--) {
2091
- const [Cls, val] = wrappers[i];
2092
- const wrapper = px.addNodeIf(Cls, val, node);
2093
- if (wrapper !== null) node = wrapper;
2094
- }
2095
- return node;
2096
- }
2097
- function maybeDeprecateBareXDirective(px, opName, name) {
2098
- px.onDeprecatedSyntax("bare-x-directive", { op: opName, name });
2099
- }
2100
- function wrap(node, px, wrappers) {
2101
- if (wrappers) {
2102
- for (let i = wrappers.length - 1; i >= 0; i--) {
2103
- const wrapperNode = makeWrapperNode(wrappers[i], px);
2104
- if (wrapperNode) {
2105
- wrapperNode.wrapNode(node);
2106
- node = wrapperNode;
2107
- }
2108
- }
2109
- }
2110
- return node;
2111
- }
2112
- function makeWrapperNode(data, px) {
2113
- const Cls = WRAPPER_NODES[data.name];
2114
- const node = Cls.register ? px.addNodeIf(Cls, data.val) : data.val && new Cls(null, data.val);
2115
- if (node !== null && data.name === "each") {
2116
- node.iterInfo.enrichWithVal = data.enrichWithVal ?? null;
2117
- node.iterInfo.whenVal = data.whenVal ?? null;
2118
- node.iterInfo.loopWithVal = data.loopWithVal ?? null;
2119
- }
2120
- return node;
2121
- }
2122
- var MacroNode = class extends BaseNode {
2123
- constructor(name, attrs, slots, px) {
2124
- super();
2125
- this.name = name;
2126
- this.attrs = attrs;
2127
- this.slots = slots;
2128
- this.px = px;
2129
- this.node = null;
2130
- this.dataAttrs = {};
2131
- }
2132
- compile(scope) {
2133
- const { name, attrs, slots } = this;
2134
- if (this.px.isInsideMacro(name)) throw new Error(`Recursive macro expansion: ${name}`);
2135
- const macro2 = scope.lookupMacro(name);
2136
- if (macro2 === null) this.node = new CommentNode(`bad macro: ${name}`);
2137
- else {
2138
- const vars = { ...macro2.defaults, ...attrs };
2139
- this.node = macro2.expand(this.px.enterMacro(name, vars, slots));
2140
- for (const key in this.dataAttrs) this.node.setDataAttr(key, this.dataAttrs[key]);
2141
- }
2142
- }
2143
- render(stack, rx) {
2144
- return this.node.render(stack, rx);
2145
- }
2146
- setDataAttr(key, val) {
2147
- this.dataAttrs[key] = val;
2148
- }
2149
- isConstant() {
2150
- return this.node.isConstant();
2151
- }
2152
- optimize() {
2153
- this.node = optimizeNode(this.node);
2154
- }
2155
- };
2156
- var Macro = class {
2157
- constructor(defaults, rawView) {
2158
- this.defaults = defaults;
2159
- this.rawView = rawView;
2160
- }
2161
- expand(px) {
2162
- return ANode.parse(this.rawView, px);
2163
- }
2164
- };
2165
- var RenderViewId = class extends ANode {
2166
- constructor(nodeId, val, viewVal) {
2167
- super(nodeId, val);
2168
- this.viewVal = viewVal;
2169
- }
2170
- // The `as=` view selector, evaluated against the host (enclosing) stack like
2171
- // `@push-view`. Null when `as=` is absent, so the renderer falls through to
2172
- // `stack.lookupBestView`.
2173
- evalViewName(stack) {
2174
- return this.viewVal ? this.viewVal.eval(stack) : null;
2175
- }
2176
- // A `<x render*>` produces no DOM element of its own to carry `data-cid`;
2177
- // when it is a view's root the component boundary is recorded in the `Comp`
2178
- // meta comment instead (see Renderer._rValComp), so this is a no-op.
2179
- setDataAttr(_key, _val) {
2180
- }
2181
- };
2182
- function dynRenderStep(comp, name, key) {
2183
- const p = resolveDynProducer(comp, name);
2184
- if (!p) return null;
2185
- return key === void 0 ? new DynStep(p.producerCompId, p.producerSteps) : new DynEachStep(p.producerCompId, p.producerSteps, key);
2186
- }
2187
- var RenderNode = class extends RenderViewId {
2188
- render(stack, rx) {
2189
- const newStack = stack.enter(this.val.eval(stack), {}, true);
2190
- return rx.renderIt(newStack, this, "", this.evalViewName(stack));
2191
- }
2192
- toPathStep(ctx) {
2193
- if (this.val instanceof DynVal) return dynRenderStep(ctx.comp, this.val.name);
2194
- return super.toPathStep(ctx);
2195
- }
2196
- };
2197
- var RenderItNode = class extends RenderViewId {
2198
- render(stack, rx) {
2199
- const newStack = stack.enter(stack.it, {}, true);
2200
- return rx.renderIt(newStack, this, "", this.evalViewName(stack));
2201
- }
2202
- toPathStep(ctx) {
2203
- const next = ctx.next();
2204
- if (next === null) return null;
2205
- const nextNode = next.resolveNode();
2206
- if (nextNode instanceof EachNode && next.hasKey) {
2207
- if (nextNode.val instanceof DynVal)
2208
- return dynRenderStep(ctx.comp, nextNode.val.name, next.key);
2209
- return new EachRenderItStep(nextNode.val.name, next.key);
2210
- }
2211
- return null;
2212
- }
2213
- };
2214
- function parseRenderEach(px, value, as, attrs) {
2215
- const seqVal = parseXOpVal("render-each", value, px, parseSequence);
2216
- if (seqVal === null) return null;
2217
- const renderIt = px.addNode(RenderItNode, as);
2218
- const attrParser = getAttrParser(px);
2219
- const eachAttr = attrParser.pushWrapper("each", value, seqVal);
2220
- attrParser.eachAttr = eachAttr;
2221
- const when = attrs.getNamedItem("@when") ?? attrs.getNamedItem("when");
2222
- if (when) {
2223
- if (when.name.charCodeAt(0) !== 64) maybeDeprecateBareXDirective(px, "render-each", "when");
2224
- attrParser._parseWhen(when.value);
2225
- }
2226
- const lWith = attrs.getNamedItem("@loop-with") ?? attrs.getNamedItem("loop-with");
2227
- if (lWith) {
2228
- if (lWith.name.charCodeAt(0) !== 64)
2229
- maybeDeprecateBareXDirective(px, "render-each", "loop-with");
2230
- attrParser._parseLoopWith(lWith.value);
2231
- }
2232
- const each = px.addNodeIf(EachNode, seqVal);
2233
- each.iterInfo.whenVal = eachAttr.whenVal ?? null;
2234
- each.iterInfo.loopWithVal = eachAttr.loopWithVal ?? null;
2235
- each.fromRenderEach = true;
2236
- each.wrapNode(renderIt);
2237
- return each;
2238
- }
2239
- var RenderTextNode = class extends ANode {
2240
- render(stack, _rx) {
2241
- return this.val.eval(stack);
2242
- }
2243
- // Renders to a text node, which can't carry `data-cid`.
2244
- setDataAttr(_key, _val) {
2245
- }
2246
- };
2247
- var RenderOnceNode = class extends BaseNode {
2248
- constructor(node) {
2249
- super();
2250
- this.node = node;
2251
- this._render = (stack, rx) => {
2252
- const dom = node.render(stack, rx);
2253
- this._render = (_stack, _rx) => dom;
2254
- return dom;
2255
- };
2256
- }
2257
- render(stack, rx) {
2258
- return this._render(stack, rx);
2259
- }
2260
- };
2261
- var WrapperNode = class extends ANode {
2262
- constructor(nodeId, val, node = null) {
2263
- super(nodeId, val);
2264
- this.node = node;
2265
- }
2266
- wrapNode(node) {
2267
- this.node = node;
2268
- }
2269
- setDataAttr(key, val) {
2270
- this.node.setDataAttr(key, val);
2271
- }
2272
- optimize() {
2273
- this.node = optimizeNode(this.node);
2274
- }
2275
- static register = false;
2276
- };
2277
- var ShowNode = class extends WrapperNode {
2278
- render(stack, rx) {
2279
- return this.val.eval(stack) ? this.node.render(stack, rx) : null;
2280
- }
2281
- };
2282
- var HideNode = class extends WrapperNode {
2283
- render(stack, rx) {
2284
- return this.val.eval(stack) ? null : this.node.render(stack, rx);
2285
- }
2286
- };
2287
- var PushViewNameNode = class extends WrapperNode {
2288
- render(stack, rx) {
2289
- return this.node.render(stack.pushViewName(this.val.eval(stack)), rx);
2290
- }
2291
- };
2292
- var SlotNode = class extends WrapperNode {
2293
- // Marker instead of `instanceof`: newMacroNode receives nodes that may come
2294
- // from a different copy of this module (CLI tools import src/ while the
2295
- // module under render imports the dist bundle), and cross-copy instanceof
2296
- // is always false.
2297
- isSlotNode = true;
2298
- optimize() {
2299
- this.node.optimize();
2300
- }
2301
- };
2302
- var ScopeNode = class extends WrapperNode {
2303
- render(stack, rx) {
2304
- const binds = this.val.evalAsHandler(stack)?.call(stack.it) ?? {};
2305
- const dom = this.node.render(stack.enter(stack.it, binds, false), rx);
2306
- return rx.renderScopeMeta(this.nodeId, dom);
2307
- }
2308
- toPathStep(_ctx) {
2309
- return new ScopeBindStep(this.val);
2310
- }
2311
- wrapNode(node) {
2312
- this.node = node;
2313
- this.node.setDataAttr("data-nid", this.nodeId);
2314
- }
2315
- static register = true;
2316
- };
2317
- var EachNode = class extends WrapperNode {
2318
- constructor(nodeId, val) {
2319
- super(nodeId, val);
2320
- this.iterInfo = new IterInfo(val, null, null, null);
2321
- }
2322
- render(stack, rx) {
2323
- return rx.renderEachWhen(stack, this.iterInfo, this.node, this.nodeId);
2324
- }
2325
- toPathStep(ctx) {
2326
- return ctx.hasKey ? new EachBindStep(this.iterInfo, ctx.key) : null;
2327
- }
2328
- static register = true;
2329
- };
2330
- var IterInfo = class {
2331
- constructor(val, whenVal, loopWithVal, enrichWithVal) {
2332
- this.val = val;
2333
- this.whenVal = whenVal;
2334
- this.loopWithVal = loopWithVal;
2335
- this.enrichWithVal = enrichWithVal;
2336
- }
2337
- eval(stack) {
2338
- const seq = this.val.eval(stack) ?? [];
2339
- const filter = this.whenVal?.evalAsHandler(stack) ?? filterAlwaysTrue;
2340
- const loopWith = this.loopWithVal?.evalAsHandler(stack) ?? nullLoopWith;
2341
- const enricher = this.enrichWithVal?.evalAsHandler(stack) ?? null;
2342
- return { seq, filter, loopWith, enricher };
2343
- }
2344
- // Rebuild the per-item binds for `key`, mirroring renderEachWhen: seed
2345
- // { key, value }, then run @enrich-with (with @loop-with's iterData) if any.
2346
- enrichBinds(stack, key) {
2347
- const { seq, filter, loopWith, enricher } = this.eval(stack);
2348
- const value = seqGet(seq, key, null);
2349
- const binds = { key, value };
2350
- if (enricher) {
2351
- const { iterData } = unpackLoopResult(
2352
- loopWith.call(stack.it, seq, makeLoopCtx(stack, filter)),
2353
- seq
2354
- );
2355
- callEnricher(enricher, stack.it, binds, key, value, iterData);
2356
- }
2357
- return binds;
2358
- }
2359
- };
2360
- function xOp(consumed = [], { wrappable = false, wrapper = null, ignoresChildren = false } = {}) {
2361
- return { consumed: new Set(consumed), wrappable, wrapper, ignoresChildren };
2362
- }
2363
- var X_OPS = {
2364
- slot: xOp(),
2365
- text: xOp([], { wrappable: true, ignoresChildren: true }),
2366
- render: xOp(["as"], { wrappable: true, ignoresChildren: true }),
2367
- "render-it": xOp(["as"], { wrappable: true, ignoresChildren: true }),
2368
- // `@when`/`@loop-with` are consumed here (handled in parseRenderEach) so
2369
- // `processXExtras` does not flag them as unknown attrs. TEMPORARY: the bare
2370
- // `when`/`loop-with` spellings are deprecated in favor of the `@` form — see
2371
- // maybeDeprecateBareXDirective (added 2026-07-08).
2372
- "render-each": xOp(["as", "when", "loop-with", "@when", "@loop-with"], {
2373
- wrappable: true,
2374
- ignoresChildren: true
2375
- }),
2376
- show: xOp([], { wrapper: ShowNode }),
2377
- hide: xOp([], { wrapper: HideNode })
2378
- };
2379
- var WRAPPER_NODES = {
2380
- show: ShowNode,
2381
- hide: HideNode,
2382
- each: EachNode,
2383
- // internal wrapper produced by a loop-less @enrich-with (no surface @scope directive)
2384
- scope: ScopeNode,
2385
- "push-view": PushViewNameNode
2386
- };
2387
- var ParseContext = class _ParseContext {
2388
- constructor(document2, Text, Comment, nodes, events, macroNodes, frame, parent) {
2389
- this.nodes = nodes ?? [];
2390
- this.events = events ?? [];
2391
- this.macroNodes = macroNodes ?? [];
2392
- this.parent = parent ?? null;
2393
- this.frame = frame ?? {};
2394
- this.document = document2 ?? globalThis.document;
2395
- this.Text = Text ?? globalThis.Text;
2396
- this.Comment = Comment ?? globalThis.Comment;
2397
- this.cacheConstNodes = true;
2398
- this.currentTag = null;
2399
- }
2400
- isInsideMacro(name) {
2401
- return this.frame.macroName === name || this.parent?.isInsideMacro(name);
2402
- }
2403
- enterMacro(macroName, macroVars, macroSlots) {
2404
- const { document: document2, Text, Comment, nodes, events, macroNodes } = this;
2405
- const frame = { macroName, macroVars, macroSlots };
2406
- return new _ParseContext(document2, Text, Comment, nodes, events, macroNodes, frame, this);
2407
- }
2408
- parseHTML(html2) {
2409
- const t = this.document.createElement("template");
2410
- t.innerHTML = html2;
2411
- return t.content.childNodes;
2412
- }
2413
- addNodeIf(Class, val, extra) {
2414
- if (val !== null) {
2415
- const nodeId = this.nodes.length;
2416
- const node = new Class(nodeId, val, extra);
2417
- this.nodes.push(node);
2418
- return node;
2419
- }
2420
- return null;
2421
- }
2422
- // For nodes that carry no value (e.g. RenderItNode, which reads stack.it at
2423
- // render time); addNodeIf treats a null val as a failed parse.
2424
- addNode(Class, extra) {
2425
- const nodeId = this.nodes.length;
2426
- const node = new Class(nodeId, null, extra);
2427
- this.nodes.push(node);
2428
- return node;
2429
- }
2430
- registerEvents() {
2431
- const id = this.events.length;
2432
- const events = new NodeEvents(id);
2433
- this.events.push(events);
2434
- return events;
2435
- }
2436
- newMacroNode(macroName, mAttrs, childs) {
2437
- const anySlot = [];
2438
- const slots = { _: new FragmentNode(anySlot) };
2439
- for (const child of childs)
2440
- if (child.isSlotNode) slots[child.val.val] = child.node;
2441
- else if (!child.isWhiteSpace()) anySlot.push(child);
2442
- const node = new MacroNode(macroName, mAttrs, slots, this);
2443
- this.macroNodes.push(node);
2444
- return node;
2445
- }
2446
- compile(scope) {
2447
- for (let i = 0; i < this.macroNodes.length; i++) this.macroNodes[i].compile(scope);
2448
- }
2449
- *genEventNames() {
2450
- for (const event of this.events) yield* event.genEventNames();
2451
- }
2452
- getEventForId(id) {
2453
- return this.events[id] ?? null;
2454
- }
2455
- getNodeForId(id) {
2456
- return this.nodes[id] ?? null;
2457
- }
2458
- onAttributes(_attrs, _wrapperAttrs, _textChild, _isMacroCall, _tag) {
2459
- }
2460
- onParseIssue(kind, info) {
2461
- console.warn(`tutuca parse issue [${kind}]`, info);
2462
- }
2463
- // Lint-only channel for deprecation nudges on still-valid syntax; the base
2464
- // (runtime) context ignores them so live apps stay quiet. See LintParseContext.
2465
- onDeprecatedSyntax(_kind, _info) {
2466
- }
2467
- };
2468
- var _htmlBlockTags = "ADDRESS,ARTICLE,ASIDE,BLOCKQUOTE,CAPTION,COL,COLGROUP,DETAILS,DIALOG,DIV,DD,DL,DT,FIELDSET,FIGCAPTION,FIGURE,FOOTER,FORM,H1,H2,H3,H4,H5,H6,HEADER,HGROUP,HR,LEGEND,LI,MAIN,MENU,NAV,OL,P,PRE,SECTION,SUMMARY,TABLE,TBODY,TD,TFOOT,TH,THEAD,TR,UL";
2469
- var HTML_BLOCK_TAGS = new Set(_htmlBlockTags.split(","));
2470
- var isBlockDomNode = (n) => {
2471
- const node = n instanceof FragmentNode ? n.childs[0] : n;
2472
- return node instanceof DomNode && HTML_BLOCK_TAGS.has(node.tagName);
2473
- };
2474
- var isEmptyText = (c) => c instanceof TextNode && c.val === "";
2475
- var isIgnorableXChild = (c) => c instanceof CommentNode || (c.isWhiteSpace?.() ?? false);
2476
- var hasMeaningfulChilds = (childs) => childs.some((c) => !isIgnorableXChild(c));
2477
- function trimEdgeWhite(node) {
2478
- if (!node.isWhiteSpace?.()) return false;
2479
- node.condenseWhiteSpace();
2480
- return true;
2481
- }
2482
- function condenseChildsWhites(childs) {
2483
- if (childs.length === 0) return childs;
2484
- const last = childs.length - 1;
2485
- let emptied = trimEdgeWhite(childs[0]);
2486
- if (last > 0 && trimEdgeWhite(childs[last])) emptied = true;
2487
- for (let i = 1; i < last; i++) {
2488
- const cur = childs[i];
2489
- if (!(cur.isWhiteSpace?.() && cur.hasNewLine())) continue;
2490
- const bothBlock = isBlockDomNode(childs[i - 1]) && isBlockDomNode(childs[i + 1]);
2491
- cur.condenseWhiteSpace(bothBlock ? "" : " ");
2492
- if (bothBlock) emptied = true;
2493
- }
2494
- return emptied ? childs.filter((c) => !isEmptyText(c)) : childs;
2495
- }
2496
- var View = class {
2497
- constructor(name, rawView = "No View Defined", style = "", anode = null, ctx = null) {
2498
- this.name = name;
2499
- this.anode = anode;
2500
- this.style = style;
2501
- this.ctx = ctx;
2502
- this.rawView = rawView;
2503
- }
2504
- compile(ctx, scope, cid) {
2505
- this.ctx = ctx;
2506
- this.anode = ANode.parse(this.rawView, ctx);
2507
- this.anode.setDataAttr("data-cid", cid);
2508
- this.anode.setDataAttr("data-vid", this.name);
2509
- this.ctx.compile(scope);
2510
- if (ctx.cacheConstNodes) this.anode = optimizeNode(this.anode);
2511
- }
2512
- render(stack, rx) {
2513
- if (this.anode === null) {
2514
- throw new Error(
2515
- `tutuca: view "${this.name}" was rendered before it was compiled — its component is not registered in this app/scope. Source: ${String(this.rawView).slice(0, 80).replace(/\s+/g, " ")}…`
2516
- );
2517
- }
2518
- return this.anode.render(stack, rx);
2519
- }
2520
- };
2521
- var NodeEvents = class {
2522
- constructor(id) {
2523
- this.id = id;
2524
- this.handlers = [];
2525
- }
2526
- add(name, handlerCall, modifiers) {
2527
- this.handlers.push(new NodeEvent(name, handlerCall, modifiers));
2528
- }
2529
- *genEventNames() {
2530
- for (const handler of this.handlers) yield handler.name;
2531
- }
2532
- getHandlersFor(eventName) {
2533
- let r = null;
2534
- for (const handler of this.handlers)
2535
- if (handler.handlesEventName(eventName)) {
2536
- r ??= [];
2537
- r.push(handler);
2538
- }
2539
- return r;
2540
- }
2541
- };
2542
- var NodeEvent = class {
2543
- constructor(name, handlerCall, modifiers) {
2544
- this.name = name;
2545
- this.handlerCall = handlerCall;
2546
- this.modifierWrapper = compileModifiers(name, modifiers);
2547
- this.modifiers = modifiers;
2548
- }
2549
- handlesEventName(name) {
2550
- return this.name === name;
2551
- }
2552
- getHandlerAndArgs(stack, event) {
2553
- const r = this.handlerCall.getHandlerAndArgs(stack, event);
2554
- r[0] = this.modifierWrapper(r[0], event);
2555
- return r;
2556
- }
2557
- };
2558
- var fwdIfCtxPred = (pred) => (w) => (that, f, args, ctx) => pred(ctx) ? w(that, f, args, ctx) : that;
2559
- var fwdIfKey = (keyName) => fwdIfCtxPred((ctx) => ctx.e.key === keyName);
2560
- var fwdCtrl = fwdIfCtxPred(({ e }) => isMac && e.metaKey || e.ctrlKey);
2561
- var fwdMeta = fwdIfCtxPred(({ e }) => e.metaKey);
2562
- var fwdAlt = fwdIfCtxPred(({ e }) => e.altKey);
2563
- var MOD_WRAPPERS_FOR_ANY_EVENT = {
2564
- ctrl: fwdCtrl,
2565
- cmd: fwdCtrl,
2566
- meta: fwdMeta,
2567
- alt: fwdAlt
2568
- };
2569
- var MOD_WRAPPERS_BY_EVENT = {
2570
- keydown: {
2571
- send: fwdIfKey("Enter"),
2572
- cancel: fwdIfKey("Escape")
2573
- }
2574
- };
2575
- var MOD_EFFECTS = {
2576
- prevent: (e) => e.preventDefault?.(),
2577
- stop: (e) => e.stopPropagation?.()
2578
- };
2579
- var NO_WRAPPERS = {};
2580
- var identityModifierWrapper = (f, _ctx) => f;
2581
- function compileModifiers(eventName, names) {
2582
- if (names.length === 0) return identityModifierWrapper;
2583
- const wrappers = MOD_WRAPPERS_BY_EVENT[eventName] ?? NO_WRAPPERS;
2584
- const effects = [];
2585
- for (const name of names) {
2586
- const effect = MOD_EFFECTS[name];
2587
- if (effect !== void 0) effects.push(effect);
2588
- }
2589
- let w = effects.length === 0 ? (that, f, args, _ctx) => f.apply(that, args) : (that, f, args, ctx) => {
2590
- for (const effect of effects) effect(ctx.e);
2591
- return f.apply(that, args);
2592
- };
2593
- for (const name of names) {
2594
- const wrapper = wrappers[name] ?? MOD_WRAPPERS_FOR_ANY_EVENT[name];
2595
- if (wrapper !== void 0) w = wrapper(w);
2596
- }
2597
- return (f, ctx) => function(...args) {
2598
- return w(this, f, args, ctx);
2599
- };
2600
- }
2601
-
2602
- // src/components.js
2603
- var Components = class {
2604
- constructor() {
2605
- this.getComponentSymbol = /* @__PURE__ */ Symbol("getComponent");
2606
- this.byId = /* @__PURE__ */ new Map();
2607
- }
2608
- registerComponent(comp) {
2609
- comp.Class.prototype[this.getComponentSymbol] = () => comp;
2610
- this.byId.set(comp.id, comp);
2611
- }
2612
- getComponentForId(id) {
2613
- return this.byId.get(id) ?? null;
2614
- }
2615
- getCompFor(v) {
2616
- return v?.[this.getComponentSymbol]?.() ?? null;
2617
- }
2618
- getHandlerFor(v, name, key) {
2619
- return this.getCompFor(v)?.[key][name] ?? null;
2620
- }
2621
- // The `lex` leg of an intent's route: the handlers registered on the scope chain of
2622
- // the component that raised it, innermost first. A list, not a first match — see
2623
- // ComponentStack.lookupIntentChain.
2624
- getIntentChainFor(v, name) {
2625
- return this.getCompFor(v)?.scope.lookupIntentChain(name) ?? [];
2626
- }
2627
- compileStyles() {
2628
- const styles = [];
2629
- for (const comp of this.byId.values()) styles.push(comp.compileStyle());
2630
- return styles.join("\n");
2631
- }
2632
- };
2633
- var ComponentStack = class _ComponentStack {
2634
- constructor(comps = new Components(), parent = null) {
2635
- this.comps = comps;
2636
- this.parent = parent;
2637
- this.byName = {};
2638
- this.intentsByName = {};
2639
- this.macros = {};
2640
- }
2641
- enter() {
2642
- return new _ComponentStack(this.comps, this);
2643
- }
2644
- registerComponents(comps, opts) {
2645
- const { aliases = {} } = opts ?? {};
2646
- for (let i = 0; i < comps.length; i++) {
2647
- const comp = comps[i];
2648
- comp.scope = this.enter();
2649
- comp.Class.scope = comp.scope;
2650
- this.comps.registerComponent(comp);
2651
- this.byName[comp.name] = comp;
2652
- }
2653
- for (const alias in aliases) {
2654
- const comp = this.byName[aliases[alias]];
2655
- console.assert(this.byName[alias] === void 0, "alias overrides component", alias);
2656
- if (comp !== void 0) this.byName[alias] = comp;
2657
- else console.warn("alias", alias, "to inexistent component", aliases[alias]);
2658
- }
2659
- }
2660
- registerMacros(macros) {
2661
- for (const key in macros) {
2662
- const lower = key.toLowerCase();
2663
- console.assert(this.macros[lower] === void 0, "macro key collision", lower);
2664
- this.macros[lower] = macros[key];
2665
- }
2666
- }
2667
- getCompFor(v) {
2668
- return this.comps.getCompFor(v);
2669
- }
2670
- // Handlers that answer an intent's `lex` leg. A name maps to a LIST, because the leg
2671
- // walks: a handler that returns PASS declines and hands the intent to the next one.
2672
- // A bare function is accepted as a one-element list.
2673
- registerIntentHandlers(handlers) {
2674
- for (const name in handlers) {
2675
- const fns = Array.isArray(handlers[name]) ? handlers[name] : [handlers[name]];
2676
- this.intentsByName[name] = fns.map((fn) => new IntentHandler(name, fn));
2677
- }
2678
- }
2679
- // Innermost scope first, then outward. Concatenated rather than first-match, so a
2680
- // declining handler can hand the intent to one further up.
2681
- lookupIntentChain(name) {
2682
- const here = this.intentsByName[name] ?? [];
2683
- const up = this.parent?.lookupIntentChain(name) ?? [];
2684
- return up.length === 0 ? here : here.concat(up);
2685
- }
2686
- lookupComponent(name) {
2687
- return this.byName[name] ?? this.parent?.lookupComponent(name) ?? null;
2688
- }
2689
- lookupMacro(name) {
2690
- return this.macros[name] ?? this.parent?.lookupMacro(name) ?? null;
2691
- }
2692
- };
2693
- var ProvideInfo = class {
2694
- constructor(name, val, symbol) {
2695
- this.name = name;
2696
- this.val = val;
2697
- this.symbol = symbol;
2698
- }
2699
- };
2700
- var LookupInfo = class {
2701
- constructor(name, compName, provideName, val) {
2702
- this.name = name;
2703
- this.compName = compName;
2704
- this.provideName = provideName;
2705
- this.val = val;
2706
- this._sym = void 0;
2707
- }
2708
- getProducerSymbol(stack) {
2709
- if (this._sym === void 0)
2710
- this._sym = stack.lookupType(this.compName)?.provide?.[this.provideName]?.symbol ?? null;
2711
- return this._sym;
2712
- }
2713
- };
2714
- var isString = (v) => typeof v === "string";
2715
- var _rawSpecKeys = "name view style commonStyle globalStyle receive intent alter views provide lookup fields methods statics";
2716
- var KNOWN_SPEC_KEYS = new Set(_rawSpecKeys.split(" "));
2717
- var _compId = 0;
2718
- var Component = class {
2719
- constructor(Class, o) {
2720
- this.id = _compId++;
2721
- this.name = o.name ?? "UnkComp";
2722
- this.Class = Class;
2723
- this.views = { main: new View("main", o.view, o.style) };
2724
- this.commonStyle = o.commonStyle ?? "";
2725
- this.globalStyle = o.globalStyle ?? "";
2726
- this.receive = o.receive ?? {};
2727
- this.intent = o.intent ?? {};
2728
- this.alter = o.alter ?? {};
2729
- for (const name in o.views ?? {}) {
2730
- const v = o.views[name];
2731
- const { view, style } = isString(v) ? { view: v } : v;
2732
- this.views[name] = new View(name, view, style);
2733
- }
2734
- this._rawProvide = o.provide ?? {};
2735
- this._rawLookup = o.lookup ?? {};
2736
- this.provide = {};
2737
- this.lookup = {};
2738
- this.scope = null;
2739
- this.spec = o;
2740
- this.extra = {};
2741
- for (const key of Object.keys(o)) if (!KNOWN_SPEC_KEYS.has(key)) this.extra[key] = o[key];
2742
- }
2743
- compile(ParseContext2) {
2744
- for (const name in this.views)
2745
- this.views[name].compile(new ParseContext2(), this.scope, this.id);
2746
- const ctx = this.views.main.ctx;
2747
- for (const key in this._rawProvide) {
2748
- const val = parseProvide(this._rawProvide[key], ctx);
2749
- if (val) this.provide[key] = new ProvideInfo(key, val, Symbol(key));
2750
- }
2751
- for (const key in this._rawLookup) {
2752
- const linfo = this._rawLookup[key];
2753
- const forStr = isString(linfo) ? linfo : isString(linfo?.for) ? linfo.for : null;
2754
- const [compName, provideName] = forStr === null ? [] : forStr.split(".");
2755
- if (!isString(compName) || !isString(provideName)) continue;
2756
- const defStr = isString(linfo?.default) ? linfo.default : null;
2757
- const val = defStr === null ? null : parseField(defStr, ctx);
2758
- this.lookup[key] = new LookupInfo(key, compName, provideName, val);
2759
- }
2760
- for (const key in this.lookup)
2761
- if (this.provide[key] !== void 0)
2762
- console.warn("name declared in both provide and lookup", this.name, key);
2763
- }
2764
- make(args, opts) {
2765
- return this.Class.make(args, opts ?? { scope: this.scope });
2766
- }
2767
- getView(name) {
2768
- return this.views[name] ?? this.views.main;
2769
- }
2770
- getEventForId(id, name = "main") {
2771
- return this.getView(name).ctx.getEventForId(id);
2772
- }
2773
- getNodeForId(id, name = "main") {
2774
- return this.getView(name).ctx.getNodeForId(id);
2775
- }
2776
- compileStyle() {
2777
- const { id, commonStyle, globalStyle, views } = this;
2778
- const styles = commonStyle ? [`[data-cid="${id}"]{${commonStyle}}`] : [];
2779
- if (globalStyle !== "") styles.push(globalStyle);
2780
- for (const name in views) {
2781
- const { style } = views[name];
2782
- if (style !== "") styles.push(`[data-cid="${id}"][data-vid="${name}"]{${style}}`);
2783
- }
2784
- return styles.join("\n");
2785
- }
2786
- };
2787
-
2788
- // src/stack.js
2789
- var STOP = /* @__PURE__ */ Symbol("STOP");
2790
- var NEXT = /* @__PURE__ */ Symbol("NEXT");
2791
- function lookup(chain, name, dv = null) {
2792
- let n = chain;
2793
- while (n !== null) {
2794
- const r = n[0].lookup(name);
2795
- if (r === STOP) return dv;
2796
- if (r !== NEXT) return r;
2797
- n = n[1];
2798
- }
2799
- return dv;
2800
- }
2801
- var BindFrame = class {
2802
- constructor(it, binds, isFrame) {
2803
- this.it = it;
2804
- this.binds = binds;
2805
- this.isFrame = isFrame;
2806
- }
2807
- lookup(name) {
2808
- const v = this.binds[name];
2809
- return v === void 0 ? this.isFrame ? STOP : NEXT : v;
2810
- }
2811
- };
2812
- var ObjectFrame = class {
2813
- constructor(binds) {
2814
- this.binds = binds;
2815
- }
2816
- lookup(key) {
2817
- const v = this.binds[key];
2818
- return v === void 0 ? NEXT : v;
2819
- }
2820
- };
2821
- function computeViewsId(views) {
2822
- let s = "";
2823
- let n = views;
2824
- while (n !== null) {
2825
- s += n[0];
2826
- n = n[1];
2827
- }
2828
- return s === "main" ? "" : s;
2829
- }
2830
- var Stack = class _Stack {
2831
- constructor(comps, it, binds, dynBinds, views, viewsId, ctx = null) {
2832
- this.comps = comps;
2833
- this.it = it;
2834
- this.binds = binds;
2835
- this.dynBinds = dynBinds;
2836
- this.views = views;
2837
- this.viewsId = viewsId;
2838
- this.ctx = ctx;
2839
- }
2840
- // Evaluate every provide the entered component publishes and push them as one
2841
- // dynBinds frame (keyed by each provide's symbol). No-op when there are no provides.
2842
- _pushProvides() {
2843
- const provide = this.comps.getCompFor(this.it)?.provide;
2844
- if (provide == null) return this;
2845
- const dynObj = {};
2846
- let has = false;
2847
- for (const k in provide) {
2848
- dynObj[provide[k].symbol] = provide[k].val.eval(this);
2849
- has = true;
2850
- }
2851
- if (!has) return this;
2852
- const newDynBinds = [new ObjectFrame(dynObj), this.dynBinds];
2853
- const { comps, it, binds, views, viewsId, ctx } = this;
2854
- return new _Stack(comps, it, binds, newDynBinds, views, viewsId, ctx);
2855
- }
2856
- static root(comps, it, ctx) {
2857
- const binds = [new BindFrame(it, {}, true), null];
2858
- const dynBinds = [new ObjectFrame({}), null];
2859
- const views = ["main", null];
2860
- return new _Stack(comps, it, binds, dynBinds, views, "", ctx)._pushProvides();
2861
- }
2862
- enter(it, bindings = {}, isFrame = true) {
2863
- const { comps, binds, dynBinds, views, viewsId, ctx } = this;
2864
- const newBinds = [new BindFrame(it, bindings, isFrame), binds];
2865
- const stack = new _Stack(comps, it, newBinds, dynBinds, views, viewsId, ctx);
2866
- return isFrame ? stack._pushProvides() : stack;
2867
- }
2868
- pushViewName(name) {
2869
- const { comps, it, binds, dynBinds, views, ctx } = this;
2870
- const newViews = [name, views];
2871
- return new _Stack(comps, it, binds, dynBinds, newViews, computeViewsId(newViews), ctx);
2872
- }
2873
- _pushDynBindValuesToArray(arr, comp) {
2874
- for (const k in comp.provide) arr.push(this._lookupProvide(comp.provide[k]));
2875
- for (const k in comp.lookup) arr.push(this._lookupAlias(comp.lookup[k]));
2876
- }
2877
- _lookupProvide(p) {
2878
- return lookup(this.dynBinds, p.symbol) ?? p.val.eval(this) ?? null;
2879
- }
2880
- _lookupAlias(lk) {
2881
- const sym = lk.getProducerSymbol(this);
2882
- return (sym != null ? lookup(this.dynBinds, sym) : null) ?? lk.val?.eval(this) ?? null;
2883
- }
2884
- lookupDynamic(name) {
2885
- const comp = this.comps.getCompFor(this.it);
2886
- if (comp == null) return null;
2887
- const lk = comp.lookup[name];
2888
- if (lk !== void 0) return this._lookupAlias(lk);
2889
- const p = comp.provide[name];
2890
- return p !== void 0 ? this._lookupProvide(p) : null;
2891
- }
2892
- lookupBind(name) {
2893
- return lookup(this.binds, name);
2894
- }
2895
- lookupType(name) {
2896
- return this.comps.getCompFor(this.it).scope.lookupComponent(name);
2897
- }
2898
- lookupFieldRaw(name) {
2899
- return this.it[name] ?? null;
2900
- }
2901
- lookupMethod(name) {
2902
- const fn = this.it[name];
2903
- return fn instanceof Function ? fn.call(this.it) : null;
2904
- }
2905
- lookupName(name) {
2906
- return this.ctx.lookupName(name);
2907
- }
2908
- getHandlerFor(name, key) {
2909
- return this.comps.getHandlerFor(this.it, name, key);
2910
- }
2911
- lookupBestView(views, defaultViewName) {
2912
- let n = this.views;
2913
- while (n !== null) {
2914
- const view = views[n[0]];
2915
- if (view !== void 0) return view;
2916
- n = n[1];
2917
- }
2918
- return views[defaultViewName];
2919
- }
2920
- };
2921
-
2922
- // src/oo.js
2923
- var BAD_VALUE = /* @__PURE__ */ Symbol("BadValue");
2924
- var nullCoercer = (v) => v;
2925
- var isPlainObject2 = (v) => {
2926
- if (v === null || typeof v !== "object") return false;
2927
- const proto = Object.getPrototypeOf(v);
2928
- return proto === Object.prototype || proto === null;
2929
- };
2930
- function defaultToData(v) {
2931
- if (v instanceof Map) return [...v.entries()];
2932
- if (v instanceof Set) return [...v.values()];
2933
- return v;
2934
- }
2935
- var Field = class {
2936
- constructor(type, name, typeCheck, coercer, defaultValue = null) {
2937
- this.type = type;
2938
- this.name = name;
2939
- this.typeCheck = typeCheck;
2940
- this.coercer = coercer;
2941
- this.checks = [];
2942
- this.defaultValue = defaultValue;
2943
- }
2944
- toDataDef() {
2945
- return { type: this.type, defaultValue: defaultToData(this.defaultValue) };
2946
- }
2947
- getFirstFailingCheck(v) {
2948
- if (!this.typeCheck.isValid(v)) return this.typeCheck;
2949
- for (const check3 of this.checks) if (!check3.isValid(v)) return check3;
2950
- return null;
2951
- }
2952
- isValid(v) {
2953
- return this.getFirstFailingCheck(v) === null;
2954
- }
2955
- addCheck(check3) {
2956
- this.checks.push(check3);
2957
- return this;
2958
- }
2959
- coerceOr(v, defaultValue = null) {
2960
- if (this.isValid(v)) return v;
2961
- const v1 = this.coercer(v);
2962
- return this.isValid(v1) ? v1 : defaultValue;
2963
- }
2964
- coerceOrDefault(v) {
2965
- return this.coerceOr(v, this.defaultValue);
2966
- }
2967
- };
2968
- var Check = class {
2969
- isValid(_v) {
2970
- return true;
2971
- }
2972
- getMessage(_v) {
2973
- return "Invalid";
2974
- }
2975
- };
2976
- var CheckTypeAny = class extends Check {
2977
- };
2978
- var CHECK_TYPE_ANY = new CheckTypeAny();
2979
- var FnCheck = class extends Check {
2980
- constructor(isValidFn, getMessageFn) {
2981
- super();
2982
- this._isValid = isValidFn;
2983
- this._getMessage = getMessageFn;
2984
- }
2985
- isValid(v) {
2986
- return this._isValid(v);
2987
- }
2988
- getMessage(v) {
2989
- return this._getMessage(v);
2990
- }
2991
- };
2992
- var check = (fn, message) => new FnCheck(fn, () => message);
2993
- var CHECK_TYPE_INT = check(Number.isInteger, "Integer expected");
2994
- var CHECK_TYPE_FLOAT = check(Number.isFinite, "Float expected");
2995
- var CHECK_TYPE_BOOL = check((v) => typeof v === "boolean", "Boolean expected");
2996
- var CHECK_TYPE_STRING = check((v) => typeof v === "string", "String expected");
2997
- var CHECK_TYPE_LIST = check(Array.isArray, "Array expected");
2998
- var CHECK_TYPE_OBJECT = check(isPlainObject2, "Plain object expected");
2999
- var CHECK_TYPE_MAP = check((v) => v instanceof Map, "Map expected");
3000
- var CHECK_TYPE_SET = check((v) => v instanceof Set, "Set expected");
3001
- var FieldBool = class extends Field {
3002
- constructor(name, defaultValue = false) {
3003
- super("bool", name, CHECK_TYPE_BOOL, (v) => !!v, defaultValue);
3004
- }
3005
- };
3006
- var FieldAny = class extends Field {
3007
- constructor(name, defaultValue = null) {
3008
- super("any", name, CHECK_TYPE_ANY, nullCoercer, defaultValue);
3009
- }
3010
- toDataDef() {
3011
- return { type: getTypeName(this.defaultValue) ?? "any", defaultValue: this.defaultValue };
3012
- }
3013
- };
3014
- var FieldString = class extends Field {
3015
- constructor(name, defaultValue = "") {
3016
- super("text", name, CHECK_TYPE_STRING, (v) => v?.toString?.() ?? "", defaultValue);
3017
- }
3018
- };
3019
- var FieldInt = class extends Field {
3020
- constructor(name, defaultValue = 0) {
3021
- super(
3022
- "int",
3023
- name,
3024
- CHECK_TYPE_INT,
3025
- (v) => Number.isFinite(v) ? Math.trunc(v) : null,
3026
- defaultValue
3027
- );
3028
- }
3029
- };
3030
- var FieldFloat = class extends Field {
3031
- constructor(name, defaultValue = 0) {
3032
- super("float", name, CHECK_TYPE_FLOAT, (_) => null, defaultValue);
3033
- }
3034
- };
3035
- var getTypeName = (v) => v?.constructor?.getMetaClass?.()?.name;
3036
- var CheckTypeName = class {
3037
- constructor(typeName) {
3038
- this.typeName = typeName;
3039
- }
3040
- isValid(v) {
3041
- return getTypeName(v) === this.typeName;
3042
- }
3043
- getMessage(v) {
3044
- return `Expected "${this.typeName}", got "${getTypeName(v)}"`;
3045
- }
3046
- };
3047
- var FieldComp = class extends Field {
3048
- constructor(type, name, args) {
3049
- super(type, name, new CheckTypeName(type), nullCoercer, null);
3050
- this.args = args;
3051
- }
3052
- toDataDef() {
3053
- return { component: this.type, args: this.args };
3054
- }
3055
- };
3056
- var FieldList = class extends Field {
3057
- constructor(name, defaultValue = []) {
3058
- super("list", name, CHECK_TYPE_LIST, (v) => Array.isArray(v) ? [...v] : null, defaultValue);
3059
- }
3060
- };
3061
- var FieldObject = class extends Field {
3062
- constructor(name, defaultValue = {}) {
3063
- super(
3064
- "object",
3065
- name,
3066
- CHECK_TYPE_OBJECT,
3067
- (v) => isPlainObject2(v) ? { ...v } : null,
3068
- defaultValue
3069
- );
3070
- }
3071
- };
3072
- var FieldMap = class extends Field {
3073
- constructor(name, defaultValue = /* @__PURE__ */ new Map()) {
3074
- super(
3075
- "map",
3076
- name,
3077
- CHECK_TYPE_MAP,
3078
- (v) => {
3079
- if (v instanceof Map) return new Map(v);
3080
- if (Array.isArray(v) || isPlainObject2(v))
3081
- return new Map(Array.isArray(v) ? v : Object.entries(v));
3082
- return null;
3083
- },
3084
- defaultValue
3085
- );
3086
- }
3087
- };
3088
- var FieldSet = class extends Field {
3089
- constructor(name, defaultValue = /* @__PURE__ */ new Set()) {
3090
- super(
3091
- "set",
3092
- name,
3093
- CHECK_TYPE_SET,
3094
- (v) => v instanceof Set || Array.isArray(v) ? new Set(v) : null,
3095
- defaultValue
3096
- );
3097
- }
3098
- };
3099
- function mkCompField(field, scope, args) {
3100
- const Comp = scope?.lookupComponent(field.type) ?? null;
3101
- if (Comp === null)
3102
- console.warn(
3103
- scope ? `component field "${field.name}": component "${field.type}" not found in scope` : `component field "${field.name}": cannot resolve component "${field.type}" — built without a registered scope (use ${field.type}.make({}) as the default, or build via a registered component)`
3104
- );
3105
- return Comp?.make({ ...field.args, ...args }, { scope }) ?? null;
3106
- }
3107
- var ClassBuilder = class {
3108
- constructor(name) {
3109
- this.name = name;
3110
- this.fields = {};
3111
- this.compFields = /* @__PURE__ */ new Set();
3112
- this._methods = {};
3113
- this._statics = {};
3114
- }
3115
- build() {
3116
- const { name, fields, compFields, _methods } = this;
3117
- const defaults = Object.fromEntries(
3118
- Object.entries(fields).map(([fieldName, field]) => [fieldName, field.defaultValue])
3119
- );
3120
- const Class = {
3121
- [name]: class {
3122
- constructor(values = {}) {
3123
- Object.assign(this, defaults, values);
3124
- }
3125
- }
3126
- }[name];
3127
- Class[immer_exports.immerable] = true;
3128
- Object.assign(Class.prototype, _methods);
3129
- const metaClass = { fields, name, methods: _methods };
3130
- Object.assign(
3131
- Class,
3132
- {
3133
- getMetaClass: () => metaClass,
3134
- make(inArgs = {}, opts = {}) {
3135
- const args = {};
3136
- const scope = opts.scope ?? this.scope;
3137
- for (const key in inArgs) {
3138
- const field = fields[key];
3139
- if (compFields.has(key)) args[key] = mkCompField(field, scope, inArgs[key]);
3140
- else if (field === void 0)
3141
- console.warn("extra argument to constructor:", name, key, inArgs);
3142
- else args[key] = field.coerceOrDefault(inArgs[key]);
3143
- }
3144
- for (const key of compFields)
3145
- if (args[key] === void 0) args[key] = mkCompField(fields[key], scope, inArgs[key]);
3146
- return (0, immer_exports.freeze)(new this(args), true);
3147
- }
3148
- },
3149
- this._statics
3150
- );
3151
- return Class;
3152
- }
3153
- methods(proto) {
3154
- for (const k in proto) this._methods[k] = proto[k];
3155
- }
3156
- statics(proto) {
3157
- for (const k in proto) this._statics[k] = proto[k];
3158
- }
3159
- addField(name, dval, FieldCls) {
3160
- const field = new FieldCls(name, dval);
3161
- this.fields[name] = field;
3162
- return field;
3163
- }
3164
- addCompField(name, type, args) {
3165
- const field = new FieldComp(type, name, args);
3166
- this.compFields.add(name);
3167
- this.fields[name] = field;
3168
- return field;
3169
- }
3170
- };
3171
- var FIELD_CLASS = /* @__PURE__ */ Symbol.for("tutuca.fieldClass");
3172
- var fieldsByTypeName = {
3173
- text: FieldString,
3174
- int: FieldInt,
3175
- float: FieldFloat,
3176
- bool: FieldBool,
3177
- list: FieldList,
3178
- object: FieldObject,
3179
- map: FieldMap,
3180
- set: FieldSet,
3181
- any: FieldAny
3182
- };
3183
- function fieldFromDescriptor(name, value) {
3184
- const FieldCls = fieldsByTypeName[value.type] ?? FieldAny;
3185
- const probe = new FieldCls(name);
3186
- return [FieldCls, probe.coerceOr(value.defaultValue, probe.defaultValue)];
3187
- }
3188
- function classFromData(name, { fields = {}, methods, statics }) {
3189
- const b = new ClassBuilder(name);
3190
- for (const field in fields) {
3191
- const value = fields[field];
3192
- const type = typeof value;
3193
- if (type === "string") b.addField(field, value, FieldString);
3194
- else if (type === "number")
3195
- b.addField(field, value, Number.isInteger(value) ? FieldInt : FieldFloat);
3196
- else if (type === "boolean") b.addField(field, value, FieldBool);
3197
- else if (Array.isArray(value)) b.addField(field, [...value], FieldList);
3198
- else if (value instanceof Set) b.addField(field, new Set(value), FieldSet);
3199
- else if (value instanceof Map) b.addField(field, new Map(value), FieldMap);
3200
- else if (value?.type && Object.hasOwn(value, "defaultValue")) {
3201
- const [FieldCls, dval] = fieldFromDescriptor(field, value);
3202
- b.addField(field, dval, FieldCls);
3203
- } else if (value?.component && value?.args !== void 0)
3204
- b.addCompField(field, value.component, value.args);
3205
- else if (isPlainObject2(value)) b.addField(field, { ...value }, FieldObject);
3206
- else {
3207
- const FieldCls = value?.[FIELD_CLASS] ?? FieldAny;
3208
- b.addField(field, value, FieldCls);
3209
- }
3210
- }
3211
- if (methods) b.methods(methods);
3212
- if (statics) b.statics(statics);
3213
- return b.build();
3214
- }
3215
- function validateDraftFields(current, draft) {
3216
- const meta = current?.constructor?.getMetaClass?.();
3217
- if (!meta) return;
3218
- for (const [name, field] of Object.entries(meta.fields)) {
3219
- const value = draft[name];
3220
- if (field.isValid(value)) continue;
3221
- const coerced = field.coerceOr(value, BAD_VALUE);
3222
- if (coerced !== BAD_VALUE) draft[name] = coerced;
3223
- else {
3224
- console.warn(`invalid value for ${meta.name}.${name}`, value);
3225
- draft[name] = current[name];
3226
- }
3227
- }
3228
- }
3229
- Component.fromSpec = (opts) => new Component(classFromData(opts.name, opts), opts);
3230
- var component = (opts) => Component.fromSpec(opts);
3231
-
3232
- // src/transactor.js
3233
- var State = class {
3234
- constructor(val) {
3235
- this.val = val;
3236
- this.changeSubs = [];
3237
- }
3238
- onChange(cb) {
3239
- this.changeSubs.push(cb);
3240
- }
3241
- set(val, info) {
3242
- const old = this.val;
3243
- this.val = val;
3244
- for (const sub of this.changeSubs) sub({ val, old, info, timestamp: Date.now() });
3245
- }
3246
- update(fn, info) {
3247
- return this.set(fn(this.val), info);
3248
- }
3249
- };
3250
- var Transactor = class {
3251
- constructor(comps, rootValue) {
3252
- this.comps = comps;
3253
- this.transactions = [];
3254
- this.state = new State(rootValue);
3255
- this.onTransactionPushed = () => {
3256
- };
3257
- this._observers = [];
3258
- this._inflight = /* @__PURE__ */ new Set();
3259
- }
3260
- pushTransaction(t) {
3261
- this.transactions.push(t);
3262
- this.onTransactionPushed(t);
3263
- }
3264
- // Subscribe to a normalized record for every handler invocation. `kind` is the
3265
- // handler bucket (`receive` / `intent`), plus `answer` — an observation-only kind for
3266
- // a message the runtime wrote to answer an intent. The BUCKET does not know about
3267
- // that distinction, and must not: a handler cannot tell an answer from a message its
3268
- // parent sent. Returns an unsubscribe function. Records
3269
- // carry: kind, name, args, path, pathKeys, targetPath, handler, handlerName,
3270
- // matched, before, after, parent, timestamp. Purely observational.
3271
- observe(cb) {
3272
- this._observers.push(cb);
3273
- return () => {
3274
- const i = this._observers.indexOf(cb);
3275
- if (i !== -1) this._observers.splice(i, 1);
3276
- };
3277
- }
3278
- _emit(record) {
3279
- for (const cb of this._observers) cb(record);
3280
- }
3281
- // Build and dispatch the observer record for a settled transaction. The
3282
- // resolved handler (`_resolvedHandler`/`_matched`) and per-leaf before/after
3283
- // (`_before`/`_after`) were captured while the handler ran (see callHandler /
3284
- // updateRootValue). No-op when nobody is observing.
3285
- _emitTransaction(transaction, root) {
3286
- if (this._observers.length === 0) return;
3287
- if (transaction._resolvedHandler === void 0) return;
3288
- const path = transaction.getTransactionPath().pinKeys(root);
3289
- this._emit({
3290
- kind: transaction.observeKind,
3291
- name: transaction.observeName,
3292
- args: transaction.args ?? null,
3293
- path,
3294
- pathKeys: path.toKeys(),
3295
- targetPath: transaction.targetPath ?? transaction.path,
3296
- handler: transaction._resolvedHandler ?? null,
3297
- handlerName: transaction._resolvedHandler?.name || null,
3298
- matched: transaction._matched ?? null,
3299
- before: transaction._before,
3300
- after: transaction._after,
3301
- parent: transaction.parentTransaction,
3302
- timestamp: Date.now()
3303
- });
3304
- }
3305
- // Make `child` a tracked unit of `parent`'s subtree: the parent's completion stays open
3306
- // until the child's *whole* subtree settles. Tracking happens at dispatch time — during
3307
- // the parent's handler or afterTransaction — while the parent's self-unit is still held,
3308
- // so the parent counter can't reach zero before the child is registered. Returns `child`.
3309
- _link(child, parent) {
3310
- if (parent) {
3311
- const release = parent.completion.track();
3312
- child.completion.whenSubtreeSettled().then(release);
3313
- }
3314
- return child;
3315
- }
3316
- pushSend(path, name, args = [], opts = {}, parent = null) {
3317
- const t = new SendEvent(path, this, name, args, parent, opts);
3318
- this.pushTransaction(t);
3319
- return this._link(t, parent);
3320
- }
3321
- // Raise an intent: a job the sender did not address, walked along a route until
3322
- // something answers. `opts.route` is a list of legs (see DEFAULT_ROUTE) and
3323
- // `opts.livePath` opts out of pinning the answer's destination keys.
3324
- pushIntent(path, name, args = [], opts = {}, parent = null) {
3325
- const release = parent ? parent.completion.track() : null;
3326
- const walk = new IntentWalk(this, path, name, args, opts, parent, release);
3327
- walk.advance();
3328
- return walk;
3329
- }
3330
- // Drain queued transactions and await in-flight requests until quiescent. Each
3331
- // awaited request enqueues a ResponseEvent, which may dispatch more work, so we
3332
- // loop. `maxTurns` backstops a pathological non-terminating cascade.
3333
- async settle(maxTurns = 1e4) {
3334
- while ((this.hasPendingTransactions || this._inflight.size) && maxTurns-- > 0) {
3335
- while (this.hasPendingTransactions) this.transactNext();
3336
- if (this._inflight.size) await Promise.allSettled([...this._inflight]);
3337
- }
3338
- }
3339
- get hasPendingTransactions() {
3340
- return this.transactions.length > 0;
3341
- }
3342
- transactNext() {
3343
- if (this.hasPendingTransactions) this.transact(this.transactions.shift());
3344
- }
3345
- transact(transaction) {
3346
- try {
3347
- const curState = this.state.val;
3348
- const newState = transaction.run(curState, this.comps);
3349
- if (newState !== void 0) {
3350
- this.state.set(newState, { transaction });
3351
- transaction.afterTransaction();
3352
- this._emitTransaction(transaction, curState);
3353
- } else console.warn("undefined new state", { curState, transaction });
3354
- } finally {
3355
- transaction.ensureWalkAdvanced?.();
3356
- transaction._completion?.ensureSelfSettled();
3357
- transaction._completion?.releaseSelf();
3358
- }
3359
- }
3360
- transactInputNow(path, event, eventHandler, dragInfo) {
3361
- this.transact(new InputEvent(path, event, eventHandler, this, dragInfo));
3362
- }
3363
- };
3364
- function nullHandler() {
3365
- return this;
3366
- }
3367
- var Transaction = class {
3368
- constructor(path, transactor, parentTransaction = null) {
3369
- this.path = path;
3370
- this.transactor = transactor;
3371
- this.parentTransaction = parentTransaction;
3372
- this._completion = null;
3373
- }
3374
- // Lazily created (like the rest of the per-transaction state): a leaf event that
3375
- // nobody tracks or awaits never allocates one. See `class Completion`.
3376
- get completion() {
3377
- this._completion ??= new Completion();
3378
- return this._completion;
3379
- }
3380
- // Resolves once this transaction's own handler has run.
3381
- whenSettled() {
3382
- return this.completion.whenSettled();
3383
- }
3384
- // Resolves once this transaction AND all transitively-derived work (sends, bubbles,
3385
- // requests and the responses they produce, recursively) have settled.
3386
- whenSubtreeSettled() {
3387
- return this.completion.whenSubtreeSettled();
3388
- }
3389
- run(rootValue, comps) {
3390
- return this.updateRootValue(rootValue, comps);
3391
- }
3392
- afterTransaction() {
3393
- }
3394
- // Ending a walk and forwarding belong to the two dispatch buckets; the base warns
3395
- // rather than throwing, so a stray call from the wrong place is a message and not a
3396
- // crash. `walk` is undefined here, which is how ctx.reply/ctx.fail tell the two apart.
3397
- stop() {
3398
- console.warn('ctx.stop() is only meaningful in an "intent" handler - ignored');
3399
- }
3400
- forward(_opts) {
3401
- console.warn('ctx.forward() needs a "receive" or "intent" handler - ignored');
3402
- }
3403
- buildRootStack(root, comps) {
3404
- return Stack.root(comps, root);
3405
- }
3406
- buildStack(root, comps) {
3407
- return this.path.toTransactionPath().buildStack(this.buildRootStack(root, comps));
3408
- }
3409
- // The kind reported to observers (see Transactor.observe); null on the base.
3410
- get observeKind() {
3411
- return null;
3412
- }
3413
- // The name reported to observers; null on the base. Overridden by NameArgs (the
3414
- // dispatched message name) and InputEvent (the DOM event type). Kept separate from
3415
- // `name`/`ctx.name` so it can't change handler-visible behavior.
3416
- get observeName() {
3417
- return null;
3418
- }
3419
- callHandler(root, instance, draft, comps) {
3420
- const [handler, args] = this.getHandlerAndArgs(root, instance, comps);
3421
- this._resolvedHandler = handler;
3422
- return handler.apply(instance, [draft, ...args]);
3423
- }
3424
- getHandlerAndArgs(_root, _instance, _comps) {
3425
- return null;
3426
- }
3427
- // The path used to apply the mutation. Teleports dynamic-var renders so it lands on
3428
- // the data's real location (the dispatch `this.path` keeps intermediates). A subclass
3429
- // may override to supply a pre-resolved path (see ResponseEvent's pinned keys).
3430
- getTransactionPath() {
3431
- return this.path.toTransactionPath().compact();
3432
- }
3433
- updateRootValue(curRoot, comps) {
3434
- const txnPath = this.getTransactionPath();
3435
- const curLeaf = txnPath.lookup(curRoot);
3436
- const newLeaf = (0, immer_exports.produce)(curLeaf, (draft) => {
3437
- const result = this.callHandler(curRoot, curLeaf, draft, comps);
3438
- if (result === void 0 || result === draft) validateDraftFields(curLeaf, draft);
3439
- return result;
3440
- });
3441
- this._before = curLeaf;
3442
- this._after = newLeaf;
3443
- this._completion?.markSelfSettled({ value: newLeaf, old: curLeaf });
3444
- return curLeaf !== newLeaf ? txnPath.setValue(curRoot, newLeaf) : curRoot;
3445
- }
3446
- lookupName(_name) {
3447
- return null;
3448
- }
3449
- };
3450
- var toNullIfNaN = (v) => Number.isNaN(v) ? null : v;
3451
- function getValue(e) {
3452
- return e.target.type === "checkbox" ? e.target.checked : (e instanceof CustomEvent ? e.detail : e.target.value) ?? null;
3453
- }
3454
- var InputEvent = class extends Transaction {
3455
- constructor(path, e, handler, transactor, dragInfo) {
3456
- super(path, transactor);
3457
- this.e = e;
3458
- this.handler = handler;
3459
- this.dragInfo = dragInfo;
3460
- this._dispatchPath = null;
3461
- }
3462
- // Frame steps removed, DynStep + one step per crossed component kept: bubbling
3463
- // it visits every component (including intermediates of a dynamic-var render).
3464
- get dispatchPath() {
3465
- this._dispatchPath ??= this.path.compact();
3466
- return this._dispatchPath;
3467
- }
3468
- // A DOM event is an ADDRESSED message like any other — it reaches the component that
3469
- // owns the view and stops. It keeps its own class only because it resolves its handler
3470
- // from the compiled view and runs synchronously, not because it is a different channel.
3471
- get observeKind() {
3472
- return "receive";
3473
- }
3474
- get observeName() {
3475
- return this.e?.type ?? null;
3476
- }
3477
- // A view's name is a message. `forward` is where that name can LEAVE the component:
3478
- // it becomes an intent with the same name and the same arguments, so a view that says
3479
- // `@on.click="saveDraft .text"` never has to change when an ancestor takes the job over.
3480
- // (A `$method` handler has a name too, so forwarding one raises an intent under it.)
3481
- forward(opts) {
3482
- this._forward = opts ?? {};
3483
- }
3484
- afterTransaction() {
3485
- const f = this._forward;
3486
- if (f === void 0) return;
3487
- this._forward = void 0;
3488
- const { args = this._handlerArgs ?? [], ...rest } = f;
3489
- const hv = this.handler?.handlerCall?.handlerVal ?? this.handler?.handlerVal;
3490
- const name = hv?.name;
3491
- if (name === void 0)
3492
- return console.warn("ctx.forward() from a handler with no name - ignored");
3493
- this.transactor.pushIntent(this.dispatchPath, name, args, rest, this);
3494
- }
3495
- buildRootStack(root, comps) {
3496
- return Stack.root(comps, root, this);
3497
- }
3498
- getHandlerAndArgs(root, _instance, comps) {
3499
- const stack = this.buildStack(root, comps);
3500
- const [handler, args] = this.handler.getHandlerAndArgs(stack, this);
3501
- this._handlerArgs = [...args];
3502
- const path = this.dispatchPath;
3503
- args.push(new EventContext(path, this.transactor, this));
3504
- return [handler, args];
3505
- }
3506
- lookupName(name) {
3507
- const { e } = this;
3508
- switch (name) {
3509
- case "value":
3510
- return getValue(e);
3511
- case "valueAsInt":
3512
- return toNullIfNaN(parseInt(getValue(e), 10));
3513
- case "valueAsFloat":
3514
- return toNullIfNaN(parseFloat(getValue(e)));
3515
- case "target":
3516
- return e.target;
3517
- case "event":
3518
- return e;
3519
- case "isAlt":
3520
- return e.altKey;
3521
- case "isShift":
3522
- return e.shiftKey;
3523
- case "isCtrl":
3524
- /* falls through */
3525
- case "isCmd":
3526
- return isMac && e.metaKey || e.ctrlKey;
3527
- case "key":
3528
- return e.key;
3529
- case "keyCode":
3530
- return e.keyCode;
3531
- case "isUpKey":
3532
- return e.key === "ArrowUp";
3533
- case "isDownKey":
3534
- return e.key === "ArrowDown";
3535
- case "isSend":
3536
- return e.key === "Enter";
3537
- case "isCancel":
3538
- return e.key === "Escape";
3539
- case "isTabKey":
3540
- return e.key === "Tab";
3541
- case "ctx":
3542
- return new EventContext(this.dispatchPath, this.transactor, this);
3543
- case "dragInfo":
3544
- return this.dragInfo;
3545
- }
3546
- return null;
3547
- }
3548
- };
3549
- var NameArgsTransaction = class extends Transaction {
3550
- constructor(path, transactor, name, args, parentTransaction, opts = {}) {
3551
- super(path, transactor, parentTransaction);
3552
- this.name = name;
3553
- this.args = args;
3554
- this.opts = opts;
3555
- this.targetPath = path;
3556
- }
3557
- handlerProp = null;
3558
- // NameArgs verbs map their handler bucket straight to the observed kind:
3559
- // receive / bubble / response / input.
3560
- get observeKind() {
3561
- return this.handlerProp;
3562
- }
3563
- get observeName() {
3564
- return this.name;
3565
- }
3566
- getHandlerForName(comp) {
3567
- const handlers = comp?.[this.handlerProp];
3568
- const exact = handlers?.[this.name];
3569
- if (exact) {
3570
- this._matched = "exact";
3571
- return exact;
3572
- }
3573
- const unknown = handlers?.$unknown;
3574
- if (unknown) {
3575
- this._matched = "unknown";
3576
- return unknown;
3577
- }
3578
- this._matched = "none";
3579
- return nullHandler;
3580
- }
3581
- getHandlerAndArgs(_root, instance, comps) {
3582
- const handler = this.getHandlerForName(comps.getCompFor(instance));
3583
- return [handler, [...this.args, new EventContext(this.path, this.transactor, this)]];
3584
- }
3585
- };
3586
- var SendEvent = class extends NameArgsTransaction {
3587
- handlerProp = "receive";
3588
- // `receive` is the ADDRESSED bucket, and it is the only one: a view's `@on.click`,
3589
- // a parent's ctx.send, the host's sendAtRoot and an answer to an intent all arrive
3590
- // here, and a handler cannot tell them apart. Splitting on where a message came from
3591
- // would let the view's name through and turn the parent's away, though the two say
3592
- // the same thing — and neither a test nor a parent could drive the component.
3593
- get observeKind() {
3594
- return this._isAnswer ? "answer" : "receive";
3595
- }
3596
- // `forward` in a RECEIVE body starts a walk: the message that arrived becomes an
3597
- // intent, keeping its name and payload. This is what lets a view's name leave the
3598
- // component without the view changing.
3599
- forward(opts) {
3600
- this._forward = opts ?? {};
3601
- }
3602
- afterTransaction() {
3603
- const f = this._forward;
3604
- if (f === void 0) return;
3605
- this._forward = void 0;
3606
- const { args = this.args, ...rest } = f;
3607
- this.transactor.pushIntent(this.path, this.name, args, rest, this);
3608
- }
3609
- };
3610
- var IntentEvent = class extends NameArgsTransaction {
3611
- handlerProp = "intent";
3612
- constructor(path, transactor, walk) {
3613
- super(path, transactor, walk.name, walk.args, walk.parent, {});
3614
- this.walk = walk;
3615
- this.targetPath = walk.origin;
3616
- }
3617
- // `forward` in an INTENT body amends the hop about to be pushed — it does not push
3618
- // one itself, because a walk advances on its own.
3619
- forward(opts) {
3620
- this.walk.amend(opts, this.path);
3621
- }
3622
- stop() {
3623
- this.walk.finish(null, null);
3624
- }
3625
- afterTransaction() {
3626
- this.ensureWalkAdvanced();
3627
- }
3628
- // Idempotent, because both afterTransaction and the transactor's `finally` call it —
3629
- // whichever gets there first advances the walk exactly once.
3630
- ensureWalkAdvanced() {
3631
- if (this._advanced) return;
3632
- this._advanced = true;
3633
- this.walk.advance();
3634
- }
3635
- };
3636
- var PASS = /* @__PURE__ */ Symbol.for("tutuca.intent.pass");
3637
- var DEFAULT_ROUTE = ["dyn", "lex"];
3638
- var INTENT_DEPTH = 64;
3639
- var IntentWalk = class {
3640
- constructor(transactor, path, name, args, opts, parent, release) {
3641
- this.transactor = transactor;
3642
- this.name = name;
3643
- this.args = args;
3644
- this.route = opts?.route ?? DEFAULT_ROUTE;
3645
- this.parent = parent;
3646
- this.release = release;
3647
- this.origin = path;
3648
- this.answerPath = opts?.livePath ? null : path.toTransactionPath().pinKeys(transactor.state.val);
3649
- this.legIndex = 0;
3650
- this.dynAt = path;
3651
- this.hops = 0;
3652
- this.ended = false;
3653
- }
3654
- // Offer the intent to the next hop. Called once at dispatch, and again by every hop
3655
- // that ran without answering.
3656
- advance() {
3657
- if (this.ended) return;
3658
- if (this.hops++ >= INTENT_DEPTH) return this.exhaust("intentDepth");
3659
- while (this.legIndex < this.route.length) {
3660
- const leg = this.route[this.legIndex];
3661
- if (leg === "dyn") {
3662
- if (this.dynAt.steps.length === 0) {
3663
- this.legIndex++;
3664
- continue;
3665
- }
3666
- this.dynAt = this.dynAt.popStep();
3667
- this.transactor.pushTransaction(new IntentEvent(this.dynAt, this.transactor, this));
3668
- return;
3669
- }
3670
- if (leg === "lex") {
3671
- this.legIndex++;
3672
- return this._tryLex();
3673
- }
3674
- console.warn("unknown intent route leg", leg, '- expected "dyn" or "lex"');
3675
- this.legIndex++;
3676
- }
3677
- this.exhaust("noHandler");
3678
- }
3679
- // The `lex` leg: the handlers registered on the scope chain of the component that
3680
- // raised the intent, in order.
3681
- _tryLex() {
3682
- const root = this.transactor.state.val;
3683
- const txnPath = this.origin.toTransactionPath();
3684
- const leaf = txnPath.lookup(root);
3685
- const chain = this.transactor.comps.getIntentChainFor(leaf, this.name);
3686
- if (this.transactor._observers.length > 0) {
3687
- const path = txnPath.pinKeys(root);
3688
- this.transactor._emit({
3689
- kind: "intent",
3690
- name: this.name,
3691
- args: this.args,
3692
- path,
3693
- pathKeys: path.toKeys(),
3694
- targetPath: this.origin,
3695
- handler: chain[0]?.fn ?? null,
3696
- handlerName: chain[0]?.fn?.name || this.name,
3697
- matched: chain.length > 0 ? "exact" : "none",
3698
- before: leaf,
3699
- after: void 0,
3700
- parent: this.parent,
3701
- timestamp: Date.now()
3702
- });
3703
- }
3704
- if (chain.length === 0) return this.advance();
3705
- const p = this._runLex(chain, 0);
3706
- this.transactor._inflight.add(p);
3707
- p.finally(() => this.transactor._inflight.delete(p));
3708
- }
3709
- async _runLex(chain, i) {
3710
- if (this.ended) return;
3711
- if (i >= chain.length) return this.advance();
3712
- const ctx = new IntentContext(this.origin, this.transactor, this.parent);
3713
- try {
3714
- const res = await chain[i].fn.apply(null, [...this.args, ctx]);
3715
- if (res === PASS) return this._runLex(chain, i + 1);
3716
- this.answer("Ok", res);
3717
- } catch (error) {
3718
- this.answer("Error", error);
3719
- }
3720
- }
3721
- // A hop answered. Ends the walk.
3722
- answer(suffix, value) {
3723
- this.finish(`${this.name}${suffix}`, [value]);
3724
- }
3725
- // `forward` from an intent body: keep walking, optionally with new arguments or a
3726
- // narrowed route.
3727
- amend(opts, from) {
3728
- if (opts?.args !== void 0) this.args = opts.args;
3729
- if (opts?.route !== void 0) {
3730
- this.route = opts.route;
3731
- this.legIndex = 0;
3732
- this.dynAt = from ?? this.dynAt;
3733
- }
3734
- }
3735
- // The route ran out. What the sender hears depends only on what it DECLARES, which is
3736
- // how "a sender expects an answer if and only if it declares an answer arm" becomes
3737
- // code — nobody writes it down twice, and there is no schema to keep in step.
3738
- exhaust(reason) {
3739
- if (this.ended) return;
3740
- const { name, args } = this;
3741
- const comp = this.transactor.comps.getCompFor(
3742
- this.origin.toTransactionPath().lookup(this.transactor.state.val)
3743
- );
3744
- const declares = (n) => comp?.receive?.[n] !== void 0;
3745
- if (declares(`${name}Unhandled`)) {
3746
- this.finish(`${name}Unhandled`, args);
3747
- } else if (declares(`${name}Error`)) {
3748
- this.finish(`${name}Error`, [reason]);
3749
- } else {
3750
- if (declares(`${name}Ok`))
3751
- console.warn(
3752
- `intent "${name}" was not answered (${reason}) and this component declares only "${name}Ok" - add "${name}Unhandled" or "${name}Error" to handle it`
3753
- );
3754
- this.finish(null, null);
3755
- }
3756
- }
3757
- // End the walk. `name === null` means no answer goes out, which is what `stop` and a
3758
- // fire-and-forget notification both are — the tracked unit is released here instead
3759
- // of following an answer's subtree.
3760
- finish(name, args) {
3761
- if (this.ended) return;
3762
- this.ended = true;
3763
- if (name === null) return this.release?.();
3764
- const path = this.answerPath ?? this.origin;
3765
- const t = new SendEvent(path, this.transactor, name, args, this.parent, {});
3766
- t._isAnswer = true;
3767
- this.transactor.pushTransaction(t);
3768
- if (this.release) t.completion.whenSubtreeSettled().then(this.release);
3769
- }
3770
- };
3771
- var Completion = class {
3772
- constructor() {
3773
- this.val = void 0;
3774
- this.selfSettled = false;
3775
- this.subtreeSettled = false;
3776
- this.pending = 1;
3777
- this._selfResolve = null;
3778
- this._selfPromise = null;
3779
- this._subtreeResolve = null;
3780
- this._subtreePromise = null;
3781
- this._selfReleased = false;
3782
- }
3783
- whenSettled() {
3784
- if (this.selfSettled) return Promise.resolve(this.val);
3785
- this._selfPromise ??= new Promise((res) => {
3786
- this._selfResolve = res;
3787
- });
3788
- return this._selfPromise;
3789
- }
3790
- whenSubtreeSettled() {
3791
- if (this.subtreeSettled) return Promise.resolve(this.val);
3792
- this._subtreePromise ??= new Promise((res) => {
3793
- this._subtreeResolve = res;
3794
- });
3795
- return this._subtreePromise;
3796
- }
3797
- // The transaction's own handler ran (records its {value, old}). Does not touch the counter.
3798
- markSelfSettled(val) {
3799
- if (this.selfSettled) return;
3800
- this.selfSettled = true;
3801
- this.val = val;
3802
- this._selfResolve?.(val);
3803
- }
3804
- // Settle self even when no handler produced a value (skipSelf / undefined / throw paths).
3805
- ensureSelfSettled() {
3806
- if (!this.selfSettled) this.markSelfSettled(this.val);
3807
- }
3808
- // Register an outstanding unit; returns a one-shot release.
3809
- track() {
3810
- this.pending++;
3811
- let done = false;
3812
- return () => {
3813
- if (done) return;
3814
- done = true;
3815
- this._release();
3816
- };
3817
- }
3818
- releaseSelf() {
3819
- if (this._selfReleased) return;
3820
- this._selfReleased = true;
3821
- this._release();
3822
- }
3823
- _release() {
3824
- if (--this.pending === 0) {
3825
- this.subtreeSettled = true;
3826
- this._subtreeResolve?.(this.val);
3827
- }
3828
- }
3829
- };
3830
- var Dispatcher = class {
3831
- constructor(path, transactor, parentTransaction, root = transactor.state.val) {
3832
- this.path = path;
3833
- this.transactor = transactor;
3834
- this.parent = parentTransaction;
3835
- this.root = root;
3836
- }
3837
- // Walk the component instances on this ctx's path, leaf→root, calling
3838
- // callback(Component, instance). Return false from the callback to stop early.
3839
- walkPath(callback) {
3840
- const comps = this.transactor.comps;
3841
- const chain = this.path.toTransactionPath().resolveChain(this.root);
3842
- for (let i = chain.length - 1; i >= 0; i--) {
3843
- const comp = comps.getCompFor(chain[i]);
3844
- if (comp && callback(comp, chain[i]) === false) return;
3845
- }
3846
- }
3847
- get at() {
3848
- return new PathChanges(this);
3849
- }
3850
- // A message is ADDRESSED: it names one component and stops there.
3851
- send(name, args, opts) {
3852
- return this.sendAtPath(this.path, name, args, opts);
3853
- }
3854
- sendAtPath(path, name, args, opts) {
3855
- return this.transactor.pushSend(path, name, args, opts, this.parent);
3856
- }
3857
- // An intent is ROUTED: it names a job and walks until something answers. The verb
3858
- // does not decide which scope answers — `opts.route` does, written here at the call
3859
- // site, which is where the decision actually is.
3860
- intent(name, args, opts) {
3861
- return this.intentAtPath(this.path, name, args, opts);
3862
- }
3863
- intentAtPath(path, name, args, opts) {
3864
- return this.transactor.pushIntent(path, name, args, opts, this.parent);
3865
- }
3866
- lookupTypeFor(name, inst) {
3867
- return this.transactor.comps.getCompFor(inst).scope.lookupComponent(name);
3868
- }
3869
- };
3870
- var EventContext = class extends Dispatcher {
3871
- get name() {
3872
- return this.parent?.name ?? null;
3873
- }
3874
- get targetPath() {
3875
- return this.parent.targetPath;
3876
- }
3877
- // Answer the intent being handled, with a result or with an error. Either ENDS the
3878
- // walk. Legal only in an `intent` handler: a message has no sender waiting on it.
3879
- reply(value) {
3880
- return this.parent.walk === void 0 ? warnNotIntent("reply") : this.parent.walk.answer("Ok", value);
3881
- }
3882
- fail(error) {
3883
- return this.parent.walk === void 0 ? warnNotIntent("fail") : this.parent.walk.answer("Error", error);
3884
- }
3885
- // End the walk answering nothing — "served, and no answer".
3886
- stop() {
3887
- return this.parent.stop();
3888
- }
3889
- // From an `intent` body: hand the intent to the next hop, optionally amending it.
3890
- // From a `receive` body: turn the message that arrived into an intent. One word from
3891
- // both ends of a walk; which one you get depends on which bucket you are in.
3892
- forward(opts) {
3893
- return this.parent.forward(opts);
3894
- }
3895
- };
3896
- var IntentContext = class extends Dispatcher {
3897
- };
3898
- function warnNotIntent(verb) {
3899
- console.warn(`ctx.${verb}() is only meaningful in an "intent" handler - ignored`);
3900
- }
3901
- var PathChanges = class extends PathBuilder {
3902
- constructor(dispatcher) {
3903
- super();
3904
- this.dispatcher = dispatcher;
3905
- }
3906
- send(name, args, opts) {
3907
- return this.dispatcher.sendAtPath(this.buildPath(), name, args, opts);
3908
- }
3909
- intent(name, args, opts) {
3910
- return this.dispatcher.intentAtPath(this.buildPath(), name, args, opts);
3911
- }
3912
- buildPath() {
3913
- return this.dispatcher.path.concat(this.pathChanges);
3914
- }
3915
- };
3916
- function rootDispatcher(transactor) {
3917
- return new Dispatcher(new Path([]), transactor, null);
3918
- }
3919
-
3920
- // src/app.js
3921
- var _evs = "dragstart dragover dragend touchstart touchmove touchend touchcancel".split(" ");
3922
- var App = class {
3923
- constructor(rootNode, comps, renderer, ParseContext2) {
3924
- this.rootNode = rootNode;
3925
- this.comps = comps;
3926
- this.compStack = new ComponentStack(comps);
3927
- this.transactor = new Transactor(comps, null);
3928
- this.ParseContext = ParseContext2;
3929
- this.renderer = renderer;
3930
- this.maxEventNodeDepth = Infinity;
3931
- this._transactNextBatchId = this._evictCacheId = null;
3932
- this._eventNames = new Set(_evs);
3933
- this.dragInfo = this.curDragOver = null;
3934
- this._touch = null;
3935
- this.transactor.onTransactionPushed = (_transaction) => {
3936
- if (this._transactNextBatchId === null) this._scheduleNextTransactionBatchExecution();
3937
- };
3938
- this._compiled = false;
3939
- this._renderOpts = { document: rootNode.ownerDocument };
3940
- this._renderState = null;
3941
- this.rootViewName = null;
3942
- }
3943
- get state() {
3944
- return this.transactor.state;
3945
- }
3946
- handleEvent(e) {
3947
- const { type } = e;
3948
- if (type[0] === "t" && type.startsWith("touch")) {
3949
- this._handleTouchEvent(e);
3950
- return;
3951
- }
3952
- this._dispatchEvent(e);
3953
- }
3954
- _dispatchEvent(e) {
3955
- const { type } = e;
3956
- const isDrag = type === "dragover" || type === "dragstart" || type === "dragend" || type === "drop";
3957
- const { rootNode: root, maxEventNodeDepth: maxDepth, comps, transactor } = this;
3958
- const [path, handlers] = Path.fromEvent(e, root, maxDepth, comps, !isDrag);
3959
- if (isDrag) this._handleDragEvent(e, type, path);
3960
- if (path !== null && handlers !== null)
3961
- for (const handler of handlers) transactor.transactInputNow(path, e, handler, this.dragInfo);
3962
- }
3963
- _handleTouchEvent(e) {
3964
- const { type } = e;
3965
- if (type === "touchstart") {
3966
- if (this._touch !== null || e.touches.length !== 1) return;
3967
- const t = e.touches[0];
3968
- const draggable = t.target?.closest?.('[draggable="true"]');
3969
- if (!draggable) return;
3970
- this._touch = makeTouchInfo(t.identifier, t.clientX, t.clientY, draggable, false);
3971
- return;
3972
- }
3973
- if (this._touch === null) return;
3974
- const touch = findTouch(e, this._touch.id);
3975
- if (touch === null) return;
3976
- const { rootNode, _touch } = this;
3977
- const { clientX, clientY } = touch;
3978
- const fire = (type2, target) => {
3979
- const e2 = { type: type2, target, clientX, clientY, preventDefault: NOOP };
3980
- this._dispatchEvent(e2);
3981
- };
3982
- if (type === "touchmove") {
3983
- if (!_touch.active) {
3984
- const dx = clientX - _touch.startX;
3985
- const dy = clientY - _touch.startY;
3986
- if (dx * dx + dy * dy < 100) return;
3987
- _touch.active = true;
3988
- e.preventDefault();
3989
- fire("dragstart", _touch.target);
3990
- } else {
3991
- e.preventDefault();
3992
- fire("dragover", hitTest(rootNode, clientX, clientY));
3993
- }
3994
- return;
3995
- }
3996
- if (type === "touchend" || type === "touchcancel") {
3997
- if (_touch.active) {
3998
- if (type === "touchend") fire("drop", hitTest(rootNode, clientX, clientY));
3999
- fire("dragend", _touch.target);
4000
- }
4001
- this._touch = null;
4002
- }
4003
- }
4004
- _handleDragEvent(e, type, path) {
4005
- if (type === "dragover") {
4006
- const dropTarget = getClosestDropTarget(e.target, this.rootNode, Infinity);
4007
- if (dropTarget !== null) {
4008
- e.preventDefault();
4009
- this._cleanDragOverAttrs();
4010
- this.curDragOver = dropTarget;
4011
- dropTarget.dataset.draggingover = this.dragInfo?.type ?? "_external";
4012
- }
4013
- } else if (type === "dragstart") {
4014
- e.target.dataset.dragging = 1;
4015
- const rootValue = this.state.val;
4016
- const txnPath = path.compact().toTransactionPath();
4017
- const value = txnPath.lookup(rootValue);
4018
- const dragType = e.target.dataset.dragtype ?? "?";
4019
- const stack = path.toTransactionPath().buildStack(this.makeStack(rootValue));
4020
- this.dragInfo = new DragInfo(txnPath, stack, e, value, dragType, e.target);
4021
- } else if (type === "drop") {
4022
- e.preventDefault();
4023
- this._cleanDragOverAttrs();
4024
- } else {
4025
- if (this.dragInfo !== null) {
4026
- delete this.dragInfo.node.dataset.dragging;
4027
- this.dragInfo = null;
4028
- }
4029
- this._cleanDragOverAttrs();
4030
- }
4031
- }
4032
- makeStack(rootValue) {
4033
- return Stack.root(this.comps, rootValue);
4034
- }
4035
- _cleanDragOverAttrs() {
4036
- if (this.curDragOver !== null) {
4037
- delete this.curDragOver.dataset.draggingover;
4038
- this.curDragOver = null;
4039
- }
4040
- }
4041
- render() {
4042
- const root = this.state.val;
4043
- const stack = this.makeStack(root);
4044
- const { renderer, rootNode, _renderOpts, _renderState } = this;
4045
- const newState = render(
4046
- renderer.renderRoot(stack, root, this.rootViewName),
4047
- rootNode,
4048
- _renderOpts,
4049
- _renderState
4050
- );
4051
- this._renderState = newState;
4052
- return newState.dom;
4053
- }
4054
- onChange(callback) {
4055
- this.transactor.state.onChange(callback);
4056
- }
4057
- // Subscribe to a normalized record for every handler invocation. Returns an
4058
- // unsubscribe function. See Transactor.observe.
4059
- observe(callback) {
4060
- return this.transactor.observe(callback);
4061
- }
4062
- compile() {
4063
- for (const Comp of this.comps.byId.values()) {
4064
- Comp.compile(this.ParseContext);
4065
- for (const key in Comp.views)
4066
- for (const name of Comp.views[key].ctx.genEventNames()) this._eventNames.add(name);
4067
- }
4068
- this._compiled = true;
4069
- }
4070
- subscribeToEvents(eventNames) {
4071
- for (const name of eventNames) this.rootNode.addEventListener(name, this, listenerOpts(name));
4072
- }
4073
- recompileStyles(opts) {
4074
- injectCss("tutuca-app", this.comps.compileStyles(), opts?.head ?? document.head);
4075
- }
4076
- start(opts) {
4077
- if (!this._compiled) this.compile();
4078
- this.subscribeToEvents(this._eventNames);
4079
- this.onChange((info) => {
4080
- if (info.val !== info.old) this.render();
4081
- });
4082
- this.recompileStyles(opts);
4083
- if (opts?.noCache) this.renderer.setNullCache();
4084
- else this.startCacheEvictionInterval();
4085
- this.render();
4086
- }
4087
- stop() {
4088
- this.stopCacheEvictionInterval();
4089
- for (const name of this._eventNames)
4090
- this.rootNode.removeEventListener(name, this, listenerOpts(name));
4091
- }
4092
- sendAtRoot(name, args, opts) {
4093
- this.transactor.pushSend(new Path([]), name, args, opts);
4094
- }
4095
- registerComponents(comps, opts) {
4096
- const scope = this.compStack.enter();
4097
- scope.registerComponents(comps, opts);
4098
- return scope;
4099
- }
4100
- _transactNextBatch(maxRunTimeMs = 10) {
4101
- this._transactNextBatchId = null;
4102
- const startTs = Date.now();
4103
- const t = this.transactor;
4104
- while (t.hasPendingTransactions && Date.now() - startTs < maxRunTimeMs) t.transactNext();
4105
- if (t.hasPendingTransactions) this._scheduleNextTransactionBatchExecution();
4106
- }
4107
- _scheduleNextTransactionBatchExecution() {
4108
- this._transactNextBatchId = setTimeout(() => this._transactNextBatch(), 0);
4109
- }
4110
- startCacheEvictionInterval(intervalMs = 3e4) {
4111
- this._evictCacheId = setInterval(() => this.renderer.cache.evict(), intervalMs);
4112
- }
4113
- stopCacheEvictionInterval() {
4114
- clearInterval(this._evictCacheId);
4115
- this._evictCacheId = null;
4116
- }
4117
- };
4118
- function injectCss(nodeId, style, styleTarget = document.head) {
4119
- const styleNode = document.createElement("style");
4120
- const currentNodeWithId = styleTarget.querySelector(`#${nodeId}`);
4121
- if (currentNodeWithId) styleTarget.removeChild(currentNodeWithId);
4122
- styleNode.id = nodeId;
4123
- styleNode.innerHTML = style;
4124
- styleTarget.appendChild(styleNode);
4125
- }
4126
- var NOOP = () => {
4127
- };
4128
- function findTouch(e, id) {
4129
- for (const t of e.changedTouches) if (t.identifier === id) return t;
4130
- for (const t of e.touches) if (t.identifier === id) return t;
4131
- return null;
4132
- }
4133
- var listenerOpts = (name) => name === "touchmove" ? { passive: false } : void 0;
4134
- function makeTouchInfo(id, startX, startY, target, active) {
4135
- return { id, startX, startY, target, active };
4136
- }
4137
- function hitTest(rootNode, x, y) {
4138
- const root = rootNode.getRootNode();
4139
- let el = root.elementFromPoint?.(x, y) ?? null;
4140
- while (el?.shadowRoot) {
4141
- const next = el.shadowRoot.elementFromPoint(x, y);
4142
- if (next === null || next === el) break;
4143
- el = next;
4144
- }
4145
- return el ?? rootNode;
4146
- }
4147
- function getClosestDropTarget(target, rootNode, count) {
4148
- let node = target;
4149
- while (count-- > 0 && node !== rootNode) {
4150
- if (node.dataset?.droptarget !== void 0) return node;
4151
- node = node.parentNode;
4152
- }
4153
- return null;
4154
- }
4155
- var DragInfo = class {
4156
- constructor(path, stack, e, val, type, node) {
4157
- this.path = path;
4158
- this.stack = stack;
4159
- this.e = e;
4160
- this.val = val;
4161
- this.type = type;
4162
- this.node = node;
4163
- }
4164
- lookupBind(name) {
4165
- return this.stack.lookupBind(name);
4166
- }
4167
- };
4168
-
4169
- // src/on.js
4170
- var OP_KINDS = ["send", "intent"];
4171
- function phaseOps(phase) {
4172
- const ops = [];
4173
- for (const type of OP_KINDS) for (const a of phase[type] ?? []) ops.push({ type, ...a });
4174
- for (const a of phase.do ?? []) ops.push(a);
4175
- return ops;
4176
- }
4177
- function resolveArgs(args, self) {
4178
- return typeof args === "function" ? args(self) ?? [] : args ?? [];
4179
- }
4180
- function dispatchPhase(dispatcher, targetPath, phase, self) {
4181
- if (!phase) return;
4182
- for (const op of phaseOps(phase)) {
4183
- const args = resolveArgs(op.args, self);
4184
- switch (op.type) {
4185
- case "send":
4186
- dispatcher.sendAtPath(targetPath, op.name, args, op.opts);
4187
- break;
4188
- case "intent":
4189
- dispatcher.intentAtPath(targetPath, op.name, args, op.opts);
4190
- break;
4191
- }
4192
- }
4193
- }
4194
-
4195
- // index.js
4196
- var css = String.raw;
4197
- var html = String.raw;
4198
- var macro = (defaults, rawView) => new Macro(defaults, rawView);
4199
- function check2(_app) {
4200
- return { error: 0, warn: 0, hint: 0, dummyCheck: true };
4201
- }
4202
- async function test(_opts) {
4203
- return null;
4204
- }
4205
- function collectIterBindings() {
4206
- console.warn(
4207
- "collectIterBindings is a no-op in the core tutuca build; use the tutuca-dev build for a functional implementation"
4208
- );
4209
- return [];
4210
- }
4211
- function tutuca(nodeOrSelector) {
4212
- const rootNode = typeof nodeOrSelector === "string" ? document.querySelector(nodeOrSelector) : nodeOrSelector;
4213
- const comps = new Components();
4214
- const renderer = new Renderer(comps);
4215
- return new App(rootNode, comps, renderer, ParseContext);
4216
- }
4217
- export {
4218
- FIELD_CLASS,
4219
- PASS,
4220
- ParseContext,
4221
- SEQ_INFO,
4222
- check2 as check,
4223
- collectIterBindings,
4224
- component,
4225
- css,
4226
- dispatchPhase,
4227
- html,
4228
- injectCss,
4229
- macro,
4230
- phaseOps,
4231
- resolveArgs,
4232
- rootDispatcher,
4233
- test,
4234
- tutuca
4235
- };