mnfst 0.5.258 → 0.5.260

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.
@@ -2,7 +2,7 @@
2
2
 
3
3
  (function () {
4
4
 
5
- const MANIFEST_BUILD_VERSION = '0.5.258';
5
+ const MANIFEST_BUILD_VERSION = '0.5.260';
6
6
 
7
7
  /* Manifest Data Sources - Appwrite Integration */
8
8
 
@@ -336,18 +336,21 @@
336
336
  },
337
337
 
338
338
  activate() {
339
- if (this.active) return;
339
+ if (this.active || this.disposed) return;
340
340
  this.active = true;
341
- loadD3().then(d3 => { this.d3 = d3; this.bindReactive(); })
341
+ loadD3().then(d3 => { if (this.disposed) return; this.d3 = d3; this.bindReactive(); })
342
342
  .catch(() => { this.renderError('Chart engine failed to load.'); });
343
343
  },
344
344
 
345
345
  bindReactive() {
346
346
  const self = this;
347
347
  const getCfg = self.expression ? Alpine.evaluateLater(self.el, self.expression) : null;
348
- Alpine.effect(() => {
348
+ // Kept so cleanup can release it: an unreleased Alpine.effect stays subscribed
349
+ // to the long-lived data store, which pins this state, its element and the
350
+ // whole destroyed subtree for the life of the page (and keeps redrawing it).
351
+ self._effect = Alpine.effect(() => {
349
352
  // Subscribe to the data store heartbeat so $x loads/locale reloads re-run.
350
- try { void Alpine.store('data')?._dataVersion; } catch (_) { }
353
+ try { const ds = Alpine.store('data'); if (ds) void ds._dataVersion; } catch (_) { }
351
354
  // Every effect fire re-evaluates the expression (it may read live reactive
352
355
  // state — realtime rows, presence, staged $x hydration — that changes far
353
356
  // more often than the resulting config). applyConfig() short-circuits a
@@ -393,7 +396,8 @@
393
396
  try { console.warn('[Manifest Charts] "' + (this.el.id || this.expression || 'chart') + '" redrew ' + this._scheduleWindowCount + '+ times within one second — likely a reactive dependency changing on every read (or, for a map, a legitimately busy data feed hitting its per-draw geometry cost).'); } catch (_) { }
394
397
  }
395
398
  }
396
- this._t = setTimeout(() => { this._t = null; this.draw(); }, 0);
399
+ if (this.disposed) return;
400
+ this._t = setTimeout(() => { this._t = null; if (!this.disposed) this.draw(); }, 0);
397
401
  },
398
402
 
399
403
  renderError(msg) { this.el.innerHTML = ''; const d = document.createElement('small'); d.textContent = msg; this.el.appendChild(d); },
@@ -1241,6 +1245,9 @@
1241
1245
  el.style.minHeight = (hm ? +hm[1] : 240) + 'px';
1242
1246
  observer().observe(el);
1243
1247
  cleanup(() => {
1248
+ state.disposed = true;
1249
+ if (state._effect) { Alpine.release(state._effect); state._effect = null; }
1250
+ if (state._t) { clearTimeout(state._t); state._t = null; }
1244
1251
  if (state._ro) state._ro.disconnect();
1245
1252
  if (state._onLocale) window.removeEventListener('localechange', state._onLocale);
1246
1253
  if (io) io.unobserve(el);
@@ -2,7 +2,7 @@
2
2
 
3
3
  (function () {
4
4
 
5
- const MANIFEST_BUILD_VERSION = '0.5.258';
5
+ const MANIFEST_BUILD_VERSION = '0.5.260';
6
6
 
7
7
  /* Manifest Data Sources - Configuration */
8
8
 
@@ -1000,7 +1000,7 @@
1000
1000
  Alpine.effect(() => {
1001
1001
  let locale = '', dir = '';
1002
1002
  try { const ls = Alpine.store('locale'); locale = ls?.current || document.documentElement.lang || ''; dir = ls?.direction || ''; } catch (_) { }
1003
- try { void Alpine.store('data')?._dataVersion; } catch (_) { }
1003
+ try { const ds = Alpine.store('data'); if (ds) void ds._dataVersion; } catch (_) { }
1004
1004
  const ui = window.ManifestUI.resolve('date', UI_FALLBACK);
1005
1005
  const key = locale + '|' + dir + '|' + JSON.stringify(ui);
1006
1006
  if (state._uiKey === key) return;
@@ -13,6 +13,31 @@ let lastPointerTarget = null;
13
13
  document.addEventListener('keydown', () => { lastInputModality = 'keyboard'; }, true);
14
14
  document.addEventListener('pointerdown', (e) => { lastInputModality = 'mouse'; lastPointerTarget = e.target; }, true);
15
15
 
16
+ // Context-menu dismiss (click outside / Escape). Module scope on purpose: a closure
17
+ // made inside the directive shares its V8 context with every sibling closure, so it
18
+ // would pin the trigger and, through it, the trigger's whole subtree.
19
+ function bindContextDismiss(menu, signal) {
20
+ document.addEventListener('pointerdown', (e) => {
21
+ if (menu.matches(':popover-open') && !menu.contains(e.target)) menu.hidePopover();
22
+ }, { signal });
23
+ document.addEventListener('keydown', (e) => {
24
+ if (e.key === 'Escape' && menu.matches(':popover-open')) {
25
+ menu.hidePopover();
26
+ const host = menu._triggerHost;
27
+ if (host && host.isConnected) host.focus();
28
+ }
29
+ }, { signal });
30
+ }
31
+
32
+ function ensureContextDismiss(menu, owner) {
33
+ if (menu.__mnfstContextDismissBound) return;
34
+ menu.__mnfstContextDismissBound = true;
35
+ const ctxAbort = new AbortController();
36
+ menu.__mnfstContextAbort = ctxAbort;
37
+ menu.__mnfstCtxOwner = owner;
38
+ bindContextDismiss(menu, ctxAbort.signal);
39
+ }
40
+
16
41
  function initializeDropdownPlugin() {
17
42
  // Ensure an Alpine context exists. Keep the scope empty — seeding properties
18
43
  // collides with author state and the tabs plugin's `tab` property.
@@ -51,18 +76,42 @@ function initializeDropdownPlugin() {
51
76
  ensureAlpineContext();
52
77
 
53
78
  // Register dropdown directive
54
- registerDirective('dropdown', (el, { modifiers, expression }, { effect, evaluateLater }) => {
79
+ registerDirective('dropdown', (el, { modifiers, expression }, { effect, evaluateLater, cleanup }) => {
55
80
  let menu;
56
81
 
82
+ // Listeners this trigger adds to things that outlive it (its menu, document)
83
+ // are bound with this run's signal and aborted when Alpine tears the trigger
84
+ // down, or when the effect re-runs setup. Without it every re-rendered trigger
85
+ // left its closures (and through them its whole detached subtree) on the
86
+ // shared menu / document forever.
87
+ let runAbort = null;
88
+ cleanup(() => {
89
+ if (runAbort) runAbort.abort();
90
+ runAbort = null;
91
+ if (menu && menu.__mnfstCtxOwner === el) {
92
+ menu.__mnfstContextAbort?.abort();
93
+ menu.__mnfstContextDismissBound = false;
94
+ menu.__mnfstContextAbort = null;
95
+ menu.__mnfstCtxOwner = null;
96
+ }
97
+ if (menu && menu.__mnfstTrigger === el) menu.__mnfstTrigger = null;
98
+ if (menu && menu._triggerHost === el) { menu._triggerHost = null; menu._triggerEl = null; }
99
+ });
100
+
57
101
  // Shared hover state for all dropdown types
58
102
  let hoverTimeout;
59
103
  let autoCloseTimeout;
60
104
  let startAutoCloseTimer;
61
105
 
62
106
  effect(() => {
107
+ if (runAbort) runAbort.abort();
108
+ const runCtl = new AbortController();
109
+ runAbort = runCtl;
110
+ const signal = runCtl.signal;
63
111
 
64
112
  // Defer processing to ensure Alpine is fully ready
65
113
  setTimeout(() => {
114
+ if (signal.aborted) return;
66
115
  if (!window.Alpine) {
67
116
  console.warn('[Manifest] Alpine not available for dropdown processing');
68
117
  return;
@@ -154,7 +203,7 @@ function initializeDropdownPlugin() {
154
203
  if (window.__manifestComponentsInitialized) {
155
204
  waitForComponent();
156
205
  } else {
157
- window.addEventListener('manifest:components-ready', waitForComponent);
206
+ window.addEventListener('manifest:components-ready', waitForComponent, { once: true, signal });
158
207
  }
159
208
  return; // Exit early, setup will happen in waitForComponent
160
209
  }
@@ -174,7 +223,7 @@ function initializeDropdownPlugin() {
174
223
  setupDropdown();
175
224
 
176
225
  function setupDropdown() {
177
- if (!menu) return;
226
+ if (!menu || signal.aborted) return;
178
227
 
179
228
  // Context menus use manual popover (no light-dismiss) to avoid
180
229
  // conflicts with multi-touch trackpad right-click
@@ -299,6 +348,7 @@ function initializeDropdownPlugin() {
299
348
  // (client-reported empty menu) — hydrate the x-defer stash ourselves.
300
349
  if (window.ManifestDefer?.isPending(menu)) window.ManifestDefer.render(menu);
301
350
 
351
+ ensureContextDismiss(menu, el);
302
352
  if (!menu.matches(':popover-open')) menu.showPopover();
303
353
 
304
354
  // Adjust if menu overflows viewport
@@ -384,20 +434,10 @@ function initializeDropdownPlugin() {
384
434
 
385
435
  // Document-level dismiss listeners are global — bind once per
386
436
  // menu via an AbortController so reprocessing can't leak them.
387
- if (!menu.__mnfstContextDismissBound) {
388
- menu.__mnfstContextDismissBound = true;
389
- const ctxAbort = new AbortController();
390
- menu.__mnfstContextAbort = ctxAbort;
391
- const { signal } = ctxAbort;
392
-
393
- // Manual dismiss: click outside or Escape
394
- document.addEventListener('pointerdown', (e) => {
395
- if (menu.matches(':popover-open') && !menu.contains(e.target)) closeContext();
396
- }, { signal });
397
- document.addEventListener('keydown', (e) => {
398
- if (e.key === 'Escape' && menu.matches(':popover-open')) { closeContext(); el.focus(); }
399
- }, { signal });
400
- }
437
+ // Bound once per menu, owned by one trigger and released in that
438
+ // trigger's cleanup (see top). Any other trigger of the same menu
439
+ // re-binds on its next open, so a shared menu never loses dismiss.
440
+ ensureContextDismiss(menu, el);
401
441
  }
402
442
 
403
443
  // Auto-close on selection: `close` per item, .close for every row
@@ -504,7 +544,7 @@ function initializeDropdownPlugin() {
504
544
  (e.target.querySelector('a') || e.target).click();
505
545
  }
506
546
  }
507
- });
547
+ }, { signal });
508
548
 
509
549
  // Make li elements focusable when menu opens
510
550
  menu.addEventListener('toggle', (e) => {
@@ -532,7 +572,7 @@ function initializeDropdownPlugin() {
532
572
  }
533
573
  }
534
574
  }
535
- });
575
+ }, { signal });
536
576
 
537
577
  // Add hover functionality for menu
538
578
  if (modifiers.includes('hover')) {
@@ -540,14 +580,14 @@ function initializeDropdownPlugin() {
540
580
  menu.addEventListener('mouseenter', () => {
541
581
  clearTimeout(autoCloseTimeout);
542
582
  clearTimeout(hoverTimeout);
543
- });
583
+ }, { signal });
544
584
 
545
585
  menu.addEventListener('mouseleave', () => {
546
586
  // Always start timer when leaving menu bounds
547
587
  if (startAutoCloseTimer) {
548
588
  startAutoCloseTimer();
549
589
  }
550
- });
590
+ }, { signal });
551
591
 
552
592
  // Add event listeners to all interactive elements inside menu to cancel timers
553
593
  const cancelCloseTimer = () => {
@@ -561,14 +601,14 @@ function initializeDropdownPlugin() {
561
601
  menuItems.forEach(item => {
562
602
  if (wiredItems.has(item)) return;
563
603
  wiredItems.add(item);
564
- item.addEventListener('mouseenter', cancelCloseTimer);
604
+ item.addEventListener('mouseenter', cancelCloseTimer, { signal });
565
605
  });
566
606
  };
567
607
 
568
608
  // Setup listeners after a brief delay to ensure menu is rendered;
569
609
  // again when a deferred menu renders its items
570
610
  setTimeout(setupMenuItemListeners, 10);
571
- menu.addEventListener('manifest:defer-render', setupMenuItemListeners);
611
+ menu.addEventListener('manifest:defer-render', setupMenuItemListeners, { signal });
572
612
  }
573
613
  } // End of setupDropdown function
574
614
  });
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "manifest.appwrite.auth.js": "sha384-9WNhIeSEzxxJm+yP7cV41kV2xyzyXgFCNpThqiREcNbb6tnIW6O7VRZPL2jwy9Nk",
3
- "manifest.appwrite.data.js": "sha384-zpWExEiCs/Ccl9jDr2MSU5BU9MD0G69HFxsAg4el4T+slUj5a/dJa4WZuUmU3XkX",
3
+ "manifest.appwrite.data.js": "sha384-NrUgzRg0XIrS/BYh6iw9V/S5MH2EkMy7935n7UnT+nq9nUrPtVBRHlmfNuymT9R4",
4
4
  "manifest.appwrite.presences.js": "sha384-/CagPsgWhO1sWEevCNGJmZ/ezRK9YCjLjvF7hxNSM4gVlJmEVtfSLfF7wnsRq441",
5
5
  "manifest.bindings.js": "sha384-lPtMf8Ykaic9gNVZKq8Scia1881ZeMAyxer3WCIkUNwwrgzGBWto1OXADUwjvORF",
6
- "manifest.charts.js": "sha384-G+huD7UvjF9zDovdVVlh4I5X1Mq4oNnJfoFg4NnGdP3R/4Skq9hy5cGuHNYLxDYm",
6
+ "manifest.charts.js": "sha384-lzcoovnPCKhcwigrwYvc5rPAZuZNHwWmNNuu7hxPsfJuKAu8ejla5F7agHZ2Xft9",
7
7
  "manifest.chat.js": "sha384-88DfMfJLoi2oxclVD2iIMDkIGAExMXq5k47mekUredlicLaP3oZY5bwtwqs+TqDF",
8
8
  "manifest.code.js": "sha384-WGPlZE5Sp9GygUzJSmCdpLyYQTgvmEKc9BQS9Hmq1VTQwkqYJzPSWibHvPekcres",
9
9
  "manifest.color.js": "sha384-6Rv3LxyTcZNjrhtayQfqRdCx0uSZ4BiEbgEI98I62eTvp8Aw7LBIoNJ0Je1oktwL",
@@ -11,11 +11,11 @@
11
11
  "manifest.combobox.js": "sha384-MILTkrgsTzsYUjGo4ZuADqV85Xs4GmfB4xexfe9vbJMIoQTZ42W4zs+DTaVSPEsx",
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-xGbKjNmwqUapD++Kjx1XA/IAl01e1WiOVnXB/8XqX5mDgsyythAq1joIGY13XpYO",
15
- "manifest.datepicker.js": "sha384-2XwFFnS4Bg5FDvG/v8x6KUvrjcBQEOYyTxynX2K/D/Qb3PI6+Y04lEwCnVDJLzwL",
14
+ "manifest.data.js": "sha384-OXdlXbo1eKsq/47pj6A9qPTyxtvvjxycFzgZeE5tmLbIT/Nj+A1JdofNmoV9/iRa",
15
+ "manifest.datepicker.js": "sha384-JeDdzdwXdXmnvgv1N1N+7WIQlezew9YJcvui3uVEvqAOX0WEaJO7ofLRRuc4hFBS",
16
16
  "manifest.defer.js": "sha384-GK9qJkYtchtyzSQ+3zmDQUJE3+au4qthT2M8JnFjXxgfwCH5IVKc2r3tackgMryU",
17
17
  "manifest.device.js": "sha384-YeQaS56L8IILqrQNBSeAOLwIh5DuAOrE60cYLK1+46qJtUrXlAvqGe2j5mbV4Vq7",
18
- "manifest.dropdowns.js": "sha384-acufUps7R9WjqB4XXW5JLACqniuHJGM4CNTb2rF4B2Q/jv7ZRmsAwfr2McanOz3s",
18
+ "manifest.dropdowns.js": "sha384-ORVhBVvqhx58sd7Ij8b+NOIkjWF2TGcz5a8BC+CIswsS8TDLtyIDqD3YM4HDsGzB",
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,9 +26,9 @@
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-MBO345ab7H6Uq1X2dZLt4Ge1Gcra+Z2qYApvlAeIV8CJhDDJrz2FPXpp+Kcp8zcM",
29
+ "manifest.status.js": "sha384-Zf07q/5tRS2F0o128sINYrce8xP035FTV0Ot7V7/hh+cwFhVZg+EWkHZN5b42aBn",
30
30
  "manifest.svg.js": "sha384-qr/8H9+2cIhSXNbRWRtIdrDlTOxKlNJqwEefHvCYoiBNW/kKhPTFa109Orv/EBnb",
31
- "manifest.sw.js": "sha384-8xS3BLZdBXUL7CSmurvYh6v1bvI1+X56IXme2ng3lTYzwLjLHdOYQEkll4YFpKj1",
31
+ "manifest.sw.js": "sha384-V4isxMDJOOMSqedY9JNCeVXhe4tJTA/GUBReX9dghYtNr68cUhsqur76nOmlO3iF",
32
32
  "manifest.tabs.js": "sha384-otU09H7jnawVKQdLAYpMCd3jx00tgyPVr42D2hLIBzN5dG4se505YesavdnMqEi1",
33
33
  "manifest.tailwind.js": "sha384-r/Jvqb+Piz21jUts10BbVAJoKkShOdJRVFpywyi4HPIi/OfOJq5CtPyWAPbdkMgt",
34
34
  "manifest.toasts.js": "sha384-3zkWxzw693pxg3QDFsoYCA3CqYaLKmBaMArULXODD66Utolpewl0OAeTB92aWUVv",
@@ -543,7 +543,7 @@
543
543
  // Human label for a state, localized through `_ui`, else title-cased.
544
544
  function formatStateLabel(state) {
545
545
  // Dep on the data store version so labels re-resolve when a `_ui` source loads late.
546
- try { void (window.Alpine && Alpine.store('data')?._dataVersion); } catch (_) { }
546
+ try { const ds = window.Alpine && Alpine.store('data'); if (ds) void ds._dataVersion; } catch (_) { }
547
547
  const ui = window.ManifestUI ? window.ManifestUI.resolve('status', UI_FALLBACK) : UI_FALLBACK;
548
548
  const labels = (ui && ui.label) || UI_FALLBACK.label;
549
549
  if (labels[state]) return _resolveRefString(labels[state]);
@@ -16,7 +16,7 @@
16
16
 
17
17
  // Build stamps the published version here; unstamped (dev) falls back to the
18
18
  // registration URL's `v`, then 'dev'.
19
- const BUILD_VERSION = '0.5.258';
19
+ const BUILD_VERSION = '0.5.260';
20
20
  const CACHE_PREFIX = 'mnfst-sw:';
21
21
  const META_CACHE = CACHE_PREFIX + 'meta';
22
22
  // Sized to hold a full precache.json warm-up (PRECACHE_MAX_ENTRIES server-side)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mnfst",
3
- "version": "0.5.258",
3
+ "version": "0.5.260",
4
4
  "private": false,
5
5
  "workspaces": [
6
6
  "templates/starter",
@@ -65,6 +65,7 @@
65
65
  "puppeteer": "^24.15.0",
66
66
  "rimraf": "^5.0.0",
67
67
  "rollup": "^4.24.2",
68
+ "terser": "^5.47.1",
68
69
  "vitest": "^4.1.2"
69
70
  },
70
71
  "keywords": [