@adaas/are-html 0.0.20 → 0.0.22

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 (109) hide show
  1. package/.conf/tsconfig.base.json +1 -0
  2. package/.conf/tsconfig.browser.json +1 -0
  3. package/.conf/tsconfig.node.json +1 -0
  4. package/dist/browser/index.d.mts +206 -7
  5. package/dist/browser/index.mjs +527 -65
  6. package/dist/browser/index.mjs.map +1 -1
  7. package/dist/node/directives/AreDirectiveFor.directive.d.mts +44 -1
  8. package/dist/node/directives/AreDirectiveFor.directive.d.ts +44 -1
  9. package/dist/node/directives/AreDirectiveFor.directive.js +102 -6
  10. package/dist/node/directives/AreDirectiveFor.directive.js.map +1 -1
  11. package/dist/node/directives/AreDirectiveFor.directive.mjs +102 -6
  12. package/dist/node/directives/AreDirectiveFor.directive.mjs.map +1 -1
  13. package/dist/node/directives/AreDirectiveShow.directive.d.mts +32 -0
  14. package/dist/node/directives/AreDirectiveShow.directive.d.ts +32 -0
  15. package/dist/node/directives/AreDirectiveShow.directive.js +81 -0
  16. package/dist/node/directives/AreDirectiveShow.directive.js.map +1 -0
  17. package/dist/node/directives/AreDirectiveShow.directive.mjs +71 -0
  18. package/dist/node/directives/AreDirectiveShow.directive.mjs.map +1 -0
  19. package/dist/node/engine/AreHTML.engine.d.mts +2 -1
  20. package/dist/node/engine/AreHTML.engine.d.ts +2 -1
  21. package/dist/node/engine/AreHTML.engine.js +8 -2
  22. package/dist/node/engine/AreHTML.engine.js.map +1 -1
  23. package/dist/node/engine/AreHTML.engine.mjs +8 -2
  24. package/dist/node/engine/AreHTML.engine.mjs.map +1 -1
  25. package/dist/node/engine/AreHTML.interpreter.d.mts +3 -0
  26. package/dist/node/engine/AreHTML.interpreter.d.ts +3 -0
  27. package/dist/node/engine/AreHTML.interpreter.js +29 -0
  28. package/dist/node/engine/AreHTML.interpreter.js.map +1 -1
  29. package/dist/node/engine/AreHTML.interpreter.mjs +29 -0
  30. package/dist/node/engine/AreHTML.interpreter.mjs.map +1 -1
  31. package/dist/node/engine/AreHTML.lifecycle.d.mts +8 -1
  32. package/dist/node/engine/AreHTML.lifecycle.d.ts +8 -1
  33. package/dist/node/engine/AreHTML.lifecycle.js +46 -3
  34. package/dist/node/engine/AreHTML.lifecycle.js.map +1 -1
  35. package/dist/node/engine/AreHTML.lifecycle.mjs +46 -3
  36. package/dist/node/engine/AreHTML.lifecycle.mjs.map +1 -1
  37. package/dist/node/helpers/AreScheduler.helper.d.mts +39 -0
  38. package/dist/node/helpers/AreScheduler.helper.d.ts +39 -0
  39. package/dist/node/helpers/AreScheduler.helper.js +40 -0
  40. package/dist/node/helpers/AreScheduler.helper.js.map +1 -0
  41. package/dist/node/helpers/AreScheduler.helper.mjs +40 -0
  42. package/dist/node/helpers/AreScheduler.helper.mjs.map +1 -0
  43. package/dist/node/index.d.mts +4 -1
  44. package/dist/node/index.d.ts +4 -1
  45. package/dist/node/index.js +21 -0
  46. package/dist/node/index.mjs +3 -0
  47. package/dist/node/instructions/AreHTML.instructions.constants.d.mts +1 -0
  48. package/dist/node/instructions/AreHTML.instructions.constants.d.ts +1 -0
  49. package/dist/node/instructions/AreHTML.instructions.constants.js +2 -1
  50. package/dist/node/instructions/AreHTML.instructions.constants.js.map +1 -1
  51. package/dist/node/instructions/AreHTML.instructions.constants.mjs +2 -1
  52. package/dist/node/instructions/AreHTML.instructions.constants.mjs.map +1 -1
  53. package/dist/node/instructions/AreHTML.instructions.types.d.mts +9 -1
  54. package/dist/node/instructions/AreHTML.instructions.types.d.ts +9 -1
  55. package/dist/node/instructions/HideElement.instruction.d.mts +13 -0
  56. package/dist/node/instructions/HideElement.instruction.d.ts +13 -0
  57. package/dist/node/instructions/HideElement.instruction.js +31 -0
  58. package/dist/node/instructions/HideElement.instruction.js.map +1 -0
  59. package/dist/node/instructions/HideElement.instruction.mjs +24 -0
  60. package/dist/node/instructions/HideElement.instruction.mjs.map +1 -0
  61. package/dist/node/lib/AreRoot/AreRoot.component.d.mts +57 -3
  62. package/dist/node/lib/AreRoot/AreRoot.component.d.ts +57 -3
  63. package/dist/node/lib/AreRoot/AreRoot.component.js +138 -49
  64. package/dist/node/lib/AreRoot/AreRoot.component.js.map +1 -1
  65. package/dist/node/lib/AreRoot/AreRoot.component.mjs +140 -51
  66. package/dist/node/lib/AreRoot/AreRoot.component.mjs.map +1 -1
  67. package/dist/node/lib/AreRoot/AreRootCache.context.d.mts +58 -0
  68. package/dist/node/lib/AreRoot/AreRootCache.context.d.ts +58 -0
  69. package/dist/node/lib/AreRoot/AreRootCache.context.js +106 -0
  70. package/dist/node/lib/AreRoot/AreRootCache.context.js.map +1 -0
  71. package/dist/node/lib/AreRoot/AreRootCache.context.mjs +99 -0
  72. package/dist/node/lib/AreRoot/AreRootCache.context.mjs.map +1 -0
  73. package/examples/dashboard/dist/index.html +1 -1
  74. package/examples/dashboard/dist/{mq19zxz4-mnlgmd.js → mqh9ryml-xat335.js} +1922 -1316
  75. package/examples/dashboard/src/concept.ts +3 -2
  76. package/examples/for-perf/concept.ts +45 -0
  77. package/examples/for-perf/containers/UI.container.ts +161 -0
  78. package/examples/for-perf/dist/index.html +270 -0
  79. package/examples/for-perf/dist/mqh9ryde-m243t8.js +15223 -0
  80. package/examples/for-perf/dist/mqh9ryfo-6a8d0o.js +15223 -0
  81. package/examples/for-perf/dist/mqh9ryfq-4pf5cv.js +15223 -0
  82. package/examples/for-perf/public/index.html +270 -0
  83. package/examples/for-perf/src/components/PerfApp.component.ts +37 -0
  84. package/examples/for-perf/src/components/PerfControls.component.ts +34 -0
  85. package/examples/for-perf/src/components/PerfGrid.component.ts +225 -0
  86. package/examples/for-perf/src/components/PerfHeader.component.ts +34 -0
  87. package/examples/for-perf/src/components/PerfStats.component.ts +43 -0
  88. package/examples/for-perf/src/concept.ts +94 -0
  89. package/examples/jumpstart/dist/index.html +1 -1
  90. package/examples/jumpstart/dist/{mq1a0fv0-ccgtz6.js → mq7mgf58-vbf07e.js} +895 -521
  91. package/examples/signal-routing/dist/index.html +1 -1
  92. package/examples/signal-routing/dist/{mq1bzrik-4lec86.js → mqh9ryc9-dkcbkx.js} +2024 -1300
  93. package/examples/signal-routing/src/components/SettingsPage.component.ts +39 -0
  94. package/examples/signal-routing/src/concept.ts +2 -0
  95. package/jest.config.ts +1 -0
  96. package/package.json +10 -9
  97. package/src/directives/AreDirectiveFor.directive.ts +185 -12
  98. package/src/directives/AreDirectiveShow.directive.ts +127 -0
  99. package/src/engine/AreHTML.engine.ts +11 -1
  100. package/src/engine/AreHTML.interpreter.ts +50 -0
  101. package/src/engine/AreHTML.lifecycle.ts +83 -6
  102. package/src/helpers/AreScheduler.helper.ts +61 -0
  103. package/src/index.ts +3 -0
  104. package/src/instructions/AreHTML.instructions.constants.ts +1 -0
  105. package/src/instructions/AreHTML.instructions.types.ts +9 -0
  106. package/src/instructions/HideElement.instruction.ts +29 -0
  107. package/src/lib/AreRoot/AreRoot.component.ts +205 -72
  108. package/src/lib/AreRoot/AreRootCache.context.ts +133 -0
  109. package/tsconfig.json +1 -0
