mnfst 0.5.244 → 0.5.245

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.244';
5
+ const MANIFEST_BUILD_VERSION = '0.5.245';
6
6
 
7
7
  /* Manifest Data Sources - Appwrite Integration */
8
8
 
@@ -190,7 +190,7 @@
190
190
  function normalize(raw, el, typeFromModifier) {
191
191
  const cfg = Object.assign({}, raw || {});
192
192
  cfg.type = (cfg.type || typeFromModifier || 'line').toLowerCase();
193
- cfg.height = num(cfg.height || el.getAttribute('height'), 240);
193
+ cfg.height = num(cfg.height || el.getAttribute('height') || undefined, cfg.type === 'map' ? 0 : 240); // maps size from width
194
194
  cfg.stacked = !!cfg.stacked;
195
195
  cfg.legend = cfg.legend !== false;
196
196
  cfg.axis = cfg.axis !== false;
@@ -507,9 +507,23 @@
507
507
  function drawCartesian(state, root, width, height) {
508
508
  const cfg = state.config, d3 = state.d3;
509
509
  const m = { top: 10, right: 14, bottom: 26, left: 40 };
510
- const iw = width - m.left - m.right;
511
- const ih = height - m.top - m.bottom;
512
510
  const labels = cfg.labels.length ? cfg.labels : cfg.series[0].data.map((_, i) => i + 1);
511
+ const marks = cfg.axis ? axisMarks(cfg, labels) : [];
512
+ let iw = width - m.left - m.right;
513
+ // Widen margins (each side capped at 20% of width) so end labels sit
514
+ // centred on their points; past the cap, ends clamp inside the frame.
515
+ if (marks.length) {
516
+ const f = marks[0], l = marks[marks.length - 1];
517
+ const hf = labelWidth(root, [f.label]) / 2, hl = labelWidth(root, [l.label]) / 2;
518
+ const base = { left: m.left, right: m.right }, cap = width * 0.2;
519
+ for (let pass = 0; pass < 2; pass++) {
520
+ const c = xScheme(cfg, d3, labels.length, iw).x;
521
+ m.left = Math.max(base.left, Math.min(cap, Math.ceil(hf - c(f.at)) + 1));
522
+ m.right = Math.max(base.right, Math.min(cap, Math.ceil(hl - (iw - c(l.at))) + 1));
523
+ iw = width - m.left - m.right;
524
+ }
525
+ }
526
+ const ih = height - m.top - m.bottom;
513
527
  const locale = (() => { try { return Alpine.store('locale')?.current || document.documentElement.lang || 'en'; } catch (_) { return 'en'; } })();
514
528
  const nf = (() => { try { return new Intl.NumberFormat(locale, { notation: 'compact', maximumFractionDigits: 1 }); } catch (_) { return { format: String }; } })();
515
529
 
@@ -551,41 +565,73 @@
551
565
  else if (cfg.type === 'scatter') drawScatter(state, plot, labels, iw, ih, y);
552
566
  else drawLines(state, plot, labels, iw, ih, y); // line + area
553
567
 
554
- // x axis labels (banded), thinned so they never overlap
568
+ // x axis labels (banded), evenly thinned so they never overlap
555
569
  if (cfg.axis) {
556
- const xb = d3.scaleBand().domain(labels.map(String)).range([0, iw]).padding(0.1);
557
- xAxisTicks(root, labels, xb, iw, m).forEach(([i, cx]) => {
558
- text(plot, labels[i], { x: cx, y: ih + 16, 'text-anchor': 'middle' });
570
+ xAxisTicks(root, marks, xScheme(cfg, d3, labels.length, iw).x, iw, m).forEach(([t, cx]) => {
571
+ text(plot, t.label, { x: cx, y: ih + 16, 'text-anchor': 'middle' });
559
572
  });
560
573
  }
561
574
  if (cfg.legend && cfg.series.length > 1) drawLegend(state);
562
575
  }
563
576
 
564
- // Indices + x of the category labels to draw: every Nth by measured width,
565
- // first and last kept where they fit, ends clamped inside the svg.
566
- function xAxisTicks(root, labels, xb, iw, m) {
577
+ // One index-keyed x scheme per chart (duplicate labels stay distinct), shared
578
+ // by marks and axis. Bars (and combo lines over bars) use band centres.
579
+ function xScheme(cfg, d3, n, w) {
580
+ const idx = Array.from({ length: n }, (_, i) => i);
581
+ const candle = cfg.type === 'candlestick' || cfg.type === 'ohlc';
582
+ const banded = candle || cfg.type === 'bar' || (cfg.type === 'combo' && cfg.series.some(s => { const t = seriesType(cfg, s); return t !== 'line' && t !== 'area'; }));
583
+ if (banded) {
584
+ const band = d3.scaleBand().domain(idx).range([0, w]).padding(candle ? 0.3 : 0.2);
585
+ const bw = band.bandwidth();
586
+ return { band, step: band.step(), x: i => band(i) + bw / 2 };
587
+ }
588
+ const p = d3.scalePoint().domain(idx).range([0, w]).padding(0.5);
589
+ return { step: typeof p.step === 'function' ? p.step() : w / Math.max(1, n), x: i => p(i) };
590
+ }
591
+
592
+ // Axis label candidates: author `ticks` ([index] or [{ at, label }]) or every label.
593
+ function axisMarks(cfg, labels) {
567
594
  const n = labels.length;
568
- if (!n) return [];
569
- const step = typeof xb.step === 'function' ? xb.step() : iw / n;
570
- const center = i => xb(String(labels[i])) + xb.bandwidth() / 2;
571
- const sample = n <= 60 ? labels : [labels[0], labels[n - 1]].concat(labels.slice().sort((a, b) => String(b).length - String(a).length).slice(0, 30));
572
- const minSep = labelWidth(root, sample) + 6;
573
- const every = Math.max(1, Math.ceil(minSep / step));
574
- const edgeHalf = {};
575
- const xAt = i => {
576
- if (i !== 0 && i !== n - 1) return center(i);
577
- if (edgeHalf[i] == null) edgeHalf[i] = labelWidth(root, [labels[i]]) / 2;
578
- return Math.min(Math.max(center(i), -m.left + edgeHalf[i]), iw + m.right - edgeHalf[i]);
579
- };
580
- const fits = (a, b) => Math.abs(xAt(b) - xAt(a)) >= minSep;
581
- const keep = [];
582
- for (let i = 0; i < n - 1; i += every) keep.push(i);
583
- if (n > 1) {
584
- while (keep.length > 1 && !fits(keep[keep.length - 1], n - 1)) keep.pop();
585
- if (!keep.length || fits(keep[keep.length - 1], n - 1)) keep.push(n - 1);
586
- } else keep.push(0);
587
- while (keep.length > 2 && !fits(keep[0], keep[1])) keep.splice(1, 1);
588
- return keep.map(i => [i, xAt(i)]);
595
+ if (!Array.isArray(cfg.ticks)) return labels.map((l, i) => ({ at: i, label: l }));
596
+ const seen = new Set();
597
+ return cfg.ticks
598
+ .map(t => t && typeof t === 'object' && !Array.isArray(t) ? { at: tickIndex(t.at), label: t.label } : { at: tickIndex(t) })
599
+ .filter(t => t.at >= 0 && t.at < n && !seen.has(t.at) && seen.add(t.at))
600
+ .map(t => ({ at: t.at, label: t.label != null ? t.label : labels[t.at] }))
601
+ .sort((a, b) => a.at - b.at);
602
+ }
603
+
604
+ function tickIndex(v) {
605
+ const ok = (typeof v === 'number' && isFinite(v)) || (typeof v === 'string' && v.trim() !== '' && isFinite(+v));
606
+ return ok ? Math.round(+v) : -1;
607
+ }
608
+
609
+ // Marks + x to draw: the most evenly spaced subset (first and last kept)
610
+ // whose measured labels don't overlap; ends clamped inside the svg.
611
+ function xAxisTicks(root, marks, xc, iw, m) {
612
+ const N = marks.length;
613
+ if (!N) return [];
614
+ const GAP = 6;
615
+ let half;
616
+ if (N <= 60) {
617
+ const ws = labelWidths(root, marks.map(t => t.label));
618
+ half = k => ws[k] / 2;
619
+ } else {
620
+ const sample = [marks[0], marks[N - 1]].concat(marks.slice().sort((a, b) => String(b.label).length - String(a.label).length).slice(0, 30));
621
+ const w = labelWidth(root, sample.map(t => t.label));
622
+ half = () => w / 2;
623
+ }
624
+ const center = k => xc(marks[k].at);
625
+ const xAt = k => Math.min(Math.max(center(k), -m.left + half(k)), iw + m.right - half(k));
626
+ const pick = c => Array.from({ length: c }, (_, j) => Math.round(j * (N - 1) / (c - 1)));
627
+ const fits = ks => ks.every((k, j) => !j || xAt(k) - xAt(ks[j - 1]) >= half(k) + half(ks[j - 1]) + GAP);
628
+ const span = center(N - 1) - center(0);
629
+ const minW = N <= 60 ? Math.min(...marks.map((_, k) => half(k) * 2)) : half(0) * 2;
630
+ for (let c = Math.min(N, Math.floor(span / (minW + GAP)) + 1); c > 1; c--) {
631
+ const ks = pick(c);
632
+ if (fits(ks)) return ks.map(k => [marks[k], xAt(k)]);
633
+ }
634
+ return [[marks[0], xAt(0)]];
589
635
  }
590
636
 
591
637
  // `list` lets combo pass a subset of series with their original indices
@@ -593,15 +639,14 @@
593
639
  // bar rects per index, highlighted from the dense hover columns.
594
640
  function drawLines(state, plot, labels, iw, ih, y, list, barsAt) {
595
641
  const cfg = state.config, d3 = state.d3;
596
- const x = d3.scalePoint().domain(labels.map(String)).range([0, iw]).padding(0.5);
642
+ const X = xScheme(cfg, d3, labels.length, iw), x = X.x, step = X.step;
597
643
  const curve = curveFor(d3, cfg.curve);
598
644
  const items = list || cfg.series.map((s, i) => ({ s, i, area: cfg.type === 'area' }));
599
645
  // Dense: no per-point dots; hover columns carry the tooltip instead.
600
- const step = typeof x.step === 'function' ? x.step() : iw / Math.max(1, labels.length);
601
646
  const dense = labels.length > 1 && step < 10;
602
647
  items.forEach(({ s, i: si, area }) => {
603
648
  const color = seriesColorVar(si, s.color);
604
- const points = labels.map((l, i) => [x(String(l)), y(num(s.data[i], 0))]);
649
+ const points = labels.map((l, i) => [x(i), y(num(s.data[i], 0))]);
605
650
  if (area) {
606
651
  const areaGen = d3.area().x(p => p[0]).y0(ih).y1(p => p[1]).curve(curve);
607
652
  const ap = svg('path', { class: 'area', d: areaGen(points), style: `--color-chart-color:${color}` }, plot);
@@ -623,7 +668,7 @@
623
668
  const g = svg('g', { class: 'hover' }, plot);
624
669
  const focus = items.map(({ s, i: si }) => svg('circle', { class: 'focus', r: 3, display: 'none', style: `--color-chart-color:${seriesColorVar(si, s.color)}` }, g));
625
670
  labels.forEach((l, i) => {
626
- const cx = x(String(l));
671
+ const cx = x(i);
627
672
  const hit = svg('rect', { class: 'hit', x: cx - step / 2, y: 0, width: step, height: ih, style: 'fill:transparent' }, g);
628
673
  applyTip(hit, cfg.series.map(s => (s.name ? s.name + ': ' : '') + num(s.data[i], 0)), cfg, tipHead(cfg, l));
629
674
  const bars = (barsAt && barsAt[i]) || [];
@@ -643,17 +688,17 @@
643
688
  // only applies to the full-series bar chart, not combo.
644
689
  function drawBars(state, plot, labels, iw, ih, y, list) {
645
690
  const cfg = state.config, d3 = state.d3;
646
- const x0 = d3.scaleBand().domain(labels.map(String)).range([0, iw]).padding(0.2);
691
+ const x0 = xScheme(cfg, d3, labels.length, iw).band;
647
692
  if (cfg.stacked && !list) {
648
693
  labels.forEach((l, i) => {
649
694
  let acc = 0;
650
695
  cfg.series.forEach((s, si) => {
651
696
  const v = num(s.data[i], 0);
652
697
  const yTop = y(acc + v), yBot = y(acc);
653
- const rect = svg('rect', { x: x0(String(l)), y: yTop, width: x0.bandwidth(), height: Math.max(0, yBot - yTop), style: `--color-chart-color:${seriesColorVar(si, s.color)}` }, plot);
698
+ const rect = svg('rect', { x: x0(i), y: yTop, width: x0.bandwidth(), height: Math.max(0, yBot - yTop), style: `--color-chart-color:${seriesColorVar(si, s.color)}` }, plot);
654
699
  applyTip(rect, (s.name ? s.name + ': ' : '') + v, cfg, tipHead(cfg, l));
655
700
  animateBar(rect, ih);
656
- if (cfg.dataLabels && (yBot - yTop) > 14) dataLabel(plot, v, x0(String(l)) + x0.bandwidth() / 2, (yTop + yBot) / 2, 'middle', 'central', 'inverse');
701
+ if (cfg.dataLabels && (yBot - yTop) > 14) dataLabel(plot, v, x0(i) + x0.bandwidth() / 2, (yTop + yBot) / 2, 'middle', 'central', 'inverse');
657
702
  acc += v;
658
703
  });
659
704
  });
@@ -666,11 +711,11 @@
666
711
  const s = o.s, si = o.i;
667
712
  const v = num(s.data[i], 0);
668
713
  const yy = y(Math.max(0, v));
669
- const rect = svg('rect', { x: x0(String(l)) + x1(String(k)), y: yy, width: x1.bandwidth(), height: Math.abs(y(v) - y(0)), style: `--color-chart-color:${seriesColorVar(si, s.color)}` }, plot);
714
+ const rect = svg('rect', { x: x0(i) + x1(String(k)), y: yy, width: x1.bandwidth(), height: Math.abs(y(v) - y(0)), style: `--color-chart-color:${seriesColorVar(si, s.color)}` }, plot);
670
715
  byIndex[i].push(rect);
671
716
  applyTip(rect, (s.name ? s.name + ': ' : '') + v, cfg, tipHead(cfg, l));
672
717
  animateBar(rect, ih);
673
- if (cfg.dataLabels) dataLabel(plot, v, x0(String(l)) + x1(String(k)) + x1.bandwidth() / 2, yy - 4);
718
+ if (cfg.dataLabels) dataLabel(plot, v, x0(i) + x1(String(k)) + x1.bandwidth() / 2, yy - 4);
674
719
  });
675
720
  });
676
721
  return byIndex;
@@ -694,10 +739,9 @@
694
739
  function drawCandles(state, plot, labels, iw, ih, y) {
695
740
  const cfg = state.config, d3 = state.d3;
696
741
  const cs = candleData(cfg.series[0].data);
697
- const x0 = d3.scaleBand().domain(labels.map(String)).range([0, iw]).padding(0.3);
698
- const bw = x0.bandwidth();
742
+ const X = xScheme(cfg, d3, labels.length, iw), bw = X.band.bandwidth();
699
743
  cs.forEach((c, i) => {
700
- const cx = x0(String(labels[i])) + bw / 2;
744
+ const cx = X.x(i);
701
745
  const up = c.c >= c.o;
702
746
  const g = svg('g', { class: up ? 'positive' : 'negative' }, plot);
703
747
  svg('line', { x1: cx, x2: cx, y1: y(c.h), y2: y(c.l) }, g);
@@ -715,14 +759,14 @@
715
759
 
716
760
  function drawScatter(state, plot, labels, iw, ih, y) {
717
761
  const cfg = state.config, d3 = state.d3;
718
- const x = d3.scalePoint().domain(labels.map(String)).range([0, iw]).padding(0.5);
762
+ const x = xScheme(cfg, d3, labels.length, iw).x;
719
763
  cfg.series.forEach((s, si) => {
720
764
  labels.forEach((l, i) => {
721
765
  const v = num(s.data[i], 0);
722
- const dot = svg('circle', { class: 'scatter', cx: x(String(l)), cy: y(v), r: 5, style: `--color-chart-color:${seriesColorVar(si, s.color)}` }, plot);
766
+ const dot = svg('circle', { class: 'scatter', cx: x(i), cy: y(v), r: 5, style: `--color-chart-color:${seriesColorVar(si, s.color)}` }, plot);
723
767
  applyTip(dot, (s.name ? s.name + ': ' : '') + v, cfg, tipHead(cfg, l));
724
768
  animate(dot, [{ opacity: 0, transform: 'scale(0)' }, { opacity: 1, transform: 'scale(1)' }], { duration: 350, delay: i * 25 });
725
- if (cfg.dataLabels) dataLabel(plot, v, x(String(l)), y(v) - 9);
769
+ if (cfg.dataLabels) dataLabel(plot, v, x(i), y(v) - 9);
726
770
  });
727
771
  });
728
772
  }
@@ -752,11 +796,13 @@
752
796
  // fallback). Sizes axis gutters to their content so charts sit balanced.
753
797
  function labelWidth(root, items, cls) {
754
798
  if (!items || !items.length) return 0;
799
+ return Math.max(...labelWidths(root, items, cls));
800
+ }
801
+ function labelWidths(root, items, cls) {
755
802
  const probe = svg('text', cls ? { class: cls, x: -9999, y: -9999 } : { x: -9999, y: -9999 }, root);
756
- let max = 0;
757
- for (const s of items) { probe.textContent = String(s); let w = 0; try { w = probe.getComputedTextLength(); } catch (_) { } if (!w) w = String(s).length * 7; if (w > max) max = w; }
803
+ const out = items.map(s => { probe.textContent = String(s == null ? '' : s); let w = 0; try { w = probe.getComputedTextLength(); } catch (_) { } return Math.ceil(w || String(s == null ? '' : s).length * 7); });
758
804
  root.removeChild(probe);
759
- return Math.ceil(max);
805
+ return out;
760
806
  }
761
807
 
762
808
  function drawGauge(state, root, width, height) {
@@ -2,7 +2,7 @@
2
2
 
3
3
  (function () {
4
4
 
5
- const MANIFEST_BUILD_VERSION = '0.5.244';
5
+ const MANIFEST_BUILD_VERSION = '0.5.245';
6
6
 
7
7
  /* Manifest Data Sources - Configuration */
8
8
 
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "manifest.appwrite.auth.js": "sha384-dsIWnH0qCAGSzAXMGrd8fvOrVl4tOIE6j5tC35JPTrQXZ56qLD36ARyvhR7vi3Id",
3
- "manifest.appwrite.data.js": "sha384-rC30q9rBFR/prFJ8rseKe2GBKoGEkfT6WLZaz5/lbdZzr+w6XDefgub/vyHgMc0A",
3
+ "manifest.appwrite.data.js": "sha384-bg/IZDy3pRsvCsopmIJQ1g5uc3KjpL6O1X/waOZLwDw0KjXuHsG8dRMkKdTdviP4",
4
4
  "manifest.appwrite.presences.js": "sha384-JyVDWFT69b7Svq8J8p9tNWe2v9PZNQBY5UyhtOl1vnuHFAX0CRQ3MA9SiKq5wzNh",
5
5
  "manifest.bindings.js": "sha384-lPtMf8Ykaic9gNVZKq8Scia1881ZeMAyxer3WCIkUNwwrgzGBWto1OXADUwjvORF",
6
- "manifest.charts.js": "sha384-vjUCZ1MWu7c4rT/25Ct+nw95+OYUDZ2ouc9pw/gVOXrYZ+iW+LI0+lwvEhXmeMcZ",
6
+ "manifest.charts.js": "sha384-FTLltd+0atALQQD5Wi9eASFnNYYzhxouH3HJlMkFrDfjTV9/8TZhEif7bMDqElq3",
7
7
  "manifest.chat.js": "sha384-88DfMfJLoi2oxclVD2iIMDkIGAExMXq5k47mekUredlicLaP3oZY5bwtwqs+TqDF",
8
8
  "manifest.code.js": "sha384-WGPlZE5Sp9GygUzJSmCdpLyYQTgvmEKc9BQS9Hmq1VTQwkqYJzPSWibHvPekcres",
9
9
  "manifest.color.js": "sha384-6Rv3LxyTcZNjrhtayQfqRdCx0uSZ4BiEbgEI98I62eTvp8Aw7LBIoNJ0Je1oktwL",
@@ -11,7 +11,7 @@
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-PNN+BUdyhlBcNW822on2vOdWlq9tR7+sJHZppI25OfF0w1FIU7Jj9JBvwlhnvNTt",
14
+ "manifest.data.js": "sha384-Ie+YDjD9X8mUgLqbRdgt6KLXNSB6ysP++yBs3W7JdhLr19xIkY9d54RLVGvwY6v7",
15
15
  "manifest.datepicker.js": "sha384-XnbeURni2X91ozVzatQcSjYYIS4MoW12dBRJnYAGBr0nCQ624heH5s+xtQSpLZX6",
16
16
  "manifest.defer.js": "sha384-pMRGqQKiEoJhpUcD40QeM4jEHau7sz9Xn/YCpiUamT1T4xJBZDSiYX+xnVrSWoq+",
17
17
  "manifest.device.js": "sha384-g+txmrEszYtuOvRelTp/c1wSNR2AtllVI9rx5NPPD7DzhVUnMNughonZykv2xlBW",
@@ -28,7 +28,7 @@
28
28
  "manifest.slides.js": "sha384-3uRTkyK9XPLmnxI2+igZlpi4EyPlU/7IHj5j3BZJJ2KN455vXyk99fiXV3feO/XY",
29
29
  "manifest.status.js": "sha384-MBO345ab7H6Uq1X2dZLt4Ge1Gcra+Z2qYApvlAeIV8CJhDDJrz2FPXpp+Kcp8zcM",
30
30
  "manifest.svg.js": "sha384-qr/8H9+2cIhSXNbRWRtIdrDlTOxKlNJqwEefHvCYoiBNW/kKhPTFa109Orv/EBnb",
31
- "manifest.sw.js": "sha384-pAkPjlgCScvndUFGJmMzi9XBRTIbUoUsiA8m1KPHCnYPMj1K0uR6mLy/998PxKc4",
31
+ "manifest.sw.js": "sha384-rK22P7FyWRLX/DuW34gS9OIShs4Jo/s5+10o9Y23dbrbktHY3XYfzRygMlIgK4IB",
32
32
  "manifest.tabs.js": "sha384-OIIcyaQnGS0eNYk4zgAFEDboNYect7THvUd9D0V6nqCeDOxQcoiqLrBEmmWVTTet",
33
33
  "manifest.tailwind.js": "sha384-r/Jvqb+Piz21jUts10BbVAJoKkShOdJRVFpywyi4HPIi/OfOJq5CtPyWAPbdkMgt",
34
34
  "manifest.toasts.js": "sha384-3zkWxzw693pxg3QDFsoYCA3CqYaLKmBaMArULXODD66Utolpewl0OAeTB92aWUVv",
@@ -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.244';
19
+ const BUILD_VERSION = '0.5.245';
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.244",
3
+ "version": "0.5.245",
4
4
  "private": false,
5
5
  "workspaces": [
6
6
  "templates/starter",