@adaas/are-html 0.0.19 → 0.0.21

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 (74) hide show
  1. package/dist/browser/index.d.mts +161 -5
  2. package/dist/browser/index.mjs +357 -55
  3. package/dist/browser/index.mjs.map +1 -1
  4. package/dist/node/directives/AreDirectiveFor.directive.d.mts +7 -0
  5. package/dist/node/directives/AreDirectiveFor.directive.d.ts +7 -0
  6. package/dist/node/directives/AreDirectiveFor.directive.js +17 -2
  7. package/dist/node/directives/AreDirectiveFor.directive.js.map +1 -1
  8. package/dist/node/directives/AreDirectiveFor.directive.mjs +17 -2
  9. package/dist/node/directives/AreDirectiveFor.directive.mjs.map +1 -1
  10. package/dist/node/directives/AreDirectiveShow.directive.d.mts +32 -0
  11. package/dist/node/directives/AreDirectiveShow.directive.d.ts +32 -0
  12. package/dist/node/directives/AreDirectiveShow.directive.js +81 -0
  13. package/dist/node/directives/AreDirectiveShow.directive.js.map +1 -0
  14. package/dist/node/directives/AreDirectiveShow.directive.mjs +71 -0
  15. package/dist/node/directives/AreDirectiveShow.directive.mjs.map +1 -0
  16. package/dist/node/engine/AreHTML.engine.d.mts +2 -1
  17. package/dist/node/engine/AreHTML.engine.d.ts +2 -1
  18. package/dist/node/engine/AreHTML.engine.js +8 -2
  19. package/dist/node/engine/AreHTML.engine.js.map +1 -1
  20. package/dist/node/engine/AreHTML.engine.mjs +8 -2
  21. package/dist/node/engine/AreHTML.engine.mjs.map +1 -1
  22. package/dist/node/engine/AreHTML.interpreter.d.mts +3 -0
  23. package/dist/node/engine/AreHTML.interpreter.d.ts +3 -0
  24. package/dist/node/engine/AreHTML.interpreter.js +29 -0
  25. package/dist/node/engine/AreHTML.interpreter.js.map +1 -1
  26. package/dist/node/engine/AreHTML.interpreter.mjs +29 -0
  27. package/dist/node/engine/AreHTML.interpreter.mjs.map +1 -1
  28. package/dist/node/index.d.mts +4 -1
  29. package/dist/node/index.d.ts +4 -1
  30. package/dist/node/index.js +21 -0
  31. package/dist/node/index.mjs +3 -0
  32. package/dist/node/instructions/AreHTML.instructions.constants.d.mts +1 -0
  33. package/dist/node/instructions/AreHTML.instructions.constants.d.ts +1 -0
  34. package/dist/node/instructions/AreHTML.instructions.constants.js +2 -1
  35. package/dist/node/instructions/AreHTML.instructions.constants.js.map +1 -1
  36. package/dist/node/instructions/AreHTML.instructions.constants.mjs +2 -1
  37. package/dist/node/instructions/AreHTML.instructions.constants.mjs.map +1 -1
  38. package/dist/node/instructions/AreHTML.instructions.types.d.mts +9 -1
  39. package/dist/node/instructions/AreHTML.instructions.types.d.ts +9 -1
  40. package/dist/node/instructions/HideElement.instruction.d.mts +13 -0
  41. package/dist/node/instructions/HideElement.instruction.d.ts +13 -0
  42. package/dist/node/instructions/HideElement.instruction.js +31 -0
  43. package/dist/node/instructions/HideElement.instruction.js.map +1 -0
  44. package/dist/node/instructions/HideElement.instruction.mjs +24 -0
  45. package/dist/node/instructions/HideElement.instruction.mjs.map +1 -0
  46. package/dist/node/lib/AreRoot/AreRoot.component.d.mts +57 -3
  47. package/dist/node/lib/AreRoot/AreRoot.component.d.ts +57 -3
  48. package/dist/node/lib/AreRoot/AreRoot.component.js +137 -48
  49. package/dist/node/lib/AreRoot/AreRoot.component.js.map +1 -1
  50. package/dist/node/lib/AreRoot/AreRoot.component.mjs +139 -50
  51. package/dist/node/lib/AreRoot/AreRoot.component.mjs.map +1 -1
  52. package/dist/node/lib/AreRoot/AreRootCache.context.d.mts +58 -0
  53. package/dist/node/lib/AreRoot/AreRootCache.context.d.ts +58 -0
  54. package/dist/node/lib/AreRoot/AreRootCache.context.js +106 -0
  55. package/dist/node/lib/AreRoot/AreRootCache.context.js.map +1 -0
  56. package/dist/node/lib/AreRoot/AreRootCache.context.mjs +99 -0
  57. package/dist/node/lib/AreRoot/AreRootCache.context.mjs.map +1 -0
  58. package/examples/jumpstart/dist/index.html +1 -1
  59. package/examples/jumpstart/dist/{mq1a0fv0-ccgtz6.js → mq7hqrxy-4kus50.js} +629 -433
  60. package/examples/signal-routing/dist/index.html +1 -1
  61. package/examples/signal-routing/dist/{mq1bzrik-4lec86.js → mq7k53th-qiwy4x.js} +903 -486
  62. package/examples/signal-routing/src/components/SettingsPage.component.ts +39 -0
  63. package/examples/signal-routing/src/concept.ts +2 -0
  64. package/package.json +9 -9
  65. package/src/directives/AreDirectiveFor.directive.ts +44 -2
  66. package/src/directives/AreDirectiveShow.directive.ts +127 -0
  67. package/src/engine/AreHTML.engine.ts +11 -1
  68. package/src/engine/AreHTML.interpreter.ts +50 -0
  69. package/src/index.ts +3 -0
  70. package/src/instructions/AreHTML.instructions.constants.ts +1 -0
  71. package/src/instructions/AreHTML.instructions.types.ts +9 -0
  72. package/src/instructions/HideElement.instruction.ts +29 -0
  73. package/src/lib/AreRoot/AreRoot.component.ts +201 -71
  74. package/src/lib/AreRoot/AreRootCache.context.ts +133 -0
