mnfst 0.5.210 → 0.5.211

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.210';
5
+ const MANIFEST_BUILD_VERSION = '0.5.211';
6
6
 
7
7
  /* Manifest Data Sources - Appwrite Integration */
8
8
 
@@ -182,9 +182,11 @@ window.ManifestComponentsProcessor = {
182
182
  const registry = window.ManifestComponentsRegistry;
183
183
  const loader = window.ManifestComponentsLoader;
184
184
  if (!registry || !loader) {
185
+ console.debug('[Manifest Components] skipped: registry or loader unavailable', element);
185
186
  return;
186
187
  }
187
188
  if (!registry.registered.has(name)) {
189
+ console.debug('[Manifest Components] skipped: component not registered', element);
188
190
  return;
189
191
  }
190
192
  if (element.hasAttribute('data-pre-rendered') || element.hasAttribute('data-processed')) {
@@ -192,10 +194,12 @@ window.ManifestComponentsProcessor = {
192
194
  if (element.hasAttribute('data-pre-rendered') && window.Alpine && typeof window.Alpine.initTree === 'function') {
193
195
  try { window.Alpine.initTree(element); } catch (e) { /* graceful */ }
194
196
  }
197
+ console.debug('[Manifest Components] skipped: already pre-rendered/processed', element);
195
198
  return;
196
199
  }
197
200
  const content = await loader.loadComponent(name);
198
201
  if (!content) {
202
+ console.debug('[Manifest Components] skipped: failed to load component', element);
199
203
  element.replaceWith(document.createComment(` Failed to load component: ${name} `));
200
204
  return;
201
205
  }
@@ -203,6 +207,7 @@ window.ManifestComponentsProcessor = {
203
207
  container.innerHTML = content.trim();
204
208
  const topLevelElements = Array.from(container.children);
205
209
  if (topLevelElements.length === 0) {
210
+ console.debug('[Manifest Components] skipped: empty component template', element);
206
211
  element.replaceWith(document.createComment(` Empty component: ${name} `));
207
212
  return;
208
213
  }
@@ -415,6 +420,7 @@ window.ManifestComponentsProcessor = {
415
420
  }
416
421
  const parent = element.parentElement;
417
422
  if (!parent || !document.contains(element)) {
423
+ console.debug('[Manifest Components] skipped: element detached before swap', element);
418
424
  return;
419
425
  }
420
426
  // Replace the placeholder element with the component content
@@ -494,11 +500,21 @@ window.ManifestComponentsProcessor = {
494
500
 
495
501
  // Components swapping
496
502
  (function () {
503
+ // Never reset — ids must stay unique for the life of the page so overlapping
504
+ // processAll runs can't mint a colliding id for a live instance.
497
505
  let componentInstanceCounters = {};
498
506
  const swappedInstances = new Set();
499
507
  const instanceRouteMap = new Map();
500
508
  const placeholderMap = new Map();
501
509
 
510
+ // Serialises processAll: a call while one is in flight coalesces into a
511
+ // single trailing re-run (latest path wins) instead of interleaving.
512
+ let activeRun = null;
513
+ let trailingRun = null;
514
+ let trailingPath = null;
515
+ let hasTrailing = false;
516
+ let trailingSettlers = null;
517
+
502
518
  function getComponentInstanceId(name) {
503
519
  if (!componentInstanceCounters[name]) componentInstanceCounters[name] = 1;
504
520
  else componentInstanceCounters[name]++;
@@ -513,9 +529,15 @@ window.ManifestComponentsProcessor = {
513
529
  window.ManifestComponentsSwapping = {
514
530
  // Swap in source code for a placeholder
515
531
  async swapIn(placeholder) {
516
- if (placeholder.hasAttribute('data-swapped')) return;
532
+ if (placeholder.hasAttribute('data-swapped')) {
533
+ console.debug('[Manifest Components] skipped swapIn: already marked data-swapped', placeholder);
534
+ return;
535
+ }
517
536
  const processor = window.ManifestComponentsProcessor;
518
- if (!processor) return;
537
+ if (!processor) {
538
+ console.debug('[Manifest Components] skipped swapIn: processor unavailable', placeholder);
539
+ return;
540
+ }
519
541
  const name = placeholder.tagName.toLowerCase().replace('x-', '');
520
542
  let instanceId = placeholder.getAttribute('data-component');
521
543
  if (!instanceId) {
@@ -542,10 +564,16 @@ window.ManifestComponentsProcessor = {
542
564
  },
543
565
  // Revert to placeholder
544
566
  revert(instanceId) {
545
- if (!swappedInstances.has(instanceId)) return;
567
+ if (!swappedInstances.has(instanceId)) {
568
+ console.debug('[Manifest Components] skipped revert: instance not tracked', instanceId);
569
+ return;
570
+ }
546
571
  // Remove all elements with data-component=instanceId
547
572
  const rendered = Array.from(document.querySelectorAll(`[data-component="${instanceId}"]`));
548
- if (rendered.length === 0) return;
573
+ if (rendered.length === 0) {
574
+ console.debug('[Manifest Components] skipped revert: no rendered elements found', instanceId);
575
+ return;
576
+ }
549
577
  const first = rendered[0];
550
578
  const parent = first.parentNode;
551
579
  // Retrieve the original placeholder from the map
@@ -585,11 +613,14 @@ window.ManifestComponentsProcessor = {
585
613
  // Log after revert
586
614
  logSiblings(parent, `After revert for ${instanceId}`);
587
615
  },
588
- // Main swapping logic
589
- async processAll(normalizedPathFromEvent = null) {
590
- componentInstanceCounters = {};
616
+ // Main swapping logic — single pass. Call via processAll(), not directly:
617
+ // this has no re-entrancy guard of its own.
618
+ async _runProcessAll(normalizedPathFromEvent) {
591
619
  const registry = window.ManifestComponentsRegistry;
592
- if (!registry) return;
620
+ if (!registry) {
621
+ console.debug('[Manifest Components] skipped processAll: registry unavailable');
622
+ return;
623
+ }
593
624
  const routing = window.ManifestRouting;
594
625
 
595
626
  // Use normalized path from event if provided, otherwise compute from window.location
@@ -676,6 +707,40 @@ window.ManifestComponentsProcessor = {
676
707
  }
677
708
  }
678
709
  },
710
+ // Public entry point. Coalesces overlapping calls: while a run is active,
711
+ // later calls don't start their own interleaved run — they queue exactly
712
+ // one trailing re-run (latest path wins) and resolve when it finishes.
713
+ async processAll(normalizedPathFromEvent = null) {
714
+ if (activeRun) {
715
+ hasTrailing = true;
716
+ trailingPath = normalizedPathFromEvent;
717
+ if (!trailingRun) {
718
+ trailingRun = new Promise((resolve, reject) => {
719
+ trailingSettlers = { resolve, reject };
720
+ });
721
+ }
722
+ return trailingRun;
723
+ }
724
+ activeRun = this._runProcessAll(normalizedPathFromEvent);
725
+ let error = null;
726
+ try {
727
+ await activeRun;
728
+ } catch (e) {
729
+ error = e;
730
+ } finally {
731
+ activeRun = null;
732
+ }
733
+ if (hasTrailing) {
734
+ hasTrailing = false;
735
+ const path = trailingPath;
736
+ const settlers = trailingSettlers;
737
+ trailingRun = null;
738
+ trailingSettlers = null;
739
+ trailingPath = null;
740
+ this.processAll(path).then(settlers.resolve, settlers.reject);
741
+ }
742
+ if (error) throw error;
743
+ },
679
744
  initialize() {
680
745
  // On init, process all
681
746
  this.processAll().then(() => {
@@ -2,7 +2,7 @@
2
2
 
3
3
  (function () {
4
4
 
5
- const MANIFEST_BUILD_VERSION = '0.5.210';
5
+ const MANIFEST_BUILD_VERSION = '0.5.211';
6
6
 
7
7
  /* Manifest Data Sources - Configuration */
8
8
 
@@ -274,6 +274,37 @@ function initializeDropdownPlugin() {
274
274
  if (menu.matches(':popover-open')) menu.hidePopover();
275
275
  };
276
276
 
277
+ // How long after a long-press open to ignore a follow-up native
278
+ // contextmenu (Android) or synthetic click.
279
+ const LONG_PRESS_DUPE_WINDOW_MS = 750;
280
+
281
+ // Shared positioning: place the menu at (x, y), overriding anchor
282
+ // positioning. Used by both the contextmenu (mouse) path and the
283
+ // touch long-press path below.
284
+ const openContextMenuAt = (triggerTarget, x, y) => {
285
+ // Stash the actual right-clicked element so menu items can act on it.
286
+ // (triggerTarget is the deepest hit; el is the directive-bearing ancestor.)
287
+ menu._triggerEl = triggerTarget.closest('[data-cp-value], [x-data]') || el;
288
+ menu._triggerHost = el;
289
+
290
+ menu.style.position = 'fixed';
291
+ menu.style.positionAnchor = 'unset';
292
+ menu.style.positionArea = 'unset';
293
+ menu.style.inset = 'auto';
294
+ menu.style.left = x + 'px';
295
+ menu.style.top = y + 'px';
296
+ menu.style.margin = '0';
297
+
298
+ if (!menu.matches(':popover-open')) menu.showPopover();
299
+
300
+ // Adjust if menu overflows viewport
301
+ requestAnimationFrame(() => {
302
+ const rect = menu.getBoundingClientRect();
303
+ if (rect.right > window.innerWidth) menu.style.left = (window.innerWidth - rect.width) + 'px';
304
+ if (rect.bottom > window.innerHeight) menu.style.top = (window.innerHeight - rect.height) + 'px';
305
+ });
306
+ };
307
+
277
308
  // Bind once per element (reprocessing would stack duplicates).
278
309
  if (!el.__mnfstContextTriggerBound) {
279
310
  el.__mnfstContextTriggerBound = true;
@@ -281,29 +312,68 @@ function initializeDropdownPlugin() {
281
312
  e.preventDefault();
282
313
  e.stopPropagation();
283
314
 
284
- // Stash the actual right-clicked element so menu items can act on it.
285
- // (e.target is the deepest hit; el is the directive-bearing ancestor.)
286
- menu._triggerEl = e.target.closest('[data-cp-value], [x-data]') || el;
287
- menu._triggerHost = el;
288
-
289
- // Position at cursor, overriding anchor positioning
290
- menu.style.position = 'fixed';
291
- menu.style.positionAnchor = 'unset';
292
- menu.style.positionArea = 'unset';
293
- menu.style.inset = 'auto';
294
- menu.style.left = e.clientX + 'px';
295
- menu.style.top = e.clientY + 'px';
296
- menu.style.margin = '0';
297
-
298
- if (!menu.matches(':popover-open')) menu.showPopover();
299
-
300
- // Adjust if menu overflows viewport
301
- requestAnimationFrame(() => {
302
- const rect = menu.getBoundingClientRect();
303
- if (rect.right > window.innerWidth) menu.style.left = (window.innerWidth - rect.width) + 'px';
304
- if (rect.bottom > window.innerHeight) menu.style.top = (window.innerHeight - rect.height) + 'px';
305
- });
315
+ // Android fires a real contextmenu after a touch long-press too —
316
+ // our pointer path already opened the menu, so ignore the dupe.
317
+ if (el.__mnfstLongPressOpenedAt && Date.now() - el.__mnfstLongPressOpenedAt < LONG_PRESS_DUPE_WINDOW_MS) return;
318
+
319
+ openContextMenuAt(e.target, e.clientX, e.clientY);
306
320
  });
321
+
322
+ // iOS Safari never fires `contextmenu` for a touch long-press (Android
323
+ // does). Drive the same open from pointer events instead — touch/pen
324
+ // only, mouse keeps native contextmenu semantics.
325
+ const LONG_PRESS_MS = 500;
326
+ const MOVE_SLOP_PX = 10;
327
+ let pressTimer = null;
328
+ let startX = 0, startY = 0;
329
+ let pressTarget = null;
330
+
331
+ const suppressCallout = () => {
332
+ el.style.setProperty('-webkit-touch-callout', 'none');
333
+ el.style.setProperty('-webkit-user-select', 'none');
334
+ el.style.setProperty('user-select', 'none');
335
+ };
336
+ const restoreCallout = () => {
337
+ el.style.removeProperty('-webkit-touch-callout');
338
+ el.style.removeProperty('-webkit-user-select');
339
+ el.style.removeProperty('user-select');
340
+ };
341
+ const cancelPress = () => {
342
+ if (pressTimer) clearTimeout(pressTimer);
343
+ pressTimer = null;
344
+ restoreCallout();
345
+ };
346
+
347
+ el.addEventListener('pointerdown', (e) => {
348
+ if (e.pointerType !== 'touch' && e.pointerType !== 'pen') return;
349
+ cancelPress();
350
+ startX = e.clientX;
351
+ startY = e.clientY;
352
+ pressTarget = e.target;
353
+ suppressCallout();
354
+ pressTimer = setTimeout(() => {
355
+ pressTimer = null;
356
+ restoreCallout();
357
+ el.__mnfstLongPressOpenedAt = Date.now();
358
+ openContextMenuAt(pressTarget, startX, startY);
359
+ }, LONG_PRESS_MS);
360
+ });
361
+ el.addEventListener('pointermove', (e) => {
362
+ if (!pressTimer) return;
363
+ if (Math.abs(e.clientX - startX) > MOVE_SLOP_PX || Math.abs(e.clientY - startY) > MOVE_SLOP_PX) cancelPress();
364
+ });
365
+ el.addEventListener('pointerup', cancelPress);
366
+ el.addEventListener('pointercancel', cancelPress);
367
+ el.addEventListener('scroll', cancelPress, true);
368
+
369
+ // Suppress the synthetic click that follows the touch sequence which
370
+ // opened the menu, so it doesn't also activate/navigate the trigger.
371
+ el.addEventListener('click', (e) => {
372
+ if (el.__mnfstLongPressOpenedAt && Date.now() - el.__mnfstLongPressOpenedAt < LONG_PRESS_DUPE_WINDOW_MS) {
373
+ e.preventDefault();
374
+ e.stopPropagation();
375
+ }
376
+ }, true);
307
377
  }
308
378
 
309
379
  // Document-level dismiss listeners are global — bind once per
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "manifest.appwrite.auth.js": "sha384-HMJmRXnvdDBWyuteXL+DTxWM2llSCdjl4EDamEI7iHNAVlqeOCE4GDTm8ZHipPRj",
3
- "manifest.appwrite.data.js": "sha384-kmsw1sYLqTxnkRfh7dt0urLSFU+LOkgHofsL1jYGndFvCizo7wsbQix6vV022/ov",
3
+ "manifest.appwrite.data.js": "sha384-ZneCr1Vg7lAvz8wb8HipzJCF0KdZpPg2e3UK7kJdfECqcHbeXGfOJ9C8/rONj0Vg",
4
4
  "manifest.appwrite.presences.js": "sha384-JyVDWFT69b7Svq8J8p9tNWe2v9PZNQBY5UyhtOl1vnuHFAX0CRQ3MA9SiKq5wzNh",
5
5
  "manifest.bindings.js": "sha384-lPtMf8Ykaic9gNVZKq8Scia1881ZeMAyxer3WCIkUNwwrgzGBWto1OXADUwjvORF",
6
6
  "manifest.charts.js": "sha384-WE0pnHK4LBAkFyTw4+R6gheB9NFVJg81ZAG9GwMnbKI5gY3zlJIKM5neMlBUhTso",
@@ -9,13 +9,13 @@
9
9
  "manifest.color.js": "sha384-6Rv3LxyTcZNjrhtayQfqRdCx0uSZ4BiEbgEI98I62eTvp8Aw7LBIoNJ0Je1oktwL",
10
10
  "manifest.colorpicker.js": "sha384-1+ZV5Hhjf7WnGP7b71Cqa5h3BKW8UgGT5V14i8Q+aF5zH8Xxwowt/ibSUVsiIHSm",
11
11
  "manifest.combobox.js": "sha384-5DaKDWkWF7zxS80h7G1AcZG9hr+uGqJ1+B09aaVIVF+z0Qn4j8+9KEg7gys/ABQG",
12
- "manifest.components.js": "sha384-DLGSqCbnGkS87AWJ8GpKoAFP4nCosgC5SFSg6C10hH/nWu/MxI+0ntHlnFEfs++u",
12
+ "manifest.components.js": "sha384-458gCDrUIZts3WE1WZ/UA8Y1XcV6PaF+5k3Wf/Jr2iIY5EM/FisjwbLzpON8I25s",
13
13
  "manifest.computed.js": "sha384-GYML/e81gQfN+iDbqhPUgny8bINCyL+SrbTofoGk4hlCnLLjv51mCv42JMqq+ku5",
14
- "manifest.data.js": "sha384-U4/sSXZI/DL0X7ymE9KrwAKhIv31pJoEo0YnZBgJTAhHYwUZxhaC7nrhWVj/5a/q",
14
+ "manifest.data.js": "sha384-iNF6nYPeG0TL3FTl0h2ZUN6oqwuDKiOGOTPPKV6+AImN74Dku3Y+zqC0qGQVv/QM",
15
15
  "manifest.datepicker.js": "sha384-8vTJYkLqqvgXgm1w1becuaRR/jUEpiLBjBJ6RsRbUr+ZkypvspD6EsssuDrX4SQq",
16
16
  "manifest.defer.js": "sha384-wXa60M2OHigi5rBurXQ7Oa8eyr5WTKs7EUn2GOru62KMdfs/LDyNegxCc9MNJFSe",
17
17
  "manifest.device.js": "sha384-g+txmrEszYtuOvRelTp/c1wSNR2AtllVI9rx5NPPD7DzhVUnMNughonZykv2xlBW",
18
- "manifest.dropdowns.js": "sha384-4HuHkBt4i8sTPUDLB9rNLli2/lWLY1tjtCLzey37gRwueG+FRLCnVH1q7ljiVvJR",
18
+ "manifest.dropdowns.js": "sha384-LrPBmAx92pwcr68agp1wByt6ZjqlRyVygQiMbcOj0mbmnv3l/RgWwYhwhkgN/txC",
19
19
  "manifest.edit.js": "sha384-oLsrH7/UkJ/KEl3UO77hexcf2rYCGgzXZd0fe2EMq3hKRE7aUqLiEnLJhSV7zTaY",
20
20
  "manifest.export.js": "sha384-HCy3n6ifrcAGIyzTRgZ5JIRvSX9q9/8ykMKRSbt1QTnhXz6PgYlV0AE2N2Yi2re+",
21
21
  "manifest.icons.js": "sha384-kKOINpX70YzNO3FvkV6umrmO3f0KIu0DNbwB+ej55a7y/lhU7v/myQtXL0bwCTbt",
@@ -28,7 +28,7 @@
28
28
  "manifest.slides.js": "sha384-3uRTkyK9XPLmnxI2+igZlpi4EyPlU/7IHj5j3BZJJ2KN455vXyk99fiXV3feO/XY",
29
29
  "manifest.status.js": "sha384-nwvUSbT+gpRMqhYx6MeBYswUMH72rY92mjgxv0vrSG7Ht7l83XYpEB6fCTbXjjfK",
30
30
  "manifest.svg.js": "sha384-qr/8H9+2cIhSXNbRWRtIdrDlTOxKlNJqwEefHvCYoiBNW/kKhPTFa109Orv/EBnb",
31
- "manifest.sw.js": "sha384-uUl8PEc1B0HXLOMpjQqF0w+4ybSeyHAAOSXmWQkHH4eO7ZiyU43yIlnEWKpsXlgb",
31
+ "manifest.sw.js": "sha384-9PKF6DREUWpXcP6EXSjpcruKQqbMuZCo7hez8hGpBthPrIky6LOUscB0bdf9IB46",
32
32
  "manifest.tabs.js": "sha384-OIIcyaQnGS0eNYk4zgAFEDboNYect7THvUd9D0V6nqCeDOxQcoiqLrBEmmWVTTet",
33
33
  "manifest.tailwind.js": "sha384-r/Jvqb+Piz21jUts10BbVAJoKkShOdJRVFpywyi4HPIi/OfOJq5CtPyWAPbdkMgt",
34
34
  "manifest.text.edit.js": "sha384-pfhc4IPKNJokj6ldlgD+KwGLN4UoM21CNeVf2sVq/34X1Un2PUhEuyTg5itmtsXS",
@@ -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.210';
19
+ const BUILD_VERSION = '0.5.211';
20
20
  const CACHE_PREFIX = 'mnfst-sw:';
21
21
  const META_CACHE = CACHE_PREFIX + 'meta';
22
22
  const SWR_CAP = 200;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mnfst",
3
- "version": "0.5.210",
3
+ "version": "0.5.211",
4
4
  "private": false,
5
5
  "workspaces": [
6
6
  "templates/starter",