@bongos/core 1.24.49 → 1.24.51

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.
@@ -56,10 +56,9 @@ function fmtUsd(n) {
56
56
  return Math.round(Number(n)).toLocaleString();
57
57
  }
58
58
 
59
- function fmtDr(n) {
59
+ function fmtMoney(n) {
60
60
  if (n == null) return '—';
61
- if (n < 100) return `${Number(n).toFixed(2)} dr.`;
62
- return `${Math.round(Number(n)).toLocaleString()} dr.`;
61
+ return `$${fmtUsd(n)}`;
63
62
  }
64
63
 
65
64
  function fmtRelativeTime(iso) {
@@ -99,13 +98,13 @@ async function fetchJson(path) {
99
98
  // in ~756px of content width without wrapping.
100
99
 
101
100
  const SECTIONS = [
102
- { id: 'headline-tablet', label: 'Pulse' },
103
- { id: 'leader-tablet', label: 'Architects' },
104
- { id: 'foundry-tablet', label: 'Foundry' },
105
- { id: 'versions-tablet', label: 'Works' },
106
- { id: 'cost-tablet', label: 'Treasury' },
107
- { id: 'shipped-tablet', label: 'Inscribed' },
108
- { id: 'world-tablet', label: 'World' },
101
+ { id: 'headline-tablet', label: 'Overview' },
102
+ { id: 'leader-tablet', label: 'Builders' },
103
+ { id: 'foundry-tablet', label: 'Trends' },
104
+ { id: 'versions-tablet', label: 'In progress' },
105
+ { id: 'cost-tablet', label: 'Spending' },
106
+ { id: 'shipped-tablet', label: 'Shipped' },
107
+ { id: 'world-tablet', label: 'Snapshot' },
109
108
  ];
110
109
 
111
110
  let navObserver = null;
@@ -354,7 +353,7 @@ async function loadVersions() {
354
353
  if (rows.length === 0) {
355
354
  const p = document.createElement('p');
356
355
  p.className = 'empty';
357
- p.textContent = 'No endeavors underway just now.';
356
+ p.textContent = 'Nothing is in progress right now.';
358
357
  els.versions.appendChild(p);
359
358
  return;
360
359
  }
@@ -497,7 +496,7 @@ function renderCostFilterNote(data) {
497
496
  const filtered = Number(data.filtered_total_usd) || 0;
498
497
  const whole = Number(data.total_usd) || 0;
499
498
  const pct = whole > 0 ? Math.round((filtered / whole) * 100) : 0;
500
- note.textContent = `Breakdown filtered to ${parts.join(' · ')}: ${fmtDr(filtered)} of ${fmtDr(whole)} (${pct}% of all spend).`;
499
+ note.textContent = `Breakdown filtered to ${parts.join(' · ')}: ${fmtMoney(filtered)} of ${fmtMoney(whole)} (${pct}% of all spend).`;
501
500
  }
502
501
 
503
502
  async function loadCosts() {
@@ -522,7 +521,7 @@ async function loadCosts() {
522
521
  if (cats.length === 0) {
523
522
  const tag = document.createElement('span');
524
523
  tag.className = 'cost-cat';
525
- tag.textContent = `No ${curLabel()} yet spent.`;
524
+ tag.textContent = `No ${curLabel()} spent yet.`;
526
525
  els.costCats.appendChild(tag);
527
526
  } else {
528
527
  for (const c of cats) {
@@ -531,7 +530,7 @@ async function loadCosts() {
531
530
  const strong = document.createElement('strong');
532
531
  strong.textContent = c.category;
533
532
  tag.appendChild(strong);
534
- tag.append(` · ${fmtDr(c.total_usd)}`);
533
+ tag.append(` · ${fmtMoney(c.total_usd)}`);
535
534
  els.costCats.appendChild(tag);
536
535
  }
537
536
  }
@@ -557,7 +556,7 @@ async function loadCosts() {
557
556
  if (total > 0) {
558
557
  const pct = Math.round((unattributed / total) * 100);
559
558
  els.costAttributionSplit.textContent =
560
- `Attributed to builders: ${fmtDr(attributed)} · Shared keep (unattributed): ${fmtDr(unattributed)} (${pct}%).`;
559
+ `Attributed to builders: ${fmtMoney(attributed)} · Shared (unattributed): ${fmtMoney(unattributed)} (${pct}%).`;
561
560
  } else {
562
561
  els.costAttributionSplit.textContent = '';
563
562
  }
@@ -577,14 +576,14 @@ async function loadCosts() {
577
576
  : (b.display_name || b.github_login || `builder #${b.builder_id}`);
578
577
  strong.textContent = label;
579
578
  tag.appendChild(strong);
580
- tag.append(` · ${fmtDr(b.total_usd)}`);
579
+ tag.append(` · ${fmtMoney(b.total_usd)}`);
581
580
  els.costBuilders.appendChild(tag);
582
581
  rendered++;
583
582
  }
584
583
  if (rendered === 0) {
585
584
  const tag = document.createElement('span');
586
585
  tag.className = 'cost-cat';
587
- tag.textContent = `No ${curLabel()} yet attributed to a hand.`;
586
+ tag.textContent = `No ${curLabel()} attributed to a builder yet.`;
588
587
  els.costBuilders.appendChild(tag);
589
588
  }
590
589
  }
@@ -643,6 +642,10 @@ async function loadCosts() {
643
642
 
644
643
  if (!window.Chart) return; // Chart.js failed to load — skip
645
644
 
645
+ // The chart paints with the page's own tokens (task 1005089): a canvas cannot
646
+ // read var(), so the resolved values are read once per draw, in whichever mode
647
+ // the page is in. Spend is ink, value the --info tone; the faces are the pack's.
648
+ const t = chartTokens();
646
649
  const ctx = els.costChart.getContext('2d');
647
650
  if (costChart) costChart.destroy();
648
651
  costChart = new Chart(ctx, {
@@ -654,23 +657,23 @@ async function loadCosts() {
654
657
  label: `${curLabel()} spent`,
655
658
  data: cumCost,
656
659
  fill: true,
657
- borderColor: '#2a2110',
658
- backgroundColor: 'rgba(139, 111, 44, 0.16)',
660
+ borderColor: t.ink,
661
+ backgroundColor: t.inkWash,
659
662
  tension: 0.2,
660
663
  pointRadius: 2.5,
661
- pointBackgroundColor: '#2a2110',
664
+ pointBackgroundColor: t.ink,
662
665
  borderWidth: 1.5,
663
666
  },
664
667
  {
665
- label: 'value wrought',
668
+ label: 'value produced',
666
669
  data: cumValue,
667
670
  fill: false,
668
- borderColor: '#2f5a3f',
669
- backgroundColor: 'rgba(74, 125, 92, 0.18)',
671
+ borderColor: t.info,
672
+ backgroundColor: t.info,
670
673
  borderDash: [5, 4],
671
674
  tension: 0.2,
672
675
  pointRadius: 2.5,
673
- pointBackgroundColor: '#2f5a3f',
676
+ pointBackgroundColor: t.info,
674
677
  borderWidth: 1.5,
675
678
  },
676
679
  ],
@@ -678,7 +681,7 @@ async function loadCosts() {
678
681
  options: {
679
682
  responsive: true,
680
683
  maintainAspectRatio: false,
681
- font: { family: '"EB Garamond", Georgia, serif' },
684
+ font: { family: t.body },
682
685
  plugins: {
683
686
  legend: {
684
687
  display: true,
@@ -686,17 +689,19 @@ async function loadCosts() {
686
689
  labels: {
687
690
  boxWidth: 22,
688
691
  padding: 18,
689
- font: { family: '"EB Garamond", Georgia, serif', size: 13, style: 'italic' },
690
- color: '#6e5b3a',
692
+ font: { family: t.body, size: 13 },
693
+ color: t.inkSoft,
691
694
  },
692
695
  },
693
696
  tooltip: {
694
- backgroundColor: 'rgba(42, 33, 16, 0.92)',
695
- titleFont: { family: '"Cinzel", Georgia, serif', size: 11, weight: '500' },
696
- bodyFont: { family: '"EB Garamond", Georgia, serif', size: 13 },
697
+ backgroundColor: t.ink,
698
+ titleColor: t.card,
699
+ bodyColor: t.card,
700
+ titleFont: { family: t.body, size: 12, weight: '500' },
701
+ bodyFont: { family: t.body, size: 13 },
697
702
  padding: 10,
698
703
  callbacks: {
699
- label: (c) => `${c.dataset.label}: ${c.parsed.y.toLocaleString(undefined, { minimumFractionDigits: 0, maximumFractionDigits: 2 })} dr.`,
704
+ label: (c) => `${c.dataset.label}: ${c.parsed.y.toLocaleString(undefined, { minimumFractionDigits: 0, maximumFractionDigits: 2 })}`,
700
705
  },
701
706
  },
702
707
  },
@@ -706,28 +711,28 @@ async function loadCosts() {
706
711
  title: {
707
712
  display: true,
708
713
  text: `${curLabel()} (cumulative)`,
709
- font: { family: '"Cinzel", Georgia, serif', size: 11, weight: '500' },
710
- color: '#735f3d',
714
+ font: { family: t.body, size: 12, weight: '500' },
715
+ color: t.inkSoft,
711
716
  },
712
717
  ticks: {
713
- callback: (v) => `${v} dr.`,
714
- font: { family: '"EB Garamond", Georgia, serif', size: 12 },
715
- color: '#6e5b3a',
718
+ callback: (v) => `${v}`,
719
+ font: { family: t.body, size: 12 },
720
+ color: t.inkSoft,
716
721
  },
717
- grid: { color: 'rgba(139, 111, 44, 0.18)', drawBorder: false },
722
+ grid: { color: t.rule, drawBorder: false },
718
723
  },
719
724
  x: {
720
725
  title: {
721
726
  display: true,
722
727
  text: 'date',
723
- font: { family: '"Cinzel", Georgia, serif', size: 11, weight: '500' },
724
- color: '#735f3d',
728
+ font: { family: t.body, size: 12, weight: '500' },
729
+ color: t.inkSoft,
725
730
  },
726
731
  grid: { display: false },
727
732
  ticks: {
728
733
  maxTicksLimit: 10,
729
- font: { family: '"EB Garamond", Georgia, serif', size: 12 },
730
- color: '#6e5b3a',
734
+ font: { family: t.body, size: 12 },
735
+ color: t.inkSoft,
731
736
  },
732
737
  },
733
738
  },
@@ -735,6 +740,23 @@ async function loadCosts() {
735
740
  });
736
741
  }
737
742
 
743
+ // The resolved values of the tokens the cost chart paints with, read from the
744
+ // page (a canvas cannot take var()). An unset token reads '' and Chart.js keeps
745
+ // its own default for it.
746
+ function chartTokens() {
747
+ const cs = window.getComputedStyle(document.body);
748
+ const v = (name) => cs.getPropertyValue(name).trim();
749
+ return {
750
+ ink: v('--ink'),
751
+ inkSoft: v('--ink-soft'),
752
+ inkWash: v('--glass-strong'),
753
+ info: v('--info'),
754
+ rule: v('--rule-soft'),
755
+ card: v('--bg-card'),
756
+ body: v('--font-body') || 'sans-serif',
757
+ };
758
+ }
759
+
738
760
  function toDayKey(d) {
739
761
  // Postgres returns 'YYYY-MM-DD' as a string OR an ISO timestamp depending
740
762
  // on transport — normalise to YYYY-MM-DD so dedupe works across formats.
@@ -785,7 +807,7 @@ function renderShippedPage() {
785
807
  if (items.length === 0) {
786
808
  const li = document.createElement('li');
787
809
  li.className = 'empty';
788
- li.textContent = 'No works yet inscribed in this window.';
810
+ li.textContent = 'Nothing shipped in this window.';
789
811
  els.shipped.appendChild(li);
790
812
  // GDS #191: pad with invisible rows so an empty window still occupies
791
813
  // the same vertical space as a full page. Keeps the section height
@@ -827,7 +849,7 @@ function renderShippedPage() {
827
849
  const by = t.display_name || t.github_login;
828
850
  // task 1004432: an approved page is the artist's; the session that applied it is a helper.
829
851
  const helper = t.helper_display_name || t.helper_login;
830
- meta.textContent = `${by ? `inscribed by ${by} · ` : ''}${helper ? `with help from ${helper} · ` : ''}${fmtRelativeTime(t.shipped_at)}`;
852
+ meta.textContent = `${by ? `shipped by ${by} · ` : ''}${helper ? `with help from ${helper} · ` : ''}${fmtRelativeTime(t.shipped_at)}`;
831
853
  li.appendChild(title);
832
854
  if (thumb) li.appendChild(thumb);
833
855
  li.appendChild(meta);
@@ -1040,7 +1062,7 @@ async function loadLeaderboard() {
1040
1062
  function stampRefreshTime() {
1041
1063
  if (!els.lastUpdated) return;
1042
1064
  const t = new Date().toLocaleTimeString(undefined, { hour: 'numeric', minute: '2-digit' });
1043
- els.lastUpdated.textContent = `tablet last consulted at ${t}`;
1065
+ els.lastUpdated.textContent = `updated at ${t}`;
1044
1066
  }
1045
1067
 
1046
1068
  // ---------- the Foundry — ecosystem dev curve (#291, C5) ----------
@@ -1050,7 +1072,7 @@ function stampRefreshTime() {
1050
1072
  // kinds-of-work fingerprint. Fed by /api/bongos/public/analytics/ecosystem.
1051
1073
  // Inline SVG sparklines — no chart library for this tile.
1052
1074
 
1053
- function foundrySparkline(values, color) {
1075
+ function foundrySparkline(values, tone) {
1054
1076
  const W = 320, H = 54, pad = 4;
1055
1077
  const nums = (values || []).map((v) => (Number.isFinite(Number(v)) ? Number(v) : 0));
1056
1078
  if (nums.length === 0) return '';
@@ -1060,9 +1082,12 @@ function foundrySparkline(values, color) {
1060
1082
  const y = (v) => H - pad - ((v - min) / span) * (H - 2 * pad);
1061
1083
  const line = nums.map((v, i) => `${x(i).toFixed(1)},${y(v).toFixed(1)}`).join(' ');
1062
1084
  const area = `${pad},${H - pad} ${line} ${(W - pad)},${H - pad}`;
1063
- return `<svg class="foundry-spark" viewBox="0 0 ${W} ${H}" preserveAspectRatio="none" role="img" aria-label="trend">
1064
- <polyline class="foundry-spark__area" points="${area}" fill="${color}" />
1065
- <polyline class="foundry-spark__line" points="${line}" fill="none" stroke="${color}" />
1085
+ // The colour is the sheet's (.foundry-spark, and --ink for the ink tone): a
1086
+ // token, never a literal handed in from here (task 1005089).
1087
+ const cls = tone === 'ink' ? 'foundry-spark foundry-spark--ink' : 'foundry-spark';
1088
+ return `<svg class="${cls}" viewBox="0 0 ${W} ${H}" preserveAspectRatio="none" role="img" aria-label="trend">
1089
+ <polyline class="foundry-spark__area" points="${area}" />
1090
+ <polyline class="foundry-spark__line" points="${line}" />
1066
1091
  </svg>`;
1067
1092
  }
1068
1093
 
@@ -1095,8 +1120,8 @@ async function loadEcosystem() {
1095
1120
  const shipVals = (data.ship_rate || []).map((r) => Number(r.n));
1096
1121
  const durVals = (data.duration_trend || []).map((r) => Number(r.avg_actual));
1097
1122
  chartsEl.innerHTML =
1098
- `<div class="foundry__chart"><div class="foundry__chart-title">Works shipped / day</div>${foundrySparkline(shipVals, '#2f5a3f') || '<p class="empty">no data</p>'}</div>` +
1099
- `<div class="foundry__chart"><div class="foundry__chart-title">Avg time per work (min)</div>${foundrySparkline(durVals, '#2d6e7e') || '<p class="empty">no data</p>'}</div>`;
1123
+ `<div class="foundry__chart"><div class="foundry__chart-title">Tasks shipped per day</div>${foundrySparkline(shipVals, 'ink') || '<p class="empty">no data</p>'}</div>` +
1124
+ `<div class="foundry__chart"><div class="foundry__chart-title">Average time per task (min)</div>${foundrySparkline(durVals, 'info') || '<p class="empty">no data</p>'}</div>`;
1100
1125
 
1101
1126
  const kinds = (data.top_kinds || []).slice(0, 6);
1102
1127
  if (kindsEl) {
@@ -1192,13 +1217,13 @@ function renderStatusDeepCard(kind, id, state) {
1192
1217
  const label = `#${id}`;
1193
1218
  panel.appendChild(mk('p', 'deeplink__kind', kind.toUpperCase()));
1194
1219
  if (state.loading) {
1195
- panel.appendChild(mk('p', 'deeplink__title', `Consulting the archive for ${label}…`));
1220
+ panel.appendChild(mk('p', 'deeplink__title', `Looking up ${label}…`));
1196
1221
  } else if (state.notFound) {
1197
1222
  panel.appendChild(mk('p', 'deeplink__title', `${label} — no such ${kind}`));
1198
1223
  panel.appendChild(mk('p', 'deeplink__note', 'Records number tasks, ideas, and blockers separately from GitHub pull requests — this may be a PR, not a record here.'));
1199
1224
  } else if (state.error) {
1200
1225
  panel.appendChild(mk('p', 'deeplink__title', `Could not resolve ${label}`));
1201
- panel.appendChild(mk('p', 'deeplink__note', 'The archive could not be reached. Try again in a moment.'));
1226
+ panel.appendChild(mk('p', 'deeplink__note', 'The server could not be reached. Try again in a moment.'));
1202
1227
  } else {
1203
1228
  const r = state.ref || {};
1204
1229
  panel.appendChild(mk('p', 'deeplink__title', r.title ? `${label} — ${r.title}` : label));