@@ -5,7 +5,7 @@ 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
@@ -268,6 +269,7 @@ var AreDirectiveFor = class extends AreDirective {
268
269
  const owner = attribute.owner;
269
270
  const currentChildren = [...owner.children];
270
271
  attribute.value = newArray;
272
+ const attached = this.isAttached(owner);
271
273
  const computeKey = this.makeKeyFn(key, index, trackExpr);
272
274
  const childByKey = /* @__PURE__ */ new Map();
273
275
  const remaining = /* @__PURE__ */ new Set();
@@ -301,15 +303,29 @@ var AreDirectiveFor = class extends AreDirective {
301
303
  }
302
304
  }
303
305
  for (const child of remaining) {
304
- child.unmount();
306
+ if (attached) child.unmount();
305
307
  owner.removeChild(child);
306
308
  }
307
309
  for (const child of newOnes) {
308
310
  child.transform();
309
311
  child.compile();
310
- child.mount();
312
+ if (attached) child.mount();
311
313
  }
312
314
  }
315
+ /**
316
+ * Walks the node's ancestor chain (inclusive) and reports whether the
317
+ * whole path is currently active — i.e. the subtree is actually rendered
318
+ * into the DOM. A single inactive ancestor scene (e.g. a `$if` whose
319
+ * condition is false) means the subtree is detached.
320
+ */
321
+ isAttached(node) {
322
+ let current = node;
323
+ while (current) {
324
+ if (current.scene?.isInactive) return false;
325
+ current = current.parent;
326
+ }
327
+ return true;
328
+ }
313
329
  // ─────────────────────────────────────────────────────────────────────────────
314
330
  // ── Helpers ──────────────────────────────────────────────────────────────────
315
331
  // ─────────────────────────────────────────────────────────────────────────────
@@ -556,6 +572,79 @@ AreDirectiveIf = __decorateClass([
556
572
  }),
557
573
  AreDirective.Priority(2)
558
574
  ], AreDirectiveIf);