@@ -1,11 +1,11 @@
1
- import { AreStore, AreScene, AreSyntax, AreCompiler, AreInterpreter, AreInstructionDefaultNames, AreNodeFeatures, AreContext, AreLifecycle, AreSignalsContext, AreAttributeFeatures, Are, AreNode, AreDeclaration, AreAttribute, AreCompilerError, AreMutation, AreSignal, AreInterpreterError, AreTokenizer, AreTransformer, AreEngine, AreSignals, AreEvent } from '@adaas/are';
1
+ import { AreStore, AreScene, AreSyntax, AreCompiler, AreInterpreter, AreInstructionDefaultNames, AreNodeFeatures, AreContext, AreLifecycle, AreSignalsContext, AreAttributeFeatures, Are, AreCompilerError, AreNode, AreDeclaration, AreAttribute, AreMutation, AreSignal, AreInterpreterError, AreTokenizer, AreTransformer, AreEngine, AreSignals, AreEvent } from '@adaas/are';
2
2
  import { A_Frame } from '@adaas/a-frame/core';
3
3
  import { A_Inject, A_Caller, A_Feature, A_Meta, A_Scope, A_Dependency, A_Component, A_Context, A_ComponentMeta, A_FormatterHelper, A_Fragment } from '@adaas/a-concept';
4
4
  import { A_Logger } from '@adaas/a-utils/a-logger';
5
5
  import { A_ExecutionContext } from '@adaas/a-utils/a-execution';
6
6
  import { A_Route } from '@adaas/a-utils/a-route';
7
7
  import { A_ServiceFeatures } from '@adaas/a-utils/a-service';
8
- import { A_SignalVector } from '@adaas/a-utils/a-signal';
8
+ import { A_SignalState, A_SignalVector } from '@adaas/a-utils/a-signal';
9
9
 
10
10
  var __defProp = Object.defineProperty;
11
11
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
@@ -207,7 +207,8 @@ var AreHTMLInstructions = {
207
207
  AddStyle: "_AreHTML_AddStyle",
208
208
  AddListener: "_AreHTML_AddListener",
209
209
  AddInterpolation: "_AreHTML_AddInterpolation",
210
- AddComment: "_AreHTML_AddComment"
210
+ AddComment: "_AreHTML_AddComment",
211
+ HideElement: "_AreHTML_HideElement"
211
212
  };
212
213
 
213
214
  // src/instructions/AddComment.instruction.ts
@@ -235,6 +236,44 @@ var AreDirectiveContext = class extends A_ExecutionContext {
235
236
  this.scope = {};
236
237
  }
237
238
  };
