mnfst 0.5.163 → 0.5.164

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.
@@ -728,8 +728,7 @@ class TailwindCompiler {
728
728
 
729
729
 
730
730
 
731
- // Synchronous utility generation
732
- // Methods for generating utilities synchronously before first paint
731
+ // Synchronous utility generation before first paint
733
732
 
734
733
  TailwindCompiler.prototype.addCriticalBlockingStylesSync = function () {
735
734
  if (!this.criticalStyleElement) return;
@@ -737,7 +736,6 @@ TailwindCompiler.prototype.addCriticalBlockingStylesSync = function () {
737
736
  const syncStart = performance.now();
738
737
 
739
738
  try {
740
- // Extract CSS variables synchronously from already-loaded sources
741
739
  const cssVariables = new Map();
742
740
 
743
741
  // 1. From inline style elements (already in DOM)
@@ -778,7 +776,7 @@ TailwindCompiler.prototype.addCriticalBlockingStylesSync = function () {
778
776
  const rules = Array.from(sheet.cssRules || []);
779
777
  for (const rule of rules) {
780
778
  if (rule.type === CSSRule.STYLE_RULE && rule.styleSheet) {
781
- // Handle @import rules that have nested stylesheets
779
+ // @import rules with nested stylesheets
782
780
  try {
783
781
  const nestedRules = Array.from(rule.styleSheet.cssRules || []);
784
782
  for (const nestedRule of nestedRules) {
@@ -809,18 +807,10 @@ TailwindCompiler.prototype.addCriticalBlockingStylesSync = function () {
809
807
  } catch (e) {
810
808
  }
811
809
 
812
- // 4. From computed styles (if :root is available)
813
- // Run even while document.readyState === 'loading'. This method is the
814
- // constructor's only origin-independent variable source: getComputedStyle
815
- // reads the resolved cascade regardless of stylesheet origin, whereas
816
- // method 3 (CSSOM cssRules) throws on cross-origin sheets (e.g. the CDN
817
- // build at cdn.jsdelivr.net). The classic <script> blocks on preceding
818
- // stylesheets, so :root variables are already resolved here. Skipping
819
- // this during 'loading' meant a CDN-hosted page captured zero variables
820
- // synchronously, so the critical "all colors" fallback never ran and
821
- // variable-derived utilities (bg-line, border-line, …) fell back to
822
- // currentColor — pure white in dark mode / black in light — until the
823
- // async compile finished.
810
+ // 4. From computed styles. Runs even during 'loading': getComputedStyle
811
+ // reads the resolved cascade across origins (CDN sheets), which the CSSOM
812
+ // path (method 3) can't without it CDN pages capture no vars and
813
+ // utilities flash as currentColor.
824
814
  try {
825
815
  if (document.documentElement) {
826
816
  const rootStyles = getComputedStyle(document.documentElement);
@@ -851,7 +841,6 @@ TailwindCompiler.prototype.addCriticalBlockingStylesSync = function () {
851
841
  while ((classMatch = classRegex.exec(htmlSource)) !== null) {
852
842
  const classes = classMatch[1].split(/\s+/).filter(Boolean);
853
843
  for (const cls of classes) {
854
- // Match utility patterns that might use CSS variables
855
844
  if (/^(border|bg|text|ring|outline|decoration|caret|accent|fill|stroke)-[a-z0-9-]+(\/[0-9]+)?$/.test(cls)) {
856
845
  classesToGenerate.add(cls);
857
846
  }
@@ -895,7 +884,7 @@ TailwindCompiler.prototype.addCriticalBlockingStylesSync = function () {
895
884
  variableSuffixes: []
896
885
  };
897
886
  } else {
898
- // Try to get classes from cache (most efficient - only generate what was used before)
887
+ // Fall back to the cache (only generate what was used before)
899
888
  const cached = localStorage.getItem('tailwind-cache');
900
889
  let cachedClasses = new Set();
901
890
 
@@ -904,17 +893,14 @@ TailwindCompiler.prototype.addCriticalBlockingStylesSync = function () {
904
893
  const parsed = JSON.parse(cached);
905
894
  const cacheEntries = Object.values(parsed);
906
895
 
907
- // Extract classes from cache keys (format: "class1,class2-themeHash")
908
896
  for (const entry of cacheEntries) {
909
- // Find the cache entry with the most recent timestamp
910
897
  const mostRecent = cacheEntries.sort((a, b) => (b.timestamp || 0) - (a.timestamp || 0))[0];
911
898
  if (mostRecent && mostRecent.css) {
912
- // Extract class names from generated CSS
913
899
  const classMatches = mostRecent.css.match(/\.([a-zA-Z0-9_-]+(?::[a-zA-Z0-9_-]+)*)\s*{/g);
914
900
  if (classMatches) {
915
901
  for (const match of classMatches) {
916
902
  const className = match.replace(/^\./, '').replace(/\s*{.*$/, '');
917
- // Only include utility classes (not Tailwind native like red-500)
903
+ // Utility classes only (not native like red-500)
918
904
  if (/^(border|bg|text|ring|outline|decoration|caret|accent|fill|stroke)-[a-z0-9-]+(\/[0-9]+)?$/.test(className.split(':').pop())) {
919
905
  cachedClasses.add(className);
920
906
  }
@@ -962,14 +948,12 @@ TailwindCompiler.prototype.addCriticalBlockingStylesSync = function () {
962
948
  }
963
949
  }
964
950
 
965
- // If no classes found, generate utilities for all color variables
951
+ // No classes found: generate every color-* utility to prevent flash.
966
952
  if (!usedData || !usedData.classes || usedData.classes.length === 0) {
967
- // Generate utilities for all color-* variables to prevent flash
968
953
  const colorVars = Array.from(cssVariables.entries())
969
954
  .filter(([name]) => name.startsWith('color-'));
970
955
 
971
956
  if (colorVars.length > 0) {
972
- // Create synthetic classes for all color utilities (text, bg, border)
973
957
  const syntheticClasses = [];
974
958
  for (const [varName] of colorVars) {
975
959
  const suffix = varName.replace('color-', '');
@@ -1046,16 +1030,12 @@ TailwindCompiler.prototype.generateSynchronousUtilities = function () {
1046
1030
  // Ignore parsing errors
1047
1031
  }
1048
1032
 
1049
- // Method 3: Check computed styles from :root (if available)
1050
- // Run even during readyState === 'loading' getComputedStyle resolves
1051
- // the cascade from cross-origin stylesheets (CDN builds) that the CSSOM
1052
- // methods above cannot read. See addCriticalBlockingStylesSync for the
1053
- // full rationale: without this, CDN-hosted pages capture no variables
1054
- // synchronously and variable-derived utilities flash as currentColor.
1033
+ // Method 3: Computed styles from :root. Runs even during 'loading' —
1034
+ // resolves cross-origin (CDN) vars the CSSOM methods can't; see
1035
+ // addCriticalBlockingStylesSync.
1055
1036
  try {
1056
1037
  if (document.documentElement) {
1057
1038
  const rootStyles = getComputedStyle(document.documentElement);
1058
- // Extract all CSS variables, not just color ones
1059
1039
  const allProps = rootStyles.length;
1060
1040
  for (let i = 0; i < allProps; i++) {
1061
1041
  const prop = rootStyles[i];
@@ -1074,14 +1054,12 @@ TailwindCompiler.prototype.generateSynchronousUtilities = function () {
1074
1054
  // Method 4: Scan HTML source directly for class attributes
1075
1055
  try {
1076
1056
  const htmlSource = document.documentElement.outerHTML;
1077
- // Extract all class attributes from HTML source
1078
1057
  const classRegex = /class=["']([^"']+)["']/gi;
1079
1058
  let classMatch;
1080
1059
  while ((classMatch = classRegex.exec(htmlSource)) !== null) {
1081
1060
  const classString = classMatch[1];
1082
1061
  const classes = classString.split(/\s+/).filter(Boolean);
1083
1062
  for (const cls of classes) {
1084
- // Match common color utility patterns (more comprehensive)
1085
1063
  if (/^(border|bg|text|ring|outline|decoration|caret|accent|fill|stroke)-[a-z0-9-]+(\/[0-9]+)?$/.test(cls)) {
1086
1064
  commonColorClasses.add(cls);
1087
1065
  }
@@ -2430,8 +2408,7 @@ TailwindCompiler.prototype.parseClassName = function (className) {
2430
2408
 
2431
2409
 
2432
2410
 
2433
- // Compilation methods
2434
- // Main compilation logic and utility generation
2411
+ // Compilation logic and utility generation
2435
2412
 
2436
2413
  // Generate utilities from CSS variables
2437
2414
  TailwindCompiler.prototype.generateUtilitiesFromVars = function (cssText, usedData) {
@@ -2579,16 +2556,10 @@ TailwindCompiler.prototype.generateUtilitiesFromVars = function (cssText, usedDa
2579
2556
  generateUtility(className, css);
2580
2557
  }
2581
2558
 
2582
- // Check for opacity variants of this utility. Collect the
2583
- // bare opacity base class (e.g. `bg-black/10`) rather than the
2584
- // full prefixed class (e.g. `backdrop:bg-black/10`). generateUtility
2585
- // discovers and applies variant prefixes itself by matching the
2586
- // trailing segment after the last `:`, so passing it the bare base
2587
- // lets its variant loop emit the correct selector
2588
- // (`.backdrop\:bg-black\/10::backdrop`). Passing the prefixed class
2589
- // instead made generateUtility treat it as a plain base class and
2590
- // emit a malformed `.backdrop\:bg-black\/10 { … }` rule with no
2591
- // `::backdrop` pseudo-element.
2559
+ // Opacity variants: collect the bare base (e.g. `bg-black/10`),
2560
+ // not the prefixed class generateUtility discovers variant
2561
+ // prefixes itself, so passing the bare base emits the correct
2562
+ // selector (`.backdrop\:bg-black\/10::backdrop`).
2592
2563
  const opacityBaseClasses = new Set();
2593
2564
  for (const cls of usedClasses) {
2594
2565
  // Parse the class to extract the base utility name
@@ -2648,16 +2619,9 @@ TailwindCompiler.prototype.generateCustomUtilities = function (usedData) {
2648
2619
  return className.replace(/[^a-zA-Z0-9-]/g, '\\$&');
2649
2620
  };
2650
2621
 
2651
- // Helper to replace & in CSS selectors (not in property values or comments)
2652
- // IMPORTANT: For CSS nesting, we should NOT replace & in nested selectors
2653
- // The & should remain as-is so CSS nesting works correctly
2654
- // This function should only be used for legacy/flattened CSS, not nested CSS
2622
+ // Replace & in selectors legacy/flattened CSS only; nested CSS keeps &
2623
+ // as-is so native nesting works.
2655
2624
  const replaceAmpersandInSelectors = (cssText, replacement) => {
2656
- // For full blocks with nested rules, don't replace & at all - preserve CSS nesting
2657
- // Check if this looks like nested CSS:
2658
- // - Has & followed by :, ., [, or whitespace then { (nested selector)
2659
- // - Has ] & (attribute selector followed by &, like [dir=rtl] &)
2660
- // - Has & on its own line followed by :, ., or [ (common nested pattern)
2661
2625
  const hasNestedSelectors =
2662
2626
  /&\s*[:\.\[{]/.test(cssText) || // &:not(), &::before, &[attr], & {
2663
2627
  /&\s*\n\s*[:\.\[{]/.test(cssText) || // & on new line followed by selector
@@ -3192,13 +3156,9 @@ TailwindCompiler.prototype.compile = async function () {
3192
3156
  // Fetch CSS content once for initial compilation
3193
3157
  const themeCss = await this.fetchThemeContent();
3194
3158
  if (themeCss) {
3195
- // Extract and cache custom utilities. We scan framework CSS too
3196
- // because the generator needs to know about semantic classes
3197
- // like .brand / .row / .col to emit their responsive/state
3198
- // variants (e.g. md:row, hover:brand). Base-form re-emission is
3199
- // suppressed in generateCustomUtilities ("Skip generating base
3200
- // utility - it already exists in the CSS"); duplicate captures
3201
- // are collapsed at the end of extractCustomUtilities.
3159
+ // Extract custom utilities. Framework CSS is scanned too so the
3160
+ // generator can emit variants of semantic classes (md:row,
3161
+ // hover:brand); base forms are suppressed in generateCustomUtilities.
3202
3162
  const discoveredCustomUtilities = this.extractCustomUtilities(themeCss);
3203
3163
  for (const [name, value] of discoveredCustomUtilities.entries()) {
3204
3164
  this.customUtilities.set(name, value);
@@ -3357,25 +3317,19 @@ TailwindCompiler.prototype.compile = async function () {
3357
3317
 
3358
3318
 
3359
3319
 
3360
- // DOM observation and event handling
3361
- // Methods for watching DOM changes and triggering recompilation
3320
+ // DOM observation: watch for changes and trigger recompilation
3362
3321
 
3363
3322
  // Setup component load listener and MutationObserver
3364
3323
  TailwindCompiler.prototype.setupComponentLoadListener = function () {
3365
- // Use a single debounced handler for all component-related events
3366
3324
  const debouncedCompile = this.debounce(() => {
3367
3325
  if (!this.isCompiling) {
3368
3326
  this.compile();
3369
3327
  }
3370
3328
  }, this.options.debounceTime);
3371
3329
 
3372
- // Listen for custom events when components are loaded/processed
3373
- // Support both old (manifest) and new (manifest) event names for compatibility
3330
+ // Recompile when components load/process; re-scan so component HTML is covered.
3374
3331
  const handleComponentEvent = () => {
3375
- // If we haven't scanned static classes yet, trigger a full re-scan
3376
- // This ensures component HTML files are scanned for utility classes
3377
3332
  if (!this.hasScannedStatic) {
3378
- // Reset the scan promise to allow re-scanning
3379
3333
  this.staticScanPromise = null;
3380
3334
  this.hasScannedStatic = false;
3381
3335
  }
@@ -3389,12 +3343,9 @@ TailwindCompiler.prototype.setupComponentLoadListener = function () {
3389
3343
  document.addEventListener('manifest:components-processed', handleComponentEvent);
3390
3344
  document.addEventListener('manifest:components-ready', handleComponentEvent);
3391
3345
 
3392
- // Listen for route changes but don't recompile unnecessarily
3346
+ // On route change, recompile only if genuinely new dynamic classes appeared.
3393
3347
  document.addEventListener('manifest:route-change', (event) => {
3394
- // Only trigger compilation if we detect new dynamic classes
3395
- // The existing MutationObserver will handle actual DOM changes
3396
3348
  if (this.hasScannedStatic) {
3397
- // Wait longer for route content to fully load before checking
3398
3349
  setTimeout(() => {
3399
3350
  const currentDynamicCount = this.dynamicClassCache.size;
3400
3351
  const currentClassesHash = this.lastClassesHash;
@@ -3405,10 +3356,9 @@ TailwindCompiler.prototype.setupComponentLoadListener = function () {
3405
3356
  const dynamicClasses = Array.from(this.dynamicClassCache);
3406
3357
  const newClassesHash = dynamicClasses.sort().join(',');
3407
3358
 
3408
- // Only compile if we found genuinely new classes, not just code processing artifacts
3409
3359
  if (newDynamicCount > currentDynamicCount && newClassesHash !== currentClassesHash) {
3410
3360
  const newClasses = dynamicClasses.filter(cls =>
3411
- // Filter out classes that are likely from code processing
3361
+ // Ignore highlight/code-processing artifacts
3412
3362
  !cls.includes('hljs') &&
3413
3363
  !cls.startsWith('language-') &&
3414
3364
  !cls.includes('copy') &&
@@ -3419,11 +3369,11 @@ TailwindCompiler.prototype.setupComponentLoadListener = function () {
3419
3369
  debouncedCompile();
3420
3370
  }
3421
3371
  }
3422
- }, 300); // Longer delay to let code processing finish
3372
+ }, 300); // let code processing finish
3423
3373
  }
3424
3374
  });
3425
3375
 
3426
- // Use a single MutationObserver for all DOM changes
3376
+ // Single MutationObserver for all DOM changes
3427
3377
  const observer = new MutationObserver((mutations) => {
3428
3378
  let shouldRecompile = false;
3429
3379
 
@@ -3510,15 +3460,10 @@ TailwindCompiler.prototype.setupComponentLoadListener = function () {
3510
3460
  });
3511
3461
  };
3512
3462
 
3513
- // Start processing with initial compilation. DOM observation is owned by
3514
- // setupComponentLoadListener (called separately from main.js init) — that one
3515
- // uses an incremental staticClassCache lookup per mutation, which scales to
3516
- // thousands of elements. A previous version of this method also installed its
3517
- // own MutationObserver here that called getUsedClasses() on EVERY mutation
3518
- // (a full document-wide O(N) DOM scan), so on a busy page with N≥3000 the
3519
- // scan cost exceeded the inter-mutation interval and the main thread froze
3520
- // (~100% CPU on docs site, 3042 elements). That observer was redundant with
3521
- // the incremental one and has been removed.
3463
+ // Initial compilation only. DOM observation is owned by
3464
+ // setupComponentLoadListener (incremental, scales to thousands of elements);
3465
+ // don't add a per-mutation getUsedClasses() scan here it froze the main
3466
+ // thread on busy pages.
3522
3467
  TailwindCompiler.prototype.startProcessing = async function () {
3523
3468
  if (this.usesStaticPrerenderUtilities) return;
3524
3469
  try {
@@ -3531,15 +3476,11 @@ TailwindCompiler.prototype.startProcessing = async function () {
3531
3476
 
3532
3477
 
3533
3478
 
3534
- // Utilities initialization
3535
- // Initialize compiler and set up event listeners
3479
+ // Utilities initialization: create compiler, set up event listeners
3536
3480
 
3537
- // Detect operating system and stamp it on <html data-os> so OS variants
3538
- // (mac:, ios:, windows:, …) and the *-only visibility classes can resolve in
3539
- // pure CSS. There is no CSS media feature for OS, so this one-time read is the
3540
- // minimum required. Runs synchronously at script load (documentElement exists
3541
- // during head parsing) to set the marker before first paint. Honors a value
3542
- // already present (e.g. written by the prerenderer or set manually).
3481
+ // Stamp <html data-os> so OS variants (mac:, ios:, …) resolve in pure CSS
3482
+ // (no CSS media feature for OS). Runs synchronously before first paint;
3483
+ // honors an existing value (prerenderer or manual).
3543
3484
  function detectOS() {
3544
3485
  try {
3545
3486
  const html = document.documentElement;
@@ -3561,13 +3502,9 @@ function detectOS() {
3561
3502
  }
3562
3503
  detectOS();
3563
3504
 
3564
- // Register the device/OS variants with Tailwind too. The Manifest compiler
3565
- // applies these variants to its own theme-derived/semantic utilities, but
3566
- // standard Tailwind utilities (px-4, flex, …) are emitted by Tailwind's own
3567
- // engine, which only knows its built-in variants. A `<style type="text/tailwindcss">`
3568
- // carrying @custom-variant definitions teaches Tailwind the same variants, so
3569
- // touch:px-4 / mac:flex / cursor:gap-2 resolve like sm:/hover:. Tailwind's
3570
- // browser build reprocesses when this style is added, so load order is moot.
3505
+ // Teach Tailwind the same device/OS variants via @custom-variant so its own
3506
+ // utilities (px-4, flex) get touch:/mac:/cursor: like sm:/hover: the Manifest
3507
+ // compiler only applies them to its own utilities.
3571
3508
  function injectTailwindVariants() {
3572
3509
  try {
3573
3510
  if (document.getElementById('manifest-tailwind-variants')) return;
@@ -3598,16 +3535,12 @@ const compiler = new TailwindCompiler();
3598
3535
  // Expose utilities compiler for optional integration
3599
3536
  window.ManifestUtilities = compiler;
3600
3537
 
3601
- // Log when DOM is ready
3602
3538
  if (document.readyState === 'loading') {
3603
3539
  document.addEventListener('DOMContentLoaded', () => {
3604
- // DOM ready
3605
3540
  });
3606
3541
  } else {
3607
- // DOM already ready
3608
3542
  }
3609
3543
 
3610
- // Log first paint if available
3611
3544
  if ('PerformanceObserver' in window) {
3612
3545
  try {
3613
3546
  const paintObserver = new PerformanceObserver((list) => {
@@ -3620,7 +3553,7 @@ if ('PerformanceObserver' in window) {
3620
3553
  }
3621
3554
  }
3622
3555
 
3623
- // Also handle DOMContentLoaded for any elements that might be added later
3556
+ // Recompile on DOMContentLoaded for late-added elements
3624
3557
  document.addEventListener('DOMContentLoaded', () => {
3625
3558
  if (!compiler.usesStaticPrerenderUtilities && !compiler.isCompiling) {
3626
3559
  compiler.compile();
@@ -1,44 +1,11 @@
1
- /* Manifest Virtual — in-flow list/table/grid/gallery virtualization for Alpine.
1
+ /* Manifest Virtual — in-flow list/table/grid/gallery/masonry virtualization for Alpine.
2
2
  *
3
- * Renders only the rows visible in the scroll viewport (plus an overscan
4
- * buffer). Rows stay in NORMAL FLOW between a top + bottom spacer whose heights
5
- * stand in for the rows above/below the window — so the same markup + CSS works
6
- * virtualized or not. No absolute positioning, so shared columns (native
7
- * <table> and .grid-table), sticky cells, and flex-wrap all behave normally.
8
- *
9
- * Usage — wrap an x-for template in the scrolling container:
10
- *
11
- * <div x-virtual style="height: 600px; overflow: auto">
12
- * <template x-for="row in $x.customers" :key="row.id">
13
- * <div class="grid-row"> … cells … </div>
14
- * </template>
15
- * </div>
16
- *
17
- * The template may be nested (e.g. inside <table><tbody>); the plugin finds it
18
- * and hosts spacers/rows in its parent.
19
- *
20
- * Options (object expression on the directive):
21
- *
22
- * estimate Initial per-row height in px (default 50), or { width, height }
23
- * for galleries. Used for unmeasured rows; closer = less drift.
24
- * overscan Rows/lines to render above/below the window (default 3).
25
- * mode 'rows' | 'gallery' | 'masonry'. Auto-detected if omitted
26
- * (masonry must be set explicitly).
27
- *
28
- * Modes (how items fill lines — the spacer element is auto-detected separately):
29
- * rows — one item per line: stacked lists, native <table>, and
30
- * .grid-table. The spacer is a div, a <tr>, or a grid cell
31
- * depending on the container.
32
- * gallery — many items per line (flex-wrap); items pack N per line and
33
- * whole lines render so flex sizing stays correct. Ragged
34
- * width + height supported.
35
- * masonry — independent-size items packed into the shortest column
36
- * (absolute positioning); set explicitly with mode: 'masonry'.
37
- *
38
- * Notes:
39
- * - Column widths should be content-independent (table-layout: fixed, fixed/%
40
- * grid tracks) or columns shift as the window changes. Stripe by data index,
41
- * not :nth-child. See the docs for these table caveats.
3
+ * Renders only rows in the viewport (plus overscan). Line modes keep rows in NORMAL
4
+ * FLOW between top/bottom spacers standing in for off-window rows, so the same markup
5
+ * works virtualized or not and shared columns/sticky/flex-wrap behave normally;
6
+ * masonry positions absolutely. Options: estimate (px or {width,height}), overscan,
7
+ * mode ('rows'|'gallery'|'masonry'; masonry must be explicit, others auto-detect).
8
+ * Column widths must be content-independent or columns shift as the window changes.
42
9
  */
43
10
 
44
11
  function initializeVirtualPlugin() {
@@ -48,8 +15,7 @@ function initializeVirtualPlugin() {
48
15
  // --- Find the template (may be nested, e.g. table > tbody > template) ---
49
16
  const template = el.querySelector('template[x-for]') || el.querySelector(':scope template');
50
17
  if (!template || !template.getAttribute('x-for')) {
51
- // Stay quiet on re-init of an already-virtualized container (x-for was
52
- // stripped on the first pass); only warn on genuine misuse.
18
+ // Quiet on re-init of an already-virtualized container (x-for stripped first pass).
53
19
  if (!el.querySelector('[data-virtual-spacer]')) {
54
20
  console.warn('[x-virtual] expects a descendant <template> with x-for, e.g. <template x-for="row in $x.items" :key="row.id">…');
55
21
  }
@@ -65,8 +31,7 @@ function initializeVirtualPlugin() {
65
31
  const sourceExpr = m[2].trim();
66
32
  const keyExpr = template.getAttribute(':key') || template.getAttribute('x-bind:key') || `${itemName}.id`;
67
33
 
68
- // Strip x-for/:key so Alpine doesn't render the full list; keep the
69
- // template in the DOM as our clone source.
34
+ // Strip x-for/:key so Alpine won't render the full list; keep the template as clone source.
70
35
  template.removeAttribute('x-for');
71
36
  template.removeAttribute(':key');
72
37
  template.removeAttribute('x-bind:key');
@@ -89,9 +54,8 @@ function initializeVirtualPlugin() {
89
54
  const isGallery = mode === 'gallery';
90
55
  const isMasonry = mode === 'masonry';
91
56
 
92
- // Masonry options: fixed `columns` or target `columnWidth`; optional
93
- // per-item `span` (cols) and `height` (px) fns from data for ~zero
94
- // settling; `gap` px (falls back to CSS gap).
57
+ // Masonry: fixed `columns` or target `columnWidth`; optional per-item `span`/
58
+ // `height` fns from data for ~zero settling; `gap` px (falls back to CSS gap).
95
59
  const colsOpt = Number(options.columns) > 0 ? Math.floor(options.columns) : 0;
96
60
  const colWidthOpt = Number(options.columnWidth) > 0 ? Number(options.columnWidth) : 0;
97
61
  const spanFn = typeof options.span === 'function' ? options.span : null;
@@ -147,9 +111,8 @@ function initializeVirtualPlugin() {
147
111
  else rebuildSingle();
148
112
  }
149
113
 
150
- // Skyline packer: place each item in the column band (of `span` width)
151
- // with the lowest top. Sizes are independent; only positions depend on
152
- // order — so the whole layout is computed here in JS, no rendering.
114
+ // Skyline packer: place each item in the `span`-wide column band with the lowest
115
+ // top. Positions depend only on order, so the whole layout is computed in JS.
153
116
  function rebuildMasonry() {
154
117
  const cw = contentWidth();
155
118
  const g = masonryGap;
@@ -279,8 +242,8 @@ function initializeVirtualPlugin() {
279
242
  scheduleMeasure(keys);
280
243
  }
281
244
 
282
- // Masonry: render items whose packed box intersects the viewport,
283
- // absolutely positioned at their computed (x, y, w).
245
+ // Masonry: render items whose packed box intersects the viewport, positioned
246
+ // absolutely at their computed (x, y, w).
284
247
  function renderMasonry() {
285
248
  if (!data.length) { clearRows(); sizer.style.height = '0px'; return; }
286
249
  const vh = scrollEl.clientHeight;
@@ -532,7 +495,6 @@ function buildKeyFn(itemName, keyExpr) {
532
495
  }
533
496
  }
534
497
 
535
- // Track initialization to prevent duplicates
536
498
  let virtualPluginInitialized = false;
537
499
 
538
500
  function ensureVirtualPluginInitialized() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mnfst",
3
- "version": "0.5.163",
3
+ "version": "0.5.164",
4
4
  "private": false,
5
5
  "workspaces": [
6
6
  "templates/starter",