575
+ var HideElementInstruction = class extends AreMutation {
576
+ constructor(parent, props) {
577
+ if ("aseid" in props) {
578
+ super(props);
579
+ } else {
580
+ super(AreHTMLInstructions.HideElement, parent, props);
581
+ }
582
+ }
583
+ };
584
+ HideElementInstruction = __decorateClass([
585
+ A_Frame.Define({
586
+ namespace: "a-are-html",
587
+ 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.'
588
+ })
589
+ ], HideElementInstruction);
590
+ var AreDirectiveShow = class extends AreDirective {
591
+ transform(attribute, logger, ...args) {
592
+ logger.debug(`[Transform] directive $SHOW for <${attribute.owner.aseid.toString()}> (no structural change)`);
593
+ }
594
+ compile(attribute, store, scene, syntax, directiveContext, ...args) {
595
+ const visible = !!syntax.evaluate(attribute.content, store, {
596
+ ...directiveContext?.scope || {}
597
+ });
598
+ attribute.value = visible;
599
+ const hide = new HideElementInstruction(scene.host, {});
600
+ attribute.cache = hide;
601
+ if (!visible)
602
+ scene.plan(hide);
603
+ }
604
+ update(attribute, store, scene, syntax, directiveContext, ...args) {
605
+ const previous = !!attribute.value;
606
+ const next = !!syntax.evaluate(attribute.content, store, {
607
+ ...directiveContext?.scope || {}
608
+ });
609
+ attribute.value = next;
610
+ if (previous === next) return;
611
+ const hide = attribute.cache;
612
+ if (!hide) return;
613
+ if (next)
614
+ scene.unPlan(hide);
615
+ else
616
+ scene.plan(hide);
617
+ attribute.owner.interpret();
618
+ }
619
+ };
620
+ __decorateClass([
621
+ AreDirective.Transform,
622
+ __decorateParam(0, A_Inject(A_Caller)),
623
+ __decorateParam(1, A_Inject(A_Logger))
624
+ ], AreDirectiveShow.prototype, "transform", 1);
625
+ __decorateClass([
626
+ AreDirective.Compile,
627
+ __decorateParam(0, A_Inject(A_Caller)),
628
+ __decorateParam(1, A_Inject(AreStore)),
629
+ __decorateParam(2, A_Inject(AreScene)),
630
+ __decorateParam(3, A_Inject(AreSyntax)),
631
+ __decorateParam(4, A_Inject(AreDirectiveContext))
632
+ ], AreDirectiveShow.prototype, "compile", 1);
633
+ __decorateClass([
634
+ AreDirective.Update,
635
+ __decorateParam(0, A_Inject(A_Caller)),
636
+ __decorateParam(1, A_Inject(AreStore)),
637
+ __decorateParam(2, A_Inject(AreScene)),
638
+ __decorateParam(3, A_Inject(AreSyntax)),
639
+ __decorateParam(4, A_Inject(AreDirectiveContext))
640
+ ], AreDirectiveShow.prototype, "update", 1);
641
+ AreDirectiveShow = __decorateClass([
642
+ A_Frame.Define({
643
+ namespace: "a-are-html",
644
+ 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."
645
+ }),
646
+ AreDirective.Priority(3)
647
+ ], AreDirectiveShow);
559
648
  var AddAttributeInstruction = class extends AreMutation {
560
649
  constructor(parent, props) {
561
650
  if ("aseid" in props) {
@@ -1486,6 +1575,19 @@ var AreHTMLInterpreter = class extends AreInterpreter {
1486
1575
  console.log("Error removing attribute:", error);
1487
1576
  }
1488
1577
  }
1578
+ hideElement(mutation, context) {
1579
+ const element = context.getElementByInstruction(mutation.parent);
1580
+ if (!element || element.nodeType !== Node.ELEMENT_NODE) return;
1581
+ const el = element;
1582
+ mutation.cache = el.style.display;
1583
+ el.style.display = "none";
1584
+ }
1585
+ showElement(mutation, context) {
1586
+ const element = context.getElementByInstruction(mutation.parent);
1587
+ if (!element || element.nodeType !== Node.ELEMENT_NODE) return;
1588
+ const el = element;
1589
+ el.style.display = mutation.payload?.display ?? mutation.cache ?? "";
1590
+ }
1489
1591
  addEventListener(mutation, context, store, syntax, directiveContext, logger) {
1490
1592
  const element = context.getElementByInstruction(mutation.parent);
1491
1593
  if (!element) {
@@ -1738,6 +1840,22 @@ __decorateClass([
1738
1840
  __decorateParam(0, A_Inject(A_Caller)),
1739
1841
  __decorateParam(1, A_Inject(AreHTMLEngineContext))
1740
1842
  ], AreHTMLInterpreter.prototype, "removeAttribute", 1);
1843
+ __decorateClass([
1844
+ A_Frame.Define({
1845
+ description: "Hide an element by setting inline display:none, caching its previous inline display value for restoration on revert."
1846
+ }),
1847
+ AreInterpreter.Apply(AreHTMLInstructions.HideElement),
1848
+ __decorateParam(0, A_Inject(A_Caller)),
1849
+ __decorateParam(1, A_Inject(AreHTMLEngineContext))
1850
+ ], AreHTMLInterpreter.prototype, "hideElement", 1);
1851
+ __decorateClass([
1852
+ A_Frame.Define({
1853
+ description: "Restore an element hidden by a HideElement instruction back to its previous inline display value."
1854
+ }),
1855
+ AreInterpreter.Revert(AreHTMLInstructions.HideElement),
1856
+ __decorateParam(0, A_Inject(A_Caller)),
1857
+ __decorateParam(1, A_Inject(AreHTMLEngineContext))
1858
+ ], AreHTMLInterpreter.prototype, "showElement", 1);
1741
1859
  __decorateClass([
1742
1860
  A_Frame.Define({
1743
1861
  description: "Add an event listener to an HTML element based on the provided mutation instruction."
@@ -2003,6 +2121,97 @@ AreHTMLTransformer = __decorateClass([
2003
2121
  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
2122
  })
2005
2123
  ], AreHTMLTransformer);
2124
+ var AreRootCache = class extends A_Fragment {
2125
+ constructor(limit = 10) {
2126
+ super({ name: "AreRootCache" });
2127
+ /**
2128
+ * rootId -> (component tag -> cache entry). The inner Map preserves
2129
+ * insertion order which is used as the LRU recency order: the first key is
2130
+ * the least-recently-used entry, the last key the most-recently-used.
2131
+ */
2132
+ this._cache = /* @__PURE__ */ new Map();
2133
+ this._limit = Math.max(0, Math.floor(limit));
2134
+ }
2135
+ /**
2136
+ * Maximum number of cached subtrees kept per root.
2137
+ */
2138
+ get limit() {
2139
+ return this._limit;
2140
+ }
2141
+ bucket(rootId) {
2142
+ let bucket = this._cache.get(rootId);
2143
+ if (!bucket) {
2144
+ bucket = /* @__PURE__ */ new Map();
2145
+ this._cache.set(rootId, bucket);
2146
+ }
2147
+ return bucket;
2148
+ }
2149
+ /**
2150
+ * Whether a subtree for the given component tag is currently cached.
2151
+ */
2152
+ has(rootId, tag) {
2153
+ return this.bucket(rootId).has(tag);
2154
+ }
2155
+ /**
2156
+ * Retrieve AND remove a cached subtree so it can become live again. Returns
2157
+ * `undefined` on a cache miss.
2158
+ */
2159
+ take(rootId, tag) {
2160
+ const bucket = this.bucket(rootId);
2161
+ const entry = bucket.get(tag);
2162
+ if (entry) {
2163
+ bucket.delete(tag);
2164
+ }
2165
+ return entry;
2166
+ }
2167
+ /**
2168
+ * Stash a detached subtree under the given component tag. Returns any entries
2169
+ * that were evicted to honour the LRU limit (or replaced for the same tag) so
2170
+ * the caller can `destroy()` them.
2171
+ */
2172
+ put(rootId, tag, entry) {
2173
+ const bucket = this.bucket(rootId);
2174
+ const evicted = [];
2175
+ const existing = bucket.get(tag);
2176
+ if (existing) {
2177
+ bucket.delete(tag);
2178
+ if (existing.node !== entry.node) {
2179
+ evicted.push(existing);
2180
+ }
2181
+ }
2182
+ bucket.set(tag, entry);
2183
+ while (bucket.size > this._limit) {
2184
+ const oldestKey = bucket.keys().next().value;
2185
+ if (oldestKey === void 0) {
2186
+ break;
2187
+ }
2188
+ const oldest = bucket.get(oldestKey);
2189
+ bucket.delete(oldestKey);
2190
+ evicted.push(oldest);
2191
+ }
2192
+ return evicted;
2193
+ }
2194
+ /**
2195
+ * Remove and return every cached entry for a root (e.g. on teardown) so the
2196
+ * caller can destroy them.
2197
+ */
2198
+ clear(rootId) {
2199
+ const bucket = this._cache.get(rootId);
2200
+ if (!bucket) {
2201
+ return [];
2202
+ }
2203
+ const entries = [...bucket.values()];
2204
+ bucket.clear();
2205
+ this._cache.delete(rootId);
2206
+ return entries;
2207
+ }
2208
+ };
2209
+ AreRootCache = __decorateClass([
2210
+ A_Frame.Define({
2211
+ namespace: "a-are-html",
2212
+ 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."
2213
+ })
2214
+ ], AreRootCache);
2006
2215
 
2007
2216
  // src/engine/AreHTML.engine.ts
2008
2217
  var AreHTMLEngine = class extends AreEngine {
@@ -2049,7 +2258,7 @@ var AreHTMLEngine = class extends AreEngine {
2049
2258
  ]
2050
2259
  });
2051
2260
  }
2052
- async init(scope, signalContext) {
2261
+ async init(scope, signalContext, rootCache) {
2053
2262
  this.package(scope, {
2054
2263
  context: new AreHTMLEngineContext({}),
2055
2264
  syntax: this.DefaultSyntax,
@@ -2063,6 +2272,10 @@ var AreHTMLEngine = class extends AreEngine {
2063
2272
  signalContext = new AreSignalsContext();
2064
2273
  scope.register(signalContext);
2065
2274
  }
2275
+ if (!rootCache) {
2276
+ rootCache = new AreRootCache();
2277
+ scope.register(rootCache);
2278
+ }
2066
2279
  }
2067
2280
  rootElementMatcher(source, from, to, build) {
2068
2281
  const rootTag = "are-root";
@@ -2172,7 +2385,8 @@ __decorateClass([
2172
2385
  before: /.*/
2173
2386
  }),
2174
2387
  __decorateParam(0, A_Inject(A_Scope)),
2175
- __decorateParam(1, A_Inject(AreSignalsContext))
2388
+ __decorateParam(1, A_Inject(AreSignalsContext)),
2389
+ __decorateParam(2, A_Inject(AreRootCache))
2176
2390
  ], AreHTMLEngine.prototype, "init", 1);
2177
2391
  AreHTMLEngine = __decorateClass([
2178
2392
  A_Frame.Define({
@@ -2181,7 +2395,7 @@ AreHTMLEngine = __decorateClass([
2181
2395
  })
2182
2396
  ], AreHTMLEngine);
2183
2397
  var AreRoot = class extends Are {
2184
- async template(root, logger, signalsContext) {
2398
+ async template(root, logger, signalsContext, signalState) {
2185
2399
  const rootId = root.id;
2186
2400
  if (signalsContext && !signalsContext.hasRoot(rootId)) {
2187
2401
  if (!root.content?.trim()) {
@@ -2193,26 +2407,9 @@ var AreRoot = class extends Are {
2193
2407
  }
2194
2408
  return;
2195
2409
  }
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
- }
2410
+ const initialVector = this.buildInitialVector(signalState);
2411
+ const renderTarget = this.matchComponent(rootId, initialVector, signalsContext);
2412
+ let componentName = renderTarget?.name ? A_FormatterHelper.toKebabCase(renderTarget.name) : void 0;
2216
2413
  if (!componentName) {
2217
2414
  if (root.content?.trim()) {
2218
2415
  return;
@@ -2234,32 +2431,17 @@ var AreRoot = class extends Are {
2234
2431
  }
2235
2432
  root.setContent(`<${componentName}></${componentName}>`);
2236
2433
  }
2237
- async onSignal(root, vector, logger, signalsContext) {
2434
+ async onSignal(root, vector, logger, signalsContext, cache) {
2238
2435
  const rootId = root.id;
2239
2436
  if (signalsContext && !signalsContext.hasRoot(rootId)) {
2240
2437
  return;
2241
2438
  }
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
- }
2439
+ const renderTarget = this.matchComponent(rootId, vector, signalsContext);
2254
2440
  const def = signalsContext?.getDefault(rootId);
2255
2441
  const componentName = renderTarget?.name ? A_FormatterHelper.toKebabCase(renderTarget.name) : def?.name ? A_FormatterHelper.toKebabCase(def.name) : void 0;
2256
2442
  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);
2443
+ for (const child of [...root.children]) {
2444
+ this.stashChild(root, child, signalsContext, cache);
2263
2445
  }
2264
2446
  root.setContent("");
2265
2447
  return;
@@ -2268,13 +2450,14 @@ var AreRoot = class extends Are {
2268
2450
  if (currentChild?.type === componentName) {
2269
2451
  return;
2270
2452
  }
2453
+ for (const child of [...root.children]) {
2454
+ this.stashChild(root, child, signalsContext, cache);
2455
+ }
2271
2456
  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);
2457
+ const cached = cache?.take(root.id, componentName);
2458
+ if (cached) {
2459
+ this.restoreChild(root, cached, signalsContext);
2460
+ return;
2278
2461
  }
2279
2462
  root.tokenize();
2280
2463
  for (let i = 0; i < root.children.length; i++) {
@@ -2289,19 +2472,138 @@ var AreRoot = class extends Are {
2289
2472
  child.mount();
2290
2473
  }
2291
2474
  }
2475
+ /**
2476
+ * Resolves the component a vector should render for the given root, mirroring
2477
+ * the priority used everywhere in the routing system:
2478
+ * 1. Root-specific conditions registered on AreSignalsContext.
2479
+ * 2. The global AreSignalsMeta map, restricted to this outlet's pool.
2480
+ *
2481
+ * Passing the pool *into* the meta lookup is critical: without it, the first
2482
+ * globally matching component wins and may belong to a different outlet
2483
+ * (e.g. AisRequirementsPanel for the meta-outlet matching
2484
+ * AisEditorCursorScope) — the pool check would then reject it and the outlet
2485
+ * would fall back to its default, hiding a valid in-pool match (e.g.
2486
+ * AisDiagramTab matching AisSetPrimaryDisplay).
2487
+ *
2488
+ * Returns `undefined` when nothing matches — callers decide whether to use a
2489
+ * configured default, body content, or clear the outlet.
2490
+ */
2491
+ matchComponent(rootId, vector, signalsContext) {
2492
+ if (!vector) return void 0;
2493
+ let renderTarget = signalsContext?.findComponentByVector(rootId, vector);
2494
+ if (!renderTarget) {
2495
+ const signalsMeta = A_Context.meta(AreSignals);
2496
+ const pool = signalsContext?.getComponentById(rootId);
2497
+ const metaTarget = signalsMeta?.findComponentByVector(
2498
+ vector,
2499
+ pool?.length ? pool : void 0,
2500
+ rootId
2501
+ );
2502
+ if (metaTarget && (!pool?.length || pool.includes(metaTarget))) {
2503
+ renderTarget = metaTarget;
2504
+ }
2505
+ }
2506
+ return renderTarget;
2507
+ }
2508
+ /**
2509
+ * Builds the vector used for the INITIAL render. It is seeded from the
2510
+ * accumulated signal state (every signal dispatched on the bus so far) so a
2511
+ * freshly-mounted outlet reflects the live application state immediately,
2512
+ * not just on the next signal tick. The current URL route is appended when
2513
+ * no AreRoute is already present in the state, so route-driven outlets still
2514
+ * resolve on the very first paint (before AreRouteWatcher has dispatched).
2515
+ */
2516
+ buildInitialVector(signalState) {
2517
+ const signals = [];
2518
+ if (signalState) {
2519
+ for (const signal of signalState.toVector()) {
2520
+ if (signal) signals.push(signal);
2521
+ }
2522
+ }
2523
+ if (!signals.some((signal) => signal instanceof AreRoute)) {
2524
+ try {
2525
+ const currentRoute = AreRoute.default();
2526
+ if (currentRoute) signals.push(currentRoute);
2527
+ } catch {
2528
+ }
2529
+ }
2530
+ return new A_SignalVector(signals);
2531
+ }
2532
+ /**
2533
+ * Detach a displayed child subtree from the outlet and stash it in the cache
2534
+ * for fast re-injection later. The subtree is unmounted (its scene plan is
2535
+ * preserved) and deregistered from the root scope, but NOT destroyed. The
2536
+ * nodes that were subscribed to the signal bus are unsubscribed while cached
2537
+ * so the detached DOM never reacts to signals, and recorded so they can be
2538
+ * re-subscribed verbatim on restore.
2539
+ *
2540
+ * When no cache is available, or the LRU evicts an entry, the affected
2541
+ * subtree is fully destroyed.
2542
+ */
2543
+ stashChild(root, child, signalsContext, cache) {
2544
+ const tag = child.type;
2545
+ child.unmount();
2546
+ const subscribers = signalsContext ? this.collectSubscribers(child, signalsContext) : [];
2547
+ for (const node of subscribers) {
2548
+ signalsContext?.unsubscribe(node);
2549
+ }
2550
+ root.removeChild(child);
2551
+ if (!cache) {
2552
+ void child.destroy();
2553
+ return;
2554
+ }
2555
+ const evicted = cache.put(root.id, tag, { node: child, subscribers });
2556
+ for (const entry of evicted) {
2557
+ void entry.node.destroy();
2558
+ }
2559
+ }
2560
+ /**
2561
+ * Re-attach a cached subtree to the outlet and re-mount it from its preserved
2562
+ * scene plan, re-subscribing exactly the nodes that were subscribed before it
2563
+ * was cached.
2564
+ */
2565
+ restoreChild(root, entry, signalsContext) {
2566
+ const child = entry.node;
2567
+ root.addChild(child);
2568
+ for (const node of entry.subscribers) {
2569
+ signalsContext?.subscribe(node);
2570
+ }
2571
+ child.mount();
2572
+ }
2573
+ /**
2574
+ * Walk a subtree and collect the nodes currently registered as signal
2575
+ * subscribers. Mirrors the subscription performed at init time in
2576
+ * AreHTMLLifecycle (component nodes and root nodes) without depending on the
2577
+ * concrete node classes — it simply intersects the subtree with the live
2578
+ * subscriber registry.
2579
+ */
2580
+ collectSubscribers(node, signalsContext) {
2581
+ const result = [];
2582
+ const queue = [node];
2583
+ while (queue.length > 0) {
2584
+ const current = queue.shift();
2585
+ if (signalsContext.subscribers.has(current)) {
2586
+ result.push(current);
2587
+ }
2588
+ queue.push(...current.children);
2589
+ }
2590
+ return result;
2591
+ }
2292
2592
  };
2293
2593
  __decorateClass([
2294
2594
  Are.Template,
2295
2595
  __decorateParam(0, A_Inject(A_Caller)),
2296
2596
  __decorateParam(1, A_Inject(A_Logger)),
2297
- __decorateParam(2, A_Inject(AreSignalsContext))
2597
+ __decorateParam(2, A_Inject(AreSignalsContext)),
2598
+ __decorateParam(3, A_Inject(A_SignalState))
2298
2599
  ], AreRoot.prototype, "template", 1);
2299
2600
  __decorateClass([
2300
2601
  Are.Signal,
2301
2602
  __decorateParam(0, A_Inject(A_Caller)),
2302
2603
  __decorateParam(1, A_Inject(A_SignalVector)),
2303
2604
  __decorateParam(2, A_Inject(A_Logger)),
2304
- __decorateParam(3, A_Inject(AreSignalsContext))
2605
+ __decorateParam(3, A_Inject(AreSignalsContext)),
2606
+ __decorateParam(4, A_Inject(AreRootCache))
2305
2607
  ], AreRoot.prototype, "onSignal", 1);
2306
2608
  AreRoot = __decorateClass([
2307
2609
  A_Frame.Define({
@@ -2372,6 +2674,6 @@ AreRouteWatcher = __decorateClass([
2372
2674
  })
2373
2675
  ], AreRouteWatcher);
2374
2676
 
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 };
2677
+ 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
2678
  //# sourceMappingURL=index.mjs.map
2377
2679
  //# sourceMappingURL=index.mjs.map