239
+
240
+ // src/helpers/AreScheduler.helper.ts
241
+ var AreSchedulerHelper = class {
242
+ /**
243
+ * High-resolution wall-clock time in milliseconds. Uses `performance.now()`
244
+ * when available (monotonic, sub-millisecond), falling back to `Date.now()`.
245
+ */
246
+ static now() {
247
+ return typeof performance !== "undefined" && typeof performance.now === "function" ? performance.now() : Date.now();
248
+ }
249
+ /**
250
+ * Schedule `fn` to run on the next macrotask.
251
+ *
252
+ * `MessageChannel` yields a true macrotask without the ~4ms clamp that nested
253
+ * `setTimeout(0)` calls incur, so the browser can paint between chunks with
254
+ * minimal scheduling overhead. Falls back to `setTimeout` in non-DOM
255
+ * environments (e.g. tests / SSR).
256
+ */
257
+ static scheduleMacrotask(fn) {
258
+ if (typeof MessageChannel === "undefined") {
259
+ setTimeout(fn, 0);
260
+ return;
261
+ }
262
+ if (!this._channel) {
263
+ this._channel = new MessageChannel();
264
+ this._channel.port1.onmessage = () => {
265
+ const next = this._queue.shift();
266
+ if (next) next();
267
+ };
268
+ }
269
+ this._queue.push(fn);
270
+ this._channel.port2.postMessage(null);
271
+ }
272
+ };
273
+ /** FIFO queue of callbacks waiting for their posted macrotask to fire. */
274
+ AreSchedulerHelper._queue = [];
275
+
276
+ // src/directives/AreDirectiveFor.directive.ts
238
277
  var AreDirectiveFor = class extends AreDirective {
239
278
  transform(attribute, scope, store, scene, logger, ...args) {
240
279
  logger.debug(`[Transform] directive $FOR for <${attribute.owner.aseid.toString()}>`);
@@ -263,11 +302,30 @@ var AreDirectiveFor = class extends AreDirective {
263
302
  scene.unPlan(hostInstruction);
264
303
  }
265
304
  update(attribute, store, scene, ...args) {
305
+ let state = AreDirectiveFor.renderState.get(attribute);
306
+ if (!state) {
307
+ state = { running: false, pending: false };
308
+ AreDirectiveFor.renderState.set(attribute, state);
309
+ }
310
+ if (state.running) {
311
+ state.pending = true;
312
+ return;
313
+ }
314
+ return this.performUpdate(attribute, store, scene, state);
315
+ }
316
+ /**
317
+ * Core of the `$for` update: re-diff the source array against the current
318
+ * children, reconcile reused/removed items, then mount the new ones (small
319
+ * lists synchronously, large lists time-sliced). Never called while another
320
+ * pass for the same `$for` is in flight (see `update`).
321
+ */
322
+ performUpdate(attribute, store, scene, state) {
266
323
  const { key, index, arrayExpr, trackExpr } = this.parseExpression(attribute.content);
267
324
  const newArray = this.resolveArray(store, arrayExpr, attribute.content);
268
325
  const owner = attribute.owner;
269
326
  const currentChildren = [...owner.children];
270
327
  attribute.value = newArray;
328
+ const attached = this.isAttached(owner);
271
329
  const computeKey = this.makeKeyFn(key, index, trackExpr);
272
330
  const childByKey = /* @__PURE__ */ new Map();
273
331
  const remaining = /* @__PURE__ */ new Set();
@@ -278,7 +336,7 @@ var AreDirectiveFor = class extends AreDirective {
278
336
  childByKey.set(k, child);
279
337
  remaining.add(child);
280
338
  }
281
- const newOnes = [];
339
+ const toCreate = [];
282
340
  for (let i = 0; i < newArray.length; i++) {
283
341
  const item = newArray[i];
284
342
  const k = computeKey(item, i);
@@ -296,20 +354,73 @@ var AreDirectiveFor = class extends AreDirective {
296
354
  [index || "index"]: i
297
355
  };
298
356
  } else {
299
- const itemNode = this.spawnItemNode(attribute.template, owner, key, index, item, i);
300
- newOnes.push(itemNode);
357
+ toCreate.push({ item, idx: i });
301
358
  }
302
359
  }
303
360
  for (const child of remaining) {
304
- child.unmount();
361
+ if (attached) child.unmount();
305
362
  owner.removeChild(child);
306
363
  }
307
- for (const child of newOnes) {
364
+ const createItem = (desc) => {
365
+ const child = this.spawnItemNode(attribute.template, owner, key, index, desc.item, desc.idx);
308
366
  child.transform();
309
367
  child.compile();
310
- child.mount();
368
+ if (attached) child.mount();
369
+ };
370
+ if (toCreate.length <= AreDirectiveFor.SYNC_THRESHOLD) {
371
+ for (const desc of toCreate) createItem(desc);
372
+ return this.finishUpdate(attribute, store, scene, state);
373
+ }
374
+ state.running = true;
375
+ let cursor = 0;
376
+ const processChunk = () => {
377
+ try {
378
+ const start = AreSchedulerHelper.now();
379
+ while (cursor < toCreate.length) {
380
+ createItem(toCreate[cursor]);
381
+ cursor++;
382
+ if (AreSchedulerHelper.now() - start >= AreDirectiveFor.CHUNK_BUDGET_MS) break;
383
+ }
384
+ } catch (error) {
385
+ state.running = false;
386
+ state.pending = false;
387
+ throw error;
388
+ }
389
+ if (cursor < toCreate.length) {
390
+ return new Promise((resolve) => {
391
+ AreSchedulerHelper.scheduleMacrotask(() => resolve(processChunk()));
392
+ });
393
+ }
394
+ return this.finishUpdate(attribute, store, scene, state);
395
+ };
396
+ return processChunk();
397
+ }
398
+ /**
399
+ * Completes an update pass. If another update() arrived while a chunked
400
+ * render was streaming, run exactly one more pass now from the latest store
401
+ * value so the final DOM always reflects the most recent data.
402
+ */
403
+ finishUpdate(attribute, store, scene, state) {
404
+ state.running = false;
405
+ if (state.pending) {
406
+ state.pending = false;
407
+ return this.performUpdate(attribute, store, scene, state);
311
408
  }
312
409
  }
410
+ /**
411
+ * Walks the node's ancestor chain (inclusive) and reports whether the
412
+ * whole path is currently active — i.e. the subtree is actually rendered
413
+ * into the DOM. A single inactive ancestor scene (e.g. a `$if` whose
414
+ * condition is false) means the subtree is detached.
415
+ */
416
+ isAttached(node) {
417
+ let current = node;
418
+ while (current) {
419
+ if (current.scene?.isInactive) return false;
420
+ current = current.parent;
421
+ }
422
+ return true;
423
+ }
313
424
  // ─────────────────────────────────────────────────────────────────────────────
314
425
  // ── Helpers ──────────────────────────────────────────────────────────────────
315
426
  // ─────────────────────────────────────────────────────────────────────────────
@@ -454,6 +565,29 @@ var AreDirectiveFor = class extends AreDirective {
454
565
  return itemNode;
455
566
  }
456
567
  };
568
+ /**
569
+ * Lists whose number of NEW item nodes is at or below this threshold render
570
+ * fully synchronously — byte-for-byte the previous behavior. Typical UIs
571
+ * (menus, small tables) are therefore completely unaffected; only genuinely
572
+ * large lists pay the (tiny) scheduling cost to keep the main thread responsive.
573
+ */
574
+ AreDirectiveFor.SYNC_THRESHOLD = 100;
575
+ /**
576
+ * Per-chunk time budget (ms). During a large-list render we mount item nodes
577
+ * until this much time has elapsed, then yield to the browser so it can paint
578
+ * and process input before the next chunk. ~16ms targets one animation frame.
579
+ */
580
+ AreDirectiveFor.CHUNK_BUDGET_MS = 16;
581
+ /**
582
+ * Per-attribute serialization state. A new update() that arrives while a
583
+ * chunked render of the SAME `$for` is still in flight does NOT start a second
584
+ * concurrent pass (which could interleave mutations on the shared children
585
+ * list); instead it marks `pending` and the in-flight run re-runs once more
586
+ * with the latest data when it finishes. This guarantees the children list is
587
+ * only ever mutated by one pass at a time and the final state always reflects
588
+ * the most recent store value.
589
+ */
590
+ AreDirectiveFor.renderState = /* @__PURE__ */ new WeakMap();
457
591
  __decorateClass([
458
592
  AreDirective.Transform,
459
593
  __decorateParam(0, A_Inject(A_Caller)),
@@ -556,6 +690,79 @@ AreDirectiveIf = __decorateClass([
556
690
  }),
557
691
  AreDirective.Priority(2)
558
692
  ], AreDirectiveIf);
693
+ var HideElementInstruction = class extends AreMutation {
694
+ constructor(parent, props) {
695
+ if ("aseid" in props) {
696
+ super(props);
697
+ } else {
698
+ super(AreHTMLInstructions.HideElement, parent, props);
699
+ }
700
+ }
701
+ };
702
+ HideElementInstruction = __decorateClass([
703
+ A_Frame.Define({
704
+ namespace: "a-are-html",
705
+ description: 'Toggles the visibility of an existing element by setting its inline display to "none" on apply and restoring the previous inline display on revert. Used by the $show directive to hide/show an element without unmounting it, preserving its subtree, listeners and scene state.'
706
+ })
707
+ ], HideElementInstruction);
708
+ var AreDirectiveShow = class extends AreDirective {
709
+ transform(attribute, logger, ...args) {
710
+ logger.debug(`[Transform] directive $SHOW for <${attribute.owner.aseid.toString()}> (no structural change)`);
711
+ }
712
+ compile(attribute, store, scene, syntax, directiveContext, ...args) {
713
+ const visible = !!syntax.evaluate(attribute.content, store, {
714
+ ...directiveContext?.scope || {}
715
+ });
716
+ attribute.value = visible;
717
+ const hide = new HideElementInstruction(scene.host, {});
718
+ attribute.cache = hide;
719
+ if (!visible)
720
+ scene.plan(hide);
721
+ }
722
+ update(attribute, store, scene, syntax, directiveContext, ...args) {
723
+ const previous = !!attribute.value;
724
+ const next = !!syntax.evaluate(attribute.content, store, {
725
+ ...directiveContext?.scope || {}
726
+ });
727
+ attribute.value = next;
728
+ if (previous === next) return;
729
+ const hide = attribute.cache;
730
+ if (!hide) return;
731
+ if (next)
732
+ scene.unPlan(hide);
733
+ else
734
+ scene.plan(hide);
735
+ attribute.owner.interpret();
736
+ }
737
+ };
738
+ __decorateClass([
739
+ AreDirective.Transform,
740
+ __decorateParam(0, A_Inject(A_Caller)),
741
+ __decorateParam(1, A_Inject(A_Logger))
742
+ ], AreDirectiveShow.prototype, "transform", 1);
743
+ __decorateClass([
744
+ AreDirective.Compile,
745
+ __decorateParam(0, A_Inject(A_Caller)),
746
+ __decorateParam(1, A_Inject(AreStore)),
747
+ __decorateParam(2, A_Inject(AreScene)),
748
+ __decorateParam(3, A_Inject(AreSyntax)),
749
+ __decorateParam(4, A_Inject(AreDirectiveContext))
750
+ ], AreDirectiveShow.prototype, "compile", 1);
751
+ __decorateClass([
752
+ AreDirective.Update,
753
+ __decorateParam(0, A_Inject(A_Caller)),
754
+ __decorateParam(1, A_Inject(AreStore)),
755
+ __decorateParam(2, A_Inject(AreScene)),
756
+ __decorateParam(3, A_Inject(AreSyntax)),
757
+ __decorateParam(4, A_Inject(AreDirectiveContext))
758
+ ], AreDirectiveShow.prototype, "update", 1);
759
+ AreDirectiveShow = __decorateClass([
760
+ A_Frame.Define({
761
+ namespace: "a-are-html",
762
+ description: "Built-in $show directive. Toggles an element's visibility by flipping its inline display value based on a store expression, keeping the element mounted (subtree, listeners and scene state preserved) instead of unmounting it like $if."
763
+ }),
764
+ AreDirective.Priority(3)
765
+ ], AreDirectiveShow);
559
766
  var AddAttributeInstruction = class extends AreMutation {
560
767
  constructor(parent, props) {
561
768
  if ("aseid" in props) {
@@ -1486,6 +1693,19 @@ var AreHTMLInterpreter = class extends AreInterpreter {
1486
1693
  console.log("Error removing attribute:", error);
1487
1694
  }
1488
1695
  }
1696
+ hideElement(mutation, context) {
1697
+ const element = context.getElementByInstruction(mutation.parent);
1698
+ if (!element || element.nodeType !== Node.ELEMENT_NODE) return;
1699
+ const el = element;
1700
+ mutation.cache = el.style.display;
1701
+ el.style.display = "none";
1702
+ }
1703
+ showElement(mutation, context) {
1704
+ const element = context.getElementByInstruction(mutation.parent);
1705
+ if (!element || element.nodeType !== Node.ELEMENT_NODE) return;
1706
+ const el = element;
1707
+ el.style.display = mutation.payload?.display ?? mutation.cache ?? "";
1708
+ }
1489
1709
  addEventListener(mutation, context, store, syntax, directiveContext, logger) {
1490
1710
  const element = context.getElementByInstruction(mutation.parent);
1491
1711
  if (!element) {
@@ -1738,6 +1958,22 @@ __decorateClass([
1738
1958
  __decorateParam(0, A_Inject(A_Caller)),
1739
1959
  __decorateParam(1, A_Inject(AreHTMLEngineContext))
1740
1960
  ], AreHTMLInterpreter.prototype, "removeAttribute", 1);
1961
+ __decorateClass([
1962
+ A_Frame.Define({
1963
+ description: "Hide an element by setting inline display:none, caching its previous inline display value for restoration on revert."
1964
+ }),
1965
+ AreInterpreter.Apply(AreHTMLInstructions.HideElement),
1966
+ __decorateParam(0, A_Inject(A_Caller)),
1967
+ __decorateParam(1, A_Inject(AreHTMLEngineContext))
1968
+ ], AreHTMLInterpreter.prototype, "hideElement", 1);
1969
+ __decorateClass([
1970
+ A_Frame.Define({
1971
+ description: "Restore an element hidden by a HideElement instruction back to its previous inline display value."
1972
+ }),
1973
+ AreInterpreter.Revert(AreHTMLInstructions.HideElement),
1974
+ __decorateParam(0, A_Inject(A_Caller)),
1975
+ __decorateParam(1, A_Inject(AreHTMLEngineContext))
1976
+ ], AreHTMLInterpreter.prototype, "showElement", 1);
1741
1977
  __decorateClass([
1742
1978
  A_Frame.Define({
1743
1979
  description: "Add an event listener to an HTML element based on the provided mutation instruction."
@@ -1908,10 +2144,45 @@ var AreHTMLLifecycle = class extends AreLifecycle {
1908
2144
  logger?.debug(`[Mount] Component Trigger for <${node.aseid.entity}> with aseid :{${node.aseid.toString()}}`);
1909
2145
  if (scene.isInactive) return;
1910
2146
  node.interpret();
1911
- for (let i = 0; i < node.children.length; i++) {
1912
- const child = node.children[i];
1913
- child.mount();
1914
- }
2147
+ const stack = [];
2148
+ for (let i = node.children.length - 1; i >= 0; i--) {
2149
+ stack.push({ node: node.children[i], entered: false });
2150
+ }
2151
+ const step = () => {
2152
+ const frame = stack[stack.length - 1];
2153
+ const current = frame.node;
2154
+ if (frame.entered) {
2155
+ stack.pop();
2156
+ current.call(AreNodeFeatures.onAfterMount, current.scope);
2157
+ return;
2158
+ }
2159
+ frame.entered = true;
2160
+ current.call(AreNodeFeatures.onBeforeMount, current.scope);
2161
+ if (!current.scene.isInactive) {
2162
+ current.interpret();
2163
+ for (let i = current.children.length - 1; i >= 0; i--) {
2164
+ stack.push({ node: current.children[i], entered: false });
2165
+ }
2166
+ }
2167
+ };
2168
+ const drive = () => {
2169
+ const start = AreSchedulerHelper.now();
2170
+ while (stack.length > 0) {
2171
+ step();
2172
+ if (stack.length > 0 && AreSchedulerHelper.now() - start >= AreHTMLLifecycle.MOUNT_BUDGET_MS) {
2173
+ return new Promise((resolve, reject) => {
2174
+ AreSchedulerHelper.scheduleMacrotask(() => {
2175
+ try {
2176
+ resolve(drive());
2177
+ } catch (error) {
2178
+ reject(error);
2179
+ }
2180
+ });
2181
+ });
2182
+ }
2183
+ }
2184
+ };
2185
+ return drive();
1915
2186
  }
1916
2187
  updateDirectiveAttribute(directive, scope, feature, logger, ...args) {
1917
2188
  if (directive.component) {
@@ -1921,6 +2192,13 @@ var AreHTMLLifecycle = class extends AreLifecycle {
1921
2192
  }
1922
2193
  }
1923
2194
  };
2195
+ /**
2196
+ * Per-chunk time budget (ms) for the time-sliced initial mount walk. While
2197
+ * mounting a large subtree we keep applying nodes until this much wall-clock
2198
+ * time has elapsed, then yield to the browser so it can paint and process
2199
+ * input before the next chunk. ~16ms targets a single animation frame.
2200
+ */
2201
+ AreHTMLLifecycle.MOUNT_BUDGET_MS = 16;
1924
2202
  __decorateClass([
1925
2203
  AreLifecycle.Init(AreComponentNode),
1926
2204
  __decorateParam(0, A_Inject(A_Caller)),
@@ -2003,6 +2281,97 @@ AreHTMLTransformer = __decorateClass([
2003
2281
  description: "HTML-specific transformer extending AreTransformer. Handles directive-attribute structural rewrites before compilation \u2014 sorting directives by declared priority and expanding compound directive expressions \u2014 so the compiler receives a clean, ordered AreHTMLNode tree ready for instruction emission."
2004
2282
  })
2005
2283
  ], AreHTMLTransformer);
2284
+ var AreRootCache = class extends A_Fragment {
2285
+ constructor(limit = 10) {
2286
+ super({ name: "AreRootCache" });
2287
+ /**
2288
+ * rootId -> (component tag -> cache entry). The inner Map preserves
2289
+ * insertion order which is used as the LRU recency order: the first key is
2290
+ * the least-recently-used entry, the last key the most-recently-used.
2291
+ */
2292
+ this._cache = /* @__PURE__ */ new Map();
2293
+ this._limit = Math.max(0, Math.floor(limit));
2294
+ }
2295
+ /**
2296
+ * Maximum number of cached subtrees kept per root.
2297
+ */
2298
+ get limit() {
2299
+ return this._limit;
2300
+ }
2301
+ bucket(rootId) {
2302
+ let bucket = this._cache.get(rootId);
2303
+ if (!bucket) {
2304
+ bucket = /* @__PURE__ */ new Map();
2305
+ this._cache.set(rootId, bucket);
2306
+ }
2307
+ return bucket;
2308
+ }
2309
+ /**
2310
+ * Whether a subtree for the given component tag is currently cached.
2311
+ */
2312
+ has(rootId, tag) {
2313
+ return this.bucket(rootId).has(tag);
2314
+ }
2315
+ /**
2316
+ * Retrieve AND remove a cached subtree so it can become live again. Returns
2317
+ * `undefined` on a cache miss.
2318
+ */
2319
+ take(rootId, tag) {
2320
+ const bucket = this.bucket(rootId);
2321
+ const entry = bucket.get(tag);
2322
+ if (entry) {
2323
+ bucket.delete(tag);
2324
+ }
2325
+ return entry;
2326
+ }
2327
+ /**
2328
+ * Stash a detached subtree under the given component tag. Returns any entries
2329
+ * that were evicted to honour the LRU limit (or replaced for the same tag) so
2330
+ * the caller can `destroy()` them.
2331
+ */
2332
+ put(rootId, tag, entry) {
2333
+ const bucket = this.bucket(rootId);
2334
+ const evicted = [];
2335
+ const existing = bucket.get(tag);
2336
+ if (existing) {
2337
+ bucket.delete(tag);
2338
+ if (existing.node !== entry.node) {
2339
+ evicted.push(existing);
2340
+ }
2341
+ }
2342
+ bucket.set(tag, entry);
2343
+ while (bucket.size > this._limit) {
2344
+ const oldestKey = bucket.keys().next().value;
2345
+ if (oldestKey === void 0) {
2346
+ break;
2347
+ }
2348
+ const oldest = bucket.get(oldestKey);
2349
+ bucket.delete(oldestKey);
2350
+ evicted.push(oldest);
2351
+ }
2352
+ return evicted;
2353
+ }
2354
+ /**
2355
+ * Remove and return every cached entry for a root (e.g. on teardown) so the
2356
+ * caller can destroy them.
2357
+ */
2358
+ clear(rootId) {
2359
+ const bucket = this._cache.get(rootId);
2360
+ if (!bucket) {
2361
+ return [];
2362
+ }
2363
+ const entries = [...bucket.values()];
2364
+ bucket.clear();
2365
+ this._cache.delete(rootId);
2366
+ return entries;
2367
+ }
2368
+ };
2369
+ AreRootCache = __decorateClass([
2370
+ A_Frame.Define({
2371
+ namespace: "a-are-html",
2372
+ description: "AreRootCache is a fragment that keeps a small per-root LRU of previously rendered are-root subtrees. When an are-root swaps the component it displays, the outgoing subtree is stashed here (unmounted + detached, but not destroyed) so that routing back to it can re-inject the preserved scene instantly instead of rebuilding from scratch."
2373
+ })
2374
+ ], AreRootCache);
2006
2375
 
2007
2376
  // src/engine/AreHTML.engine.ts
2008
2377
  var AreHTMLEngine = class extends AreEngine {
@@ -2049,7 +2418,7 @@ var AreHTMLEngine = class extends AreEngine {
2049
2418
  ]
2050
2419
  });
2051
2420
  }
2052
- async init(scope, signalContext) {
2421
+ async init(scope, signalContext, rootCache) {
2053
2422
  this.package(scope, {
2054
2423
  context: new AreHTMLEngineContext({}),
2055
2424
  syntax: this.DefaultSyntax,
@@ -2063,6 +2432,10 @@ var AreHTMLEngine = class extends AreEngine {
2063
2432
  signalContext = new AreSignalsContext();
2064
2433
  scope.register(signalContext);
2065
2434
  }
2435
+ if (!rootCache) {
2436
+ rootCache = new AreRootCache();
2437
+ scope.register(rootCache);
2438
+ }
2066
2439
  }
2067
2440
  rootElementMatcher(source, from, to, build) {
2068
2441
  const rootTag = "are-root";
@@ -2172,7 +2545,8 @@ __decorateClass([
2172
2545
  before: /.*/
2173
2546
  }),
2174
2547
  __decorateParam(0, A_Inject(A_Scope)),
2175
- __decorateParam(1, A_Inject(AreSignalsContext))
2548
+ __decorateParam(1, A_Inject(AreSignalsContext)),
2549
+ __decorateParam(2, A_Inject(AreRootCache))
2176
2550
  ], AreHTMLEngine.prototype, "init", 1);
2177
2551
  AreHTMLEngine = __decorateClass([
2178
2552
  A_Frame.Define({
@@ -2181,7 +2555,7 @@ AreHTMLEngine = __decorateClass([
2181
2555
  })
2182
2556
  ], AreHTMLEngine);
2183
2557
  var AreRoot = class extends Are {
2184
- async template(root, logger, signalsContext) {
2558
+ async template(root, logger, signalsContext, signalState) {
2185
2559
  const rootId = root.id;
2186
2560
  if (signalsContext && !signalsContext.hasRoot(rootId)) {
2187
2561
  if (!root.content?.trim()) {
@@ -2193,26 +2567,9 @@ var AreRoot = class extends Are {
2193
2567
  }
2194
2568
  return;
2195
2569
  }
2196
- const currentRoute = AreRoute.default();
2197
- let componentName;
2198
- if (currentRoute) {
2199
- const initialVector = new A_SignalVector([currentRoute]);
2200
- let renderTarget = signalsContext?.findComponentByVector(rootId, initialVector);
2201
- if (!renderTarget) {
2202
- const signalsMeta = A_Context.meta(AreSignals);
2203
- const pool = signalsContext?.getComponentById(rootId);
2204
- const metaTarget = signalsMeta?.findComponentByVector(
2205
- initialVector,
2206
- pool?.length ? pool : void 0
2207
- );
2208
- if (metaTarget && (!pool?.length || pool.includes(metaTarget))) {
2209
- renderTarget = metaTarget;
2210
- }
2211
- }
2212
- if (renderTarget?.name) {
2213
- componentName = A_FormatterHelper.toKebabCase(renderTarget.name);
2214
- }
2215
- }
2570
+ const initialVector = this.buildInitialVector(signalState);
2571
+ const renderTarget = this.matchComponent(rootId, initialVector, signalsContext);
2572
+ let componentName = renderTarget?.name ? A_FormatterHelper.toKebabCase(renderTarget.name) : void 0;
2216
2573
  if (!componentName) {
2217
2574
  if (root.content?.trim()) {
2218
2575
  return;
@@ -2234,32 +2591,17 @@ var AreRoot = class extends Are {
2234
2591
  }
2235
2592
  root.setContent(`<${componentName}></${componentName}>`);
2236
2593
  }
2237
- async onSignal(root, vector, logger, signalsContext) {
2594
+ async onSignal(root, vector, logger, signalsContext, cache) {
2238
2595
  const rootId = root.id;
2239
2596
  if (signalsContext && !signalsContext.hasRoot(rootId)) {
2240
2597
  return;
2241
2598
  }
2242
- let renderTarget = signalsContext?.findComponentByVector(rootId, vector);
2243
- if (!renderTarget) {
2244
- const signalsMeta = A_Context.meta(AreSignals);
2245
- const pool = signalsContext?.getComponentById(rootId);
2246
- const metaTarget = signalsMeta?.findComponentByVector(
2247
- vector,
2248
- pool?.length ? pool : void 0
2249
- );
2250
- if (metaTarget && (!pool?.length || pool.includes(metaTarget))) {
2251
- renderTarget = metaTarget;
2252
- }
2253
- }
2599
+ const renderTarget = this.matchComponent(rootId, vector, signalsContext);
2254
2600
  const def = signalsContext?.getDefault(rootId);
2255
2601
  const componentName = renderTarget?.name ? A_FormatterHelper.toKebabCase(renderTarget.name) : def?.name ? A_FormatterHelper.toKebabCase(def.name) : void 0;
2256
2602
  if (!componentName) {
2257
- for (let i = 0; i < root.children.length; i++) {
2258
- const child = root.children[i];
2259
- signalsContext?.unsubscribe(child);
2260
- child.unmount();
2261
- child.destroy();
2262
- root.removeChild(child);
2603
+ for (const child of [...root.children]) {
2604
+ this.stashChild(root, child, signalsContext, cache);
2263
2605
  }
2264
2606
  root.setContent("");
2265
2607
  return;
@@ -2268,13 +2610,14 @@ var AreRoot = class extends Are {
2268
2610
  if (currentChild?.type === componentName) {
2269
2611
  return;
2270
2612
  }
2613
+ for (const child of [...root.children]) {
2614
+ this.stashChild(root, child, signalsContext, cache);
2615
+ }
2271
2616
  root.setContent(`<${componentName}></${componentName}>`);
2272
- for (let i = 0; i < root.children.length; i++) {
2273
- const child = root.children[i];
2274
- signalsContext?.unsubscribe(child);
2275
- child.unmount();
2276
- child.destroy();
2277
- root.removeChild(child);
2617
+ const cached = cache?.take(root.id, componentName);
2618
+ if (cached) {
2619
+ this.restoreChild(root, cached, signalsContext);
2620
+ return;
2278
2621
  }
2279
2622
  root.tokenize();
2280
2623
  for (let i = 0; i < root.children.length; i++) {
@@ -2286,22 +2629,141 @@ var AreRoot = class extends Are {
2286
2629
  }
2287
2630
  child.transform();
2288
2631
  child.compile();
2289
- child.mount();
2632
+ await child.mount();
2633
+ }
2634
+ }
2635
+ /**
2636
+ * Resolves the component a vector should render for the given root, mirroring
2637
+ * the priority used everywhere in the routing system:
2638
+ * 1. Root-specific conditions registered on AreSignalsContext.
2639
+ * 2. The global AreSignalsMeta map, restricted to this outlet's pool.
2640
+ *
2641
+ * Passing the pool *into* the meta lookup is critical: without it, the first
2642
+ * globally matching component wins and may belong to a different outlet
2643
+ * (e.g. AisRequirementsPanel for the meta-outlet matching
2644
+ * AisEditorCursorScope) — the pool check would then reject it and the outlet
2645
+ * would fall back to its default, hiding a valid in-pool match (e.g.
2646
+ * AisDiagramTab matching AisSetPrimaryDisplay).
2647
+ *
2648
+ * Returns `undefined` when nothing matches — callers decide whether to use a
2649
+ * configured default, body content, or clear the outlet.
2650
+ */
2651
+ matchComponent(rootId, vector, signalsContext) {
2652
+ if (!vector) return void 0;
2653
+ let renderTarget = signalsContext?.findComponentByVector(rootId, vector);
2654
+ if (!renderTarget) {
2655
+ const signalsMeta = A_Context.meta(AreSignals);
2656
+ const pool = signalsContext?.getComponentById(rootId);
2657
+ const metaTarget = signalsMeta?.findComponentByVector(
2658
+ vector,
2659
+ pool?.length ? pool : void 0,
2660
+ rootId
2661
+ );
2662
+ if (metaTarget && (!pool?.length || pool.includes(metaTarget))) {
2663
+ renderTarget = metaTarget;
2664
+ }
2290
2665
  }
2666
+ return renderTarget;
2667
+ }
2668
+ /**
2669
+ * Builds the vector used for the INITIAL render. It is seeded from the
2670
+ * accumulated signal state (every signal dispatched on the bus so far) so a
2671
+ * freshly-mounted outlet reflects the live application state immediately,
2672
+ * not just on the next signal tick. The current URL route is appended when
2673
+ * no AreRoute is already present in the state, so route-driven outlets still
2674
+ * resolve on the very first paint (before AreRouteWatcher has dispatched).
2675
+ */
2676
+ buildInitialVector(signalState) {
2677
+ const signals = [];
2678
+ if (signalState) {
2679
+ for (const signal of signalState.toVector()) {
2680
+ if (signal) signals.push(signal);
2681
+ }
2682
+ }
2683
+ if (!signals.some((signal) => signal instanceof AreRoute)) {
2684
+ try {
2685
+ const currentRoute = AreRoute.default();
2686
+ if (currentRoute) signals.push(currentRoute);
2687
+ } catch {
2688
+ }
2689
+ }
2690
+ return new A_SignalVector(signals);
2691
+ }
2692
+ /**
2693
+ * Detach a displayed child subtree from the outlet and stash it in the cache
2694
+ * for fast re-injection later. The subtree is unmounted (its scene plan is
2695
+ * preserved) and deregistered from the root scope, but NOT destroyed. The
2696
+ * nodes that were subscribed to the signal bus are unsubscribed while cached
2697
+ * so the detached DOM never reacts to signals, and recorded so they can be
2698
+ * re-subscribed verbatim on restore.
2699
+ *
2700
+ * When no cache is available, or the LRU evicts an entry, the affected
2701
+ * subtree is fully destroyed.
2702
+ */
2703
+ stashChild(root, child, signalsContext, cache) {
2704
+ const tag = child.type;
2705
+ child.unmount();
2706
+ const subscribers = signalsContext ? this.collectSubscribers(child, signalsContext) : [];
2707
+ for (const node of subscribers) {
2708
+ signalsContext?.unsubscribe(node);
2709
+ }
2710
+ root.removeChild(child);
2711
+ if (!cache) {
2712
+ void child.destroy();
2713
+ return;
2714
+ }
2715
+ const evicted = cache.put(root.id, tag, { node: child, subscribers });
2716
+ for (const entry of evicted) {
2717
+ void entry.node.destroy();
2718
+ }
2719
+ }
2720
+ /**
2721
+ * Re-attach a cached subtree to the outlet and re-mount it from its preserved
2722
+ * scene plan, re-subscribing exactly the nodes that were subscribed before it
2723
+ * was cached.
2724
+ */
2725
+ restoreChild(root, entry, signalsContext) {
2726
+ const child = entry.node;
2727
+ root.addChild(child);
2728
+ for (const node of entry.subscribers) {
2729
+ signalsContext?.subscribe(node);
2730
+ }
2731
+ child.mount();
2732
+ }
2733
+ /**
2734
+ * Walk a subtree and collect the nodes currently registered as signal
2735
+ * subscribers. Mirrors the subscription performed at init time in
2736
+ * AreHTMLLifecycle (component nodes and root nodes) without depending on the
2737
+ * concrete node classes — it simply intersects the subtree with the live
2738
+ * subscriber registry.
2739
+ */
2740
+ collectSubscribers(node, signalsContext) {
2741
+ const result = [];
2742
+ const queue = [node];
2743
+ while (queue.length > 0) {
2744
+ const current = queue.shift();
2745
+ if (signalsContext.subscribers.has(current)) {
2746
+ result.push(current);
2747
+ }
2748
+ queue.push(...current.children);
2749
+ }
2750
+ return result;
2291
2751
  }
2292
2752
  };
2293
2753
  __decorateClass([
2294
2754
  Are.Template,
2295
2755
  __decorateParam(0, A_Inject(A_Caller)),
2296
2756
  __decorateParam(1, A_Inject(A_Logger)),
2297
- __decorateParam(2, A_Inject(AreSignalsContext))
2757
+ __decorateParam(2, A_Inject(AreSignalsContext)),
2758
+ __decorateParam(3, A_Inject(A_SignalState))
2298
2759
  ], AreRoot.prototype, "template", 1);
2299
2760
  __decorateClass([
2300
2761
  Are.Signal,
2301
2762
  __decorateParam(0, A_Inject(A_Caller)),
2302
2763
  __decorateParam(1, A_Inject(A_SignalVector)),
2303
2764
  __decorateParam(2, A_Inject(A_Logger)),
2304
- __decorateParam(3, A_Inject(AreSignalsContext))
2765
+ __decorateParam(3, A_Inject(AreSignalsContext)),
2766
+ __decorateParam(4, A_Inject(AreRootCache))
2305
2767
  ], AreRoot.prototype, "onSignal", 1);
2306
2768
  AreRoot = __decorateClass([
2307
2769
  A_Frame.Define({
@@ -2372,6 +2834,6 @@ AreRouteWatcher = __decorateClass([
2372
2834
  })
2373
2835
  ], AreRouteWatcher);
2374
2836
 
2375
- export { AddAttributeInstruction, AddElementInstruction, AddInterpolationInstruction, AddListenerInstruction, AddStyleInstruction, AddTextInstruction, AreBindingAttribute, AreComment, AreComponentNode, AreDirective, AreDirectiveAttribute, AreDirectiveContext, AreDirectiveFeatures, AreDirectiveFor, AreDirectiveIf, AreDirectiveMeta, AreEventAttribute, AreHTMLAttribute, AreHTMLCompiler, AreHTMLEngine, AreHTMLEngineContext, AreHTMLInstructions, AreHTMLInterpreter, AreHTMLLifecycle, AreHTMLNode, AreHTMLTokenizer, AreHTMLTransformer, AreInterpolation, AreRoot, AreRootNode, AreRoute, AreRouteWatcher, AreStaticAttribute, AreStyle, AreText, BOOLEAN_ATTRIBUTES, IDL_FORM_PROPERTIES, LISTENER_OPTION_MODIFIERS, SVG_ATTRIBUTE_NS, SVG_NAMESPACE, VOID_ELEMENTS, isBooleanAttribute, isIDLFormProperty, isVoidElement, normalizeClassValue, normalizeStyleValue, parseEventName, toDOMString };
2837
+ export { AddAttributeInstruction, AddElementInstruction, AddInterpolationInstruction, AddListenerInstruction, AddStyleInstruction, AddTextInstruction, AreBindingAttribute, AreComment, AreComponentNode, AreDirective, AreDirectiveAttribute, AreDirectiveContext, AreDirectiveFeatures, AreDirectiveFor, AreDirectiveIf, AreDirectiveMeta, AreDirectiveShow, AreEventAttribute, AreHTMLAttribute, AreHTMLCompiler, AreHTMLEngine, AreHTMLEngineContext, AreHTMLInstructions, AreHTMLInterpreter, AreHTMLLifecycle, AreHTMLNode, AreHTMLTokenizer, AreHTMLTransformer, AreInterpolation, AreRoot, AreRootCache, AreRootNode, AreRoute, AreRouteWatcher, AreStaticAttribute, AreStyle, AreText, BOOLEAN_ATTRIBUTES, HideElementInstruction, IDL_FORM_PROPERTIES, LISTENER_OPTION_MODIFIERS, SVG_ATTRIBUTE_NS, SVG_NAMESPACE, VOID_ELEMENTS, isBooleanAttribute, isIDLFormProperty, isVoidElement, normalizeClassValue, normalizeStyleValue, parseEventName, toDOMString };
2376
2838
  //# sourceMappingURL=index.mjs.map
2377
2839
  //# sourceMappingURL=index.mjs.map