mnfst 0.5.261 → 0.5.263

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.
@@ -62,6 +62,23 @@
62
62
  if (!window.ManifestUI) {
63
63
  window.ManifestUI = {
64
64
  /* Loaded data-source names only — never force-loads others to scan them. */
65
+ // One cached evaluator per expression: each Alpine.evaluate would register a cleanup on <body>, never run
66
+ _eval(expr) {
67
+ const A = window.Alpine;
68
+ // Magics registered after an evaluator was built stay invisible to it, so a registration drops the cache
69
+ if (!A.magic.__manifestMagicCounted) {
70
+ const magic = A.magic;
71
+ A.magic = function () { window.__manifestMagicVersion = (window.__manifestMagicVersion || 0) + 1; return magic.apply(this, arguments); };
72
+ A.magic.__manifestMagicCounted = true;
73
+ }
74
+ const version = window.__manifestMagicVersion || 0;
75
+ if (!this._evals || this._evalsVersion !== version) { this._evals = new Map(); this._evalsVersion = version; }
76
+ let fn = this._evals.get(expr);
77
+ if (!fn) this._evals.set(expr, fn = A.evaluateLater(document.body, expr));
78
+ let out;
79
+ fn(v => { out = v; });
80
+ return out;
81
+ },
65
82
  _loadedSourceNames() {
66
83
  try {
67
84
  const store = window.ManifestDataStore && window.ManifestDataStore.rawDataStore;
@@ -73,11 +90,11 @@
73
90
  resolve(component, fallbacks) {
74
91
  const merged = JSON.parse(JSON.stringify(fallbacks || {}));
75
92
  try {
76
- if (!window.Alpine || typeof Alpine.evaluate !== 'function') return merged;
77
- try { Alpine.evaluate(document.body, 'typeof $locale !== "undefined" && $locale.current'); } catch (_) { } // dep → re-resolve on locale switch
93
+ if (!window.Alpine || typeof Alpine.evaluateLater !== 'function') return merged;
94
+ try { this._eval('typeof $locale !== "undefined" && $locale.current'); } catch (_) { } // dep → re-resolve on locale switch
78
95
  for (const name of this._loadedSourceNames()) {
79
96
  let ui;
80
- try { ui = Alpine.evaluate(document.body, `$x['${name}'] && $x['${name}']._ui && $x['${name}']._ui['${component}']`); } catch (_) { ui = null; }
97
+ try { ui = this._eval(`$x['${name}'] && $x['${name}']._ui && $x['${name}']._ui['${component}']`); } catch (_) { ui = null; }
81
98
  if (ui && typeof ui === 'object' && !Array.isArray(ui)) this._deepOverlay(merged, ui);
82
99
  }
83
100
  } catch (_) { }
@@ -108,6 +125,7 @@
108
125
 
109
126
  // Reactive registry id -> api, so $date('id') resolves across mount order.
110
127
  const _registry = Alpine.reactive ? Alpine.reactive({}) : {};
128
+ const _registryOwners = new Map();
111
129
  let _uid = 0;
112
130
 
113
131
  // Default English UI chrome; overridable via a data source's `_ui.date`.
@@ -173,6 +191,7 @@
173
191
  const now = new Date();
174
192
  const state = {
175
193
  rootEl,
194
+ _effects: [],
176
195
  id: rootEl.id || ('mnfst-dp-' + (++_uid)),
177
196
  selectionMode: 'single', // 'single' | 'range' | 'multiple'
178
197
  selected: null, // Date | null (single)
@@ -975,7 +994,7 @@
975
994
  }
976
995
  function bindConfigValue(state, el, expression) {
977
996
  if (!expression) return;
978
- Alpine.effect(() => {
997
+ state._effects.push(Alpine.effect(() => {
979
998
  let cfg;
980
999
  try { cfg = Alpine.evaluate(el, expression); } catch (_) { return; }
981
1000
  if (!cfg || typeof cfg !== 'object' || Array.isArray(cfg)) return;
@@ -990,14 +1009,14 @@
990
1009
  if (Array.isArray(cfg.presets)) { state.presets = cfg.presets; dirty = true; }
991
1010
  if (cfg.format !== undefined) { state.format = cfg.format || null; dirty = true; }
992
1011
  if (dirty) { state.bump(); if (state._mounted) state.render(); }
993
- });
1012
+ }));
994
1013
  }
995
1014
  /* ---- Reactive chrome — re-render when locale, direction, or resolved `_ui`
996
1015
  * text changes. Output comparison skips no-op _dataVersion bumps. */
997
1016
  function bindReactiveRender(state) {
998
1017
  if (state._uiEffectBound) return;
999
1018
  state._uiEffectBound = true;
1000
- Alpine.effect(() => {
1019
+ state._effects.push(Alpine.effect(() => {
1001
1020
  let locale = '', dir = '';
1002
1021
  try { const ls = Alpine.store('locale'); locale = ls?.current || document.documentElement.lang || ''; dir = ls?.direction || ''; } catch (_) { }
1003
1022
  try { const ds = Alpine.store('data'); if (ds) void ds._dataVersion; } catch (_) { }
@@ -1008,7 +1027,7 @@
1008
1027
  state._uiKey = key;
1009
1028
  if (state._destroyed || first) return; // initial render happens at mount
1010
1029
  if (state._mounted) { state.render(); state.syncOut(); }
1011
- });
1030
+ }));
1012
1031
  }
1013
1032
 
1014
1033
  function bindModel(state, el, modelExpr) {
@@ -1016,7 +1035,23 @@
1016
1035
  state.modelGet = (cb) => { let out; read(v => { out = v; if (cb) cb(v); }); return out; };
1017
1036
  state.modelSet = (v) => { try { Alpine.evaluate(el, `${modelExpr} = ${JSON.stringify(v)}`); } catch (_) { } };
1018
1037
  // React to external model changes; syncOut writes the same value back (no loop).
1019
- Alpine.effect(() => { read(v => { const iso = (v instanceof Date) ? D.toISO(v) : v; const cur = state._primaryValue(); if (typeof iso === 'string' && iso !== cur) { const tm = iso.match(/T(\d{2}:\d{2})/); if (tm) state.time = tm[1]; const d = D.fromISO(iso); state.selected = d; if (d) state.view = { y: d.getFullYear(), m: d.getMonth() + 1 }; state.bump(); if (state._mounted) { state.render(); state.syncOut(); } } }); });
1038
+ state._effects.push(Alpine.effect(() => { read(v => { const iso = (v instanceof Date) ? D.toISO(v) : v; const cur = state._primaryValue(); if (typeof iso === 'string' && iso !== cur) { const tm = iso.match(/T(\d{2}:\d{2})/); if (tm) state.time = tm[1]; const d = D.fromISO(iso); state.selected = d; if (d) state.view = { y: d.getFullYear(), m: d.getMonth() + 1 }; state.bump(); if (state._mounted) { state.render(); state.syncOut(); } } }); }));
1039
+ }
1040
+
1041
+ // Global effects stay subscribed to the stores (and pin the calendar) until released.
1042
+ function register(id, state) {
1043
+ _registryOwners.set(id, state);
1044
+ _registry[id] = state.api;
1045
+ }
1046
+ function unregister(id, state) {
1047
+ if (!id || _registryOwners.get(id) !== state) return;
1048
+ _registryOwners.delete(id);
1049
+ delete _registry[id];
1050
+ }
1051
+ function disposeState(state) {
1052
+ state._destroyed = true;
1053
+ state._effects.forEach((fx) => Alpine.release(fx));
1054
+ state._effects.length = 0;
1020
1055
  }
1021
1056
 
1022
1057
  function createDefaultMenu(fieldEl) {
@@ -1075,9 +1110,9 @@
1075
1110
  if (modelExpr) bindModel(state, fieldEl, modelExpr);
1076
1111
 
1077
1112
  if (!state._mounted) { state.seed(); state.syncOut(); state.render(); state._mounted = true; }
1078
- if (calendar.id) _registry[calendar.id] = state.api;
1113
+ if (calendar.id) register(calendar.id, state);
1079
1114
  // Also resolve $date(fieldId) when the calendar is an auto-generated menu.
1080
- if (fieldEl.id) _registry[fieldEl.id] = state.api;
1115
+ if (fieldEl.id) register(fieldEl.id, state);
1081
1116
 
1082
1117
  // CSS anchor positioning (as in the dropdowns plugin); showPopover() on click
1083
1118
  // since a text <input> can't carry popovertarget.
@@ -1098,7 +1133,9 @@
1098
1133
  if (fieldEl._dpSynthesizedHidden && fieldEl._dpSynthesizedHidden.isConnected) fieldEl._dpSynthesizedHidden.remove();
1099
1134
  if (state._timeMenuEl && state._timeMenuEl.isConnected) state._timeMenuEl.remove();
1100
1135
  if (calendar && calendar.isConnected) calendar.remove(); // the generated dropdown
1101
- state._destroyed = true;
1136
+ unregister(fieldEl.id, state);
1137
+ unregister(calendar.id, state);
1138
+ disposeState(state);
1102
1139
  });
1103
1140
  }
1104
1141
 
@@ -1127,18 +1164,18 @@
1127
1164
 
1128
1165
  // Defer mount so linked fields can register first.
1129
1166
  setTimeout(() => {
1130
- if (state._mounted) return;
1167
+ if (state._mounted || state._destroyed) return;
1131
1168
  state.seed();
1132
1169
  state.render();
1133
1170
  state._mounted = true;
1134
- if (el.id) _registry[el.id] = state.api;
1171
+ if (el.id) register(el.id, state);
1135
1172
  bindReactiveRender(state);
1136
1173
  }, 0);
1137
1174
 
1138
1175
  cleanup(() => {
1139
- if (el.id) delete _registry[el.id];
1176
+ unregister(el.id, state);
1140
1177
  if (state._timeMenuEl && state._timeMenuEl.isConnected) state._timeMenuEl.remove();
1141
- state._destroyed = true;
1178
+ disposeState(state);
1142
1179
  delete el._dateState;
1143
1180
  });
1144
1181
  });
@@ -134,18 +134,41 @@ function register(el, rule, opts) {
134
134
  cleanup: []
135
135
  };
136
136
  wire(rec);
137
+ Alpine.onElRemoved?.(el, () => forget(rec));
137
138
  if (rule === 'route') routes.add(rec);
138
- else if (!rec.lazy) { addPending(rec); if (!el.closest('[x-route][hidden]')) track(rec); schedule(); }
139
+ else if (!rec.lazy) { addPending(rec); if (!el.closest('[x-route][hidden]')) track(rec); if (rec.urgent || warm.size < PREWARM_CAP) schedule(); }
139
140
  return rec;
140
141
  }
141
142
 
143
+ // A copy that renders like the original: a baked x-if clone is adopted through expandos
144
+ // cloneNode drops, so the copy leaves it out and its x-if renders fresh.
145
+ function pristineCopy(fragment) {
146
+ const copy = fragment.cloneNode(true);
147
+ const copies = copy.querySelectorAll('template[x-if]');
148
+ fragment.querySelectorAll('template[x-if]').forEach((t, i) => {
149
+ if (t._x_currentIfEl && t._x_currentIfEl === t.nextElementSibling) copies[i].nextElementSibling?.remove();
150
+ });
151
+ return copy;
152
+ }
153
+
142
154
  function render(rec, viaPrewarm) {
143
- if (rec.rendered) { if (!viaPrewarm && warm.delete(rec)) schedule(); return; }
155
+ if (rec.rendered) {
156
+ if (!viaPrewarm && warm.delete(rec)) {
157
+ rec.pristine = null; // opened: never evicted
158
+ schedule();
159
+ }
160
+ return;
161
+ }
144
162
  rec.rendered = true;
145
163
  pending.delete(rec);
146
164
  if (viaPrewarm) warm.add(rec); else { warm.delete(rec); untrack(rec); }
147
165
  const { el, tpl } = rec;
148
- const content = rec.discard ? tpl.content.cloneNode(true) : tpl.content;
166
+ // A prewarm can be evicted, and eviction must restore unused markup: re-initializing
167
+ // used nodes stacks listeners on any directive without cleanup and re-runs plugins that
168
+ // rewrite their markup. So it keeps a copy, and the originals (with whatever adoption
169
+ // set on them) still render.
170
+ if (viaPrewarm && !rec.discard) rec.pristine = pristineCopy(tpl.content);
171
+ const content = rec.discard ? pristineCopy(tpl.content) : tpl.content;
149
172
  Alpine.mutateDom(() => {
150
173
  tpl.remove();
151
174
  el.appendChild(content);
@@ -165,6 +188,30 @@ function teardown(rec) {
165
188
  });
166
189
  }
167
190
 
191
+ // Records hold the element and its anchor: a removed one must not pin its page.
192
+ function forget(rec) {
193
+ rec.forgotten = true;
194
+ rec.pristine = null;
195
+ pending.delete(rec);
196
+ routes.delete(rec);
197
+ pool = null;
198
+ untrack(rec);
199
+ rec.invoker = null;
200
+ if (warm.delete(rec)) schedule();
201
+ }
202
+
203
+ // A forgotten container initialized again (re-attached, or destroyed and re-walked).
204
+ // destroyTree also dropped its attribute watchers.
205
+ function revive(rec) {
206
+ if (!rec.forgotten) return;
207
+ rec.forgotten = false;
208
+ Alpine.onElRemoved?.(rec.el, () => forget(rec));
209
+ if (rec.rendered) return;
210
+ rec.rearm?.();
211
+ if (rec.rule === 'route') routes.add(rec);
212
+ else if (!rec.lazy) { addPending(rec); track(rec); schedule(); }
213
+ }
214
+
168
215
  // Evict a warm, still-closed, never-opened container back to its stash (non-urgent, least reachable first)
169
216
  function restash(rec) {
170
217
  if (!rec.rendered) return;
@@ -174,7 +221,8 @@ function restash(rec) {
174
221
  const { el, tpl } = rec;
175
222
  Alpine.mutateDom(() => {
176
223
  Array.from(el.children).forEach((child) => Alpine.destroyTree(child));
177
- while (el.firstChild) tpl.content.appendChild(el.firstChild);
224
+ while (el.firstChild) el.firstChild.remove();
225
+ if (rec.pristine) { tpl.content.appendChild(rec.pristine); rec.pristine = null; }
178
226
  el.appendChild(tpl);
179
227
  });
180
228
  warm.delete(rec);
@@ -226,6 +274,7 @@ function wire(rec) {
226
274
  render(rec);
227
275
  if (rec.discard) arm();
228
276
  });
277
+ rec.rearm = arm;
229
278
  arm();
230
279
  if (rec.discard) observe(['hidden'], () => { if (el.hasAttribute('hidden')) teardown(rec); });
231
280
  } else if (rule === 'route') {
@@ -235,6 +284,7 @@ function wire(rec) {
235
284
  if (!el.hasAttribute('hidden')) render(rec);
236
285
  if (!rec.rendered || rec.discard) arm();
237
286
  });
287
+ rec.rearm = arm;
238
288
  arm();
239
289
  if (rec.discard) observe(['hidden'], () => { if (el.hasAttribute('hidden')) teardown(rec); });
240
290
  } else if (rule === 'panel') {
@@ -300,6 +350,7 @@ function reachablePending() {
300
350
  if (pool && poolEpoch === reachEpoch && pool.length < pending.size * 2 + 64) return pool;
301
351
  poolEpoch = reachEpoch;
302
352
  pool = [];
353
+ for (const set of [warm, routes]) for (const rec of set) if (!rec.el.isConnected) { set.delete(rec); untrack(rec); }
303
354
  for (const rec of pending) {
304
355
  if (!rec.el.isConnected) { pending.delete(rec); untrack(rec); continue; }
305
356
  if (reachable(rec)) { track(rec); pool.push(rec); } else untrack(rec);
@@ -416,12 +467,14 @@ const inViewport = (r) => !!r && r.bottom > 0 && r.top < window.innerHeight && r
416
467
  // On-screen rect for ranking, or null when off-screen / boxless
417
468
  function nearRect(rec) {
418
469
  if (rec.box !== undefined) return rec.ioNear ? rec.box : null;
470
+ if (rec.anchor) return null; // IO reports within a frame; a read here forces layout per record
419
471
  const r = anchorRect(rec);
420
472
  rec.near = inViewport(r);
421
473
  return rec.near ? r : null;
422
474
  }
423
475
  function isNear(rec) {
424
476
  if (rec.box !== undefined) return !!rec.ioNear;
477
+ if (rec.anchor) return false;
425
478
  if (rec.near === undefined) rec.near = inViewport(anchorRect(rec));
426
479
  return rec.near;
427
480
  }
@@ -498,7 +551,7 @@ function defer(el) {
498
551
  }
499
552
 
500
553
  function intercept(el, skip) {
501
- if (el.__mnfstDefer) return;
554
+ if (el.__mnfstDefer) { revive(el.__mnfstDefer); return; }
502
555
  if (defer(el)) skip();
503
556
  }
504
557
 
@@ -103,14 +103,21 @@
103
103
  background-color: rgba(0, 0, 0, 0.35)
104
104
  }
105
105
 
106
- /* Hide dropdowns when dialogs are open */
106
+ /* Hide dropdowns when dialogs are open: <html> sets flags, menus read them via style queries */
107
107
  html:has(dialog:popover-open) {
108
+ --dialog-open: 1;
109
+ }
108
110
 
109
- /* Hide dropdowns outside of any dialog. A generated popover (a datepicker
110
- calendar, a colorpicker panel) is appended to <body>, so DOM position alone
111
- can't tell page chrome from a menu the dialog itself opened —
112
- data-popover-nested marks the latter, which CSS has no way to work out. */
113
- & menu[popover]:not(dialog *):not([data-popover-nested]) {
111
+ html:has(dialog:popover-open ~ dialog:popover-open) {
112
+ --dialog-stacked: 1;
113
+ }
114
+
115
+ /* Hide dropdowns outside of any dialog. A generated popover (a datepicker
116
+ calendar, a colorpicker panel) is appended to <body>, so DOM position alone
117
+ can't tell page chrome from a menu the dialog itself opened —
118
+ data-popover-nested marks the latter, which CSS has no way to work out. */
119
+ @container style(--dialog-open: 1) {
120
+ menu[popover]:not(dialog *):not([data-popover-nested]) {
114
121
  opacity: 0;
115
122
  scale: .9;
116
123
  pointer-events: none;
@@ -123,8 +130,8 @@
123
130
  }
124
131
 
125
132
  /* When multiple dialogs are open, hide dropdowns in all but closest dialog */
126
- html:has(dialog:popover-open ~ dialog:popover-open) {
127
- & dialog:popover-open:not(:last-of-type) menu[popover] {
133
+ @container style(--dialog-stacked: 1) {
134
+ dialog:popover-open:not(:last-of-type) menu[popover] {
128
135
  opacity: 0;
129
136
  scale: .9;
130
137
  pointer-events: none;
@@ -11,7 +11,7 @@ let lastInputModality = 'mouse';
11
11
  // navigating item came from.
12
12
  let lastPointerTarget = null;
13
13
  document.addEventListener('keydown', () => { lastInputModality = 'keyboard'; }, true);
14
- document.addEventListener('pointerdown', (e) => { lastInputModality = 'mouse'; lastPointerTarget = e.target; }, true);
14
+ document.addEventListener('pointerdown', (e) => { lastInputModality = 'mouse'; lastPointerTarget = new WeakRef(e.target); }, true);
15
15
 
16
16
  // Context-menu dismiss (click outside / Escape). Module scope on purpose: a closure
17
17
  // made inside the directive shares its V8 context with every sibling closure, so it
@@ -755,7 +755,7 @@ document.addEventListener('click', (event) => {
755
755
  // the route change it dispatches instead. Hash links aren't intercepted and still
756
756
  // close via the normal path.
757
757
  window.addEventListener('manifest:route-change', () => {
758
- const pressed = lastPointerTarget;
758
+ const pressed = lastPointerTarget?.deref();
759
759
  document.querySelectorAll('[popover]:popover-open').forEach((menu) => {
760
760
  if (!menu.__mnfstCloseBound) return;
761
761
  const row = pressed && menu.contains(pressed) ? pressed : null;
@@ -1,21 +1,21 @@
1
1
  {
2
2
  "manifest.appwrite.auth.js": "sha384-9WNhIeSEzxxJm+yP7cV41kV2xyzyXgFCNpThqiREcNbb6tnIW6O7VRZPL2jwy9Nk",
3
- "manifest.appwrite.data.js": "sha384-o/I02A1W20/hRbSQ4ihAlTVdMj03/v05BPIo24L93vCh1oDmJCy/mHtWXyO9P7z1",
3
+ "manifest.appwrite.data.js": "sha384-2JOzhWYTxSjkkp8XJzegFVTCejbHLDH2giMvKPjRg60GoYUhd4LSzLbxFwNhC6OV",
4
4
  "manifest.appwrite.presences.js": "sha384-/CagPsgWhO1sWEevCNGJmZ/ezRK9YCjLjvF7hxNSM4gVlJmEVtfSLfF7wnsRq441",
5
5
  "manifest.bindings.js": "sha384-lPtMf8Ykaic9gNVZKq8Scia1881ZeMAyxer3WCIkUNwwrgzGBWto1OXADUwjvORF",
6
- "manifest.charts.js": "sha384-lzcoovnPCKhcwigrwYvc5rPAZuZNHwWmNNuu7hxPsfJuKAu8ejla5F7agHZ2Xft9",
6
+ "manifest.charts.js": "sha384-AFbP7mQnigy7DU1s+YE30yKTeQAKfgNazhbfSNbfd9ZpLgeulZKaPi/fAq3gJHDO",
7
7
  "manifest.chat.js": "sha384-88DfMfJLoi2oxclVD2iIMDkIGAExMXq5k47mekUredlicLaP3oZY5bwtwqs+TqDF",
8
8
  "manifest.code.js": "sha384-WGPlZE5Sp9GygUzJSmCdpLyYQTgvmEKc9BQS9Hmq1VTQwkqYJzPSWibHvPekcres",
9
9
  "manifest.color.js": "sha384-6Rv3LxyTcZNjrhtayQfqRdCx0uSZ4BiEbgEI98I62eTvp8Aw7LBIoNJ0Je1oktwL",
10
- "manifest.colorpicker.js": "sha384-BhcCe8cGcd0DTJNwzXWWY+FQNRfSFFl6XqfHIFP8yHg/aXFks1NIC6lVx0Sr1VkM",
11
- "manifest.combobox.js": "sha384-MILTkrgsTzsYUjGo4ZuADqV85Xs4GmfB4xexfe9vbJMIoQTZ42W4zs+DTaVSPEsx",
10
+ "manifest.colorpicker.js": "sha384-P+Zx0QzhMGfku8s3yEskTte4/iXCGVX3zdhu42IGNfFcSmhOW+nqVK7w+uczWRGY",
11
+ "manifest.combobox.js": "sha384-/lJo6vaxKjgnBSRs/dzkhK1dzGk3GBw8FISoNJZB70mwAVwu1k5veZyBXjOb4keS",
12
12
  "manifest.components.js": "sha384-BUFA8h+G5Plr82sV7bKzO42pPoiRZwRQcKj1lt2ROFiKjjlkayU/vmkAFqEsdBA+",
13
13
  "manifest.computed.js": "sha384-GYML/e81gQfN+iDbqhPUgny8bINCyL+SrbTofoGk4hlCnLLjv51mCv42JMqq+ku5",
14
- "manifest.data.js": "sha384-oOf9cFcoI3VFsfqg4eZyDVq7gdeF5DoIB92shYZI0N4ajWqhFx7B6Byy9H1g5HNE",
15
- "manifest.datepicker.js": "sha384-JeDdzdwXdXmnvgv1N1N+7WIQlezew9YJcvui3uVEvqAOX0WEaJO7ofLRRuc4hFBS",
16
- "manifest.defer.js": "sha384-GK9qJkYtchtyzSQ+3zmDQUJE3+au4qthT2M8JnFjXxgfwCH5IVKc2r3tackgMryU",
14
+ "manifest.data.js": "sha384-B+rsxpC/ediBoMP0LeEwKgb7WEARzBmndGmnl2jOs4KrdnnzhTfzN4sJtwcnF0KK",
15
+ "manifest.datepicker.js": "sha384-EzlMwvIxlla/0BuyhAEqxN0M8TGcWIWgEIg840V0akKQQK1uY648wPWMkobvtq/o",
16
+ "manifest.defer.js": "sha384-5cHzFTbx+cZmGpk9FuiUtW3ciC9NvvHwJeHP7xL5yiZVp9PhMWVdQ7L9y6ju2yoo",
17
17
  "manifest.device.js": "sha384-YeQaS56L8IILqrQNBSeAOLwIh5DuAOrE60cYLK1+46qJtUrXlAvqGe2j5mbV4Vq7",
18
- "manifest.dropdowns.js": "sha384-H1IH7pU4zqjKNZtypXzwvUfnKkvNH96hM5za5xbb6SQlAucqkjKtlAepJW39nn/u",
18
+ "manifest.dropdowns.js": "sha384-TlFrYVWTXvpUmGnYaAk2til4lu0p6w5YcmWWEzZAokzd2qlmoyRLLXXVuYAK/AlR",
19
19
  "manifest.edit.js": "sha384-MOgQNdNLeHijhR0u2W6ntoOulKFglsATCFDqkinesOU15tHoILk/JNzHKkGi5dUG",
20
20
  "manifest.export.js": "sha384-Rx6XvqBZc4apzP/FwcDr6yIln4/zNa08Q4ZqBZcEqUz1aNZQfl4h0+Ae7mqCQyuG",
21
21
  "manifest.icons.js": "sha384-kKOINpX70YzNO3FvkV6umrmO3f0KIu0DNbwB+ej55a7y/lhU7v/myQtXL0bwCTbt",
@@ -26,15 +26,15 @@
26
26
  "manifest.rich.text.js": "sha384-ZBV9jXmu2nmprEHicddRE/m7nR4RPZtVc969Jte0+EJYBcVph7itkjcgN/61PyeC",
27
27
  "manifest.router.js": "sha384-9LMIkwrgnxmmFDv70TND2MRJF1z6Nl4yqG7fH5DbJ8oUIhdu/u4yUPSime1/EYhP",
28
28
  "manifest.slides.js": "sha384-3uRTkyK9XPLmnxI2+igZlpi4EyPlU/7IHj5j3BZJJ2KN455vXyk99fiXV3feO/XY",
29
- "manifest.status.js": "sha384-Zf07q/5tRS2F0o128sINYrce8xP035FTV0Ot7V7/hh+cwFhVZg+EWkHZN5b42aBn",
29
+ "manifest.status.js": "sha384-ofu7nBnVIGZ1U1o2R9d2H3V8aEMPUCVbl5FO3gIToXqi0oa36fTviEdh5UU7Kitw",
30
30
  "manifest.svg.js": "sha384-qr/8H9+2cIhSXNbRWRtIdrDlTOxKlNJqwEefHvCYoiBNW/kKhPTFa109Orv/EBnb",
31
- "manifest.sw.js": "sha384-Bm4NWZDrWtm0kekg7m9SnOve6wsB0nOjHzQre3TsvDaIlWK5vMlJAJ1qyV9cQGbi",
31
+ "manifest.sw.js": "sha384-dQrAW1WMCbGqwao0ppVom/krl9e/eFQPHpUq0UPEkmZowgFu7E73FWwdFwpvc6YP",
32
32
  "manifest.tabs.js": "sha384-otU09H7jnawVKQdLAYpMCd3jx00tgyPVr42D2hLIBzN5dG4se505YesavdnMqEi1",
33
- "manifest.tailwind.js": "sha384-r/Jvqb+Piz21jUts10BbVAJoKkShOdJRVFpywyi4HPIi/OfOJq5CtPyWAPbdkMgt",
33
+ "manifest.tailwind.js": "sha384-yuP3LEvWU+QVjxjkZ0YhErtC9RrGmhOmHjjFjhS9obqZbp5/KTIEnSR0CXDvHE0B",
34
34
  "manifest.toasts.js": "sha384-3zkWxzw693pxg3QDFsoYCA3CqYaLKmBaMArULXODD66Utolpewl0OAeTB92aWUVv",
35
- "manifest.tooltips.js": "sha384-4b24PH5WsfFsewGea5SMWLwmrhC4kD6eDP/vvjNyjXYnQXXFGi4nLdojrIOwzG8M",
35
+ "manifest.tooltips.js": "sha384-hXGp5j0s6p2kGZY/hqLPEbaUv68ca63kFCjYiZqK3DcC12mpHTBy1vz3kuApyf4p",
36
36
  "manifest.url.parameters.js": "sha384-J2aSVBOPsoqsbafHndrWLHEDpKb4AKJAoQnVmKA4gZh5P/8HHjFWHO/lkusmG84l",
37
37
  "manifest.utilities.js": "sha384-h17EquaRtNcEW0yfhk7R+a+2+CzTlBxPuFryO6i9NV/OiIRMihREoyzQ77QawoQ7",
38
38
  "manifest.virtual.js": "sha384-ZDNoF9QVhVcVpkjIK6NH15S6I5c6OBshiJpXSOYjNsIB+298I0a7g3aFO+jyq4Vb",
39
- "manifest.js": "sha384-OmHDWM4X9puYLwv9Y0Hqnymh80AESBTsXrMYcksqduBCCRkolnxjRlIbCOU5pjvk"
39
+ "manifest.js": "sha384-qMp2SeZUWYE/Lth8sguzlCAzXxn72gBDBPYUZ1iDlQVvzYuCc8/NPgw1gUJR88u1"
40
40
  }
package/lib/manifest.js CHANGED
@@ -771,6 +771,9 @@
771
771
  if (typeof document !== 'undefined') document.addEventListener('alpine:initialized', () => { window.__manifestAlpineStarted = true; }, { once: true });
772
772
  // Framework web components — <x-*> tags that are NOT project components.
773
773
  const FRAMEWORK_TAG_RE = /^x-(code|code-group)$/;
774
+ // Attribute values seen to hold no usage magic at all; repeated rows hit this instead of the regexes
775
+ const USAGE_ANY_RE = new RegExp('\\$(' + USAGE_PLUGINS.map(u => u.magic.slice(1)).join('|') + ')\\b');
776
+ const usageClean = new Set();
774
777
  function detectUsagePlugins(alreadyLoaded, root = typeof document === 'undefined' ? null : document) {
775
778
  const wanted = USAGE_PLUGINS.filter(u => !alreadyLoaded.includes(u.plugin));
776
779
  // <x-*> tags load the components plugin even with nothing registered in
@@ -778,7 +781,8 @@
778
781
  const wantComponents = !alreadyLoaded.includes('components');
779
782
  if ((wanted.length === 0 && !wantComponents) || !root) return [];
780
783
  const found = new Set();
781
- const done = () => found.size === new Set(wanted.map(u => u.plugin)).size + (wantComponents ? 1 : 0);
784
+ const target = new Set(wanted.map(u => u.plugin)).size + (wantComponents ? 1 : 0);
785
+ const done = () => found.size === target;
782
786
 
783
787
  for (const s of root.querySelectorAll('script:not([src])')) {
784
788
  const t = s.textContent || '';
@@ -794,7 +798,12 @@
794
798
  for (const attr of el.attributes || []) {
795
799
  for (const u of wanted) if (u.attr && !found.has(u.plugin) && attr.name.startsWith(u.attr)) found.add(u.plugin);
796
800
  const v = attr.value;
797
- if (!v || v.indexOf('$') === -1) continue;
801
+ if (!v || v.indexOf('$') === -1 || usageClean.has(v)) continue;
802
+ if (!USAGE_ANY_RE.test(v)) {
803
+ if (usageClean.size > 20000) usageClean.clear();
804
+ usageClean.add(v);
805
+ continue;
806
+ }
798
807
  for (const u of wanted) if (!found.has(u.plugin) && u.re.test(v)) found.add(u.plugin);
799
808
  }
800
809
  };