@eshal-bot/chat-widget 0.1.54 → 0.1.56

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.
@@ -54312,9 +54312,17 @@
54312
54312
  }
54313
54313
  function numericData(data, xKey, yKey) {
54314
54314
  return (data || []).map(row => {
54315
- var _row$xKey;
54315
+ const raw = row === null || row === void 0 ? void 0 : row[xKey];
54316
54316
  return {
54317
- label: String((_row$xKey = row === null || row === void 0 ? void 0 : row[xKey]) !== null && _row$xKey !== void 0 ? _row$xKey : ''),
54317
+ // Pre-format the label so bar/line ticks read "Jan" instead
54318
+ // of "1" and "Office Supplies" instead of "office_supplies".
54319
+ // formatAxisTick handles the month-number → month-name mapping
54320
+ // when xKey matches /month/i AND value is 1..12; otherwise it
54321
+ // falls through to prettifyLabel. See Bug 1 + Bug 3 in the
54322
+ // 2026-06-16 chart-formatting fix.
54323
+ label: formatAxisTick(raw, xKey),
54324
+ // Raw value preserved — value formatting (currency etc.) is
54325
+ // applied at render-time by BarSvg/LineSvg using yKey.
54318
54326
  value: Number(row === null || row === void 0 ? void 0 : row[yKey])
54319
54327
  };
54320
54328
  }).filter(d => Number.isFinite(d.value));
@@ -54352,18 +54360,23 @@
54352
54360
  });
54353
54361
  }
54354
54362
  const t = String(type || '').trim().toLowerCase();
54363
+ // Pass yKey down so the SVG renderers can apply currency formatting
54364
+ // ($23,376.60) when the column name suggests money — see formatValueForKey.
54355
54365
  let body;
54356
54366
  if (t === 'pie' || t === 'donut') body = /*#__PURE__*/jsxRuntimeExports.jsx(PieSvg, {
54357
54367
  rows: rows,
54358
54368
  donut: t === 'donut',
54359
- isDark: isDark
54369
+ isDark: isDark,
54370
+ yKey: y
54360
54371
  });else if (t === 'line' || t === 'area') body = /*#__PURE__*/jsxRuntimeExports.jsx(LineSvg, {
54361
54372
  rows: rows,
54362
54373
  area: t === 'area',
54363
- isDark: isDark
54374
+ isDark: isDark,
54375
+ yKey: y
54364
54376
  });else body = /*#__PURE__*/jsxRuntimeExports.jsx(BarSvg, {
54365
54377
  rows: rows,
54366
- isDark: isDark
54378
+ isDark: isDark,
54379
+ yKey: y
54367
54380
  }); // bar + every fallback
54368
54381
 
54369
54382
  return /*#__PURE__*/jsxRuntimeExports.jsxs("div", {
@@ -54380,7 +54393,8 @@
54380
54393
  function BarSvg(_ref3) {
54381
54394
  let {
54382
54395
  rows,
54383
- isDark
54396
+ isDark,
54397
+ yKey
54384
54398
  } = _ref3;
54385
54399
  const w = 320;
54386
54400
  const h = 180;
@@ -54433,7 +54447,7 @@
54433
54447
  fontSize: "9",
54434
54448
  fill: labelStroke,
54435
54449
  textAnchor: "middle",
54436
- children: formatNum(r.value)
54450
+ children: formatValueForKey(r.value, yKey)
54437
54451
  })]
54438
54452
  }, i);
54439
54453
  })]
@@ -54443,7 +54457,8 @@
54443
54457
  let {
54444
54458
  rows,
54445
54459
  area,
54446
- isDark
54460
+ isDark,
54461
+ yKey
54447
54462
  } = _ref4;
54448
54463
  const w = 320;
54449
54464
  const h = 180;
@@ -54491,14 +54506,14 @@
54491
54506
  fontSize: "9",
54492
54507
  fill: labelStroke,
54493
54508
  textAnchor: "end",
54494
- children: formatNum(maxV)
54509
+ children: formatValueForKey(maxV, yKey)
54495
54510
  }), /*#__PURE__*/jsxRuntimeExports.jsx("text", {
54496
54511
  x: padLeft - 4,
54497
54512
  y: padTop + innerH,
54498
54513
  fontSize: "9",
54499
54514
  fill: labelStroke,
54500
54515
  textAnchor: "end",
54501
- children: formatNum(minV)
54516
+ children: formatValueForKey(minV, yKey)
54502
54517
  }), area && /*#__PURE__*/jsxRuntimeExports.jsx("path", {
54503
54518
  d: areaPath,
54504
54519
  fill: lineColor,
@@ -54531,7 +54546,8 @@
54531
54546
  let {
54532
54547
  rows,
54533
54548
  donut,
54534
- isDark
54549
+ isDark,
54550
+ yKey
54535
54551
  } = _ref5;
54536
54552
  const size = 200;
54537
54553
  const cx = size / 2;
@@ -54626,7 +54642,7 @@
54626
54642
  style: {
54627
54643
  opacity: 0.7
54628
54644
  },
54629
- children: formatNum(s.value)
54645
+ children: formatValueForKey(s.value, yKey)
54630
54646
  })]
54631
54647
  })]
54632
54648
  }, i))
@@ -54720,7 +54736,7 @@
54720
54736
  textAlign: 'left',
54721
54737
  fontWeight: 600
54722
54738
  },
54723
- children: c
54739
+ children: prettifyLabel(c)
54724
54740
  }, c))
54725
54741
  })
54726
54742
  }), /*#__PURE__*/jsxRuntimeExports.jsx("tbody", {
@@ -54730,7 +54746,7 @@
54730
54746
  border,
54731
54747
  padding: '6px 8px'
54732
54748
  },
54733
- children: row && typeof row === 'object' ? formatCell(row[c]) : ''
54749
+ children: row && typeof row === 'object' ? formatCell(row[c], c) : ''
54734
54750
  }, c))
54735
54751
  }, i))
54736
54752
  })]
@@ -54953,9 +54969,66 @@
54953
54969
  if (Number.isInteger(n)) return String(n);
54954
54970
  return n.toFixed(2);
54955
54971
  }
54956
- function formatCell(v) {
54972
+
54973
+ // ── Bug 1 + Bug 3 helpers (2026-06-16) ───────────────────────────────────
54974
+ // PORTED from packages/frontend/src/components/charts/eshal-chart/
54975
+ // chartShared.tsx. Duplicated rather than shared because the widget is
54976
+ // bundle-size-conscious (a separate package import here would pull a
54977
+ // chunk of the frontend with it). The authoritative tests live with
54978
+ // the frontend copy in __tests__/chartShared.test.ts — keep this code
54979
+ // in lockstep with that file when either is touched. Regex boundaries
54980
+ // use `(^|[_\W])` instead of `\b` because JS regex `\b` doesn't fire
54981
+ // next to `_` (underscore is a word char), so "total_expenses" with
54982
+ // `\bexpense\b` would miss the match.
54983
+
54984
+ const MONTH_NAMES = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
54985
+ const CURRENCY_KEY_RE = /(^|[_\W])(price|salary|cost|amount|revenue|expense|total|value|budget|spend|income|sale|balance)/i;
54986
+ const MONTH_KEY_RE = /(^|[_\W])month/i;
54987
+ const USD_FORMATTER = new Intl.NumberFormat('en-US', {
54988
+ style: 'currency',
54989
+ currency: 'USD',
54990
+ minimumFractionDigits: 2,
54991
+ maximumFractionDigits: 2
54992
+ });
54993
+ function prettifyLabel(value) {
54994
+ if (value === null || value === undefined) return '';
54995
+ const s = String(value);
54996
+ if (!s) return '';
54997
+ return s.replace(/[_\-]+/g, ' ').replace(/([a-z])([A-Z])/g, '$1 $2').replace(/\s+/g, ' ').trim().split(' ').map(w => w.charAt(0).toUpperCase() + w.slice(1).toLowerCase()).join(' ');
54998
+ }
54999
+ function isCurrencyKey(key) {
55000
+ return typeof key === 'string' && CURRENCY_KEY_RE.test(key);
55001
+ }
55002
+
55003
+ // Like formatNum, but adds $ + comma + 2 decimals when `key` suggests
55004
+ // currency. Used for bar/line value labels and table cells so the
55005
+ // widget matches the frontend chart's formatting on the same data.
55006
+ function formatValueForKey(value, key) {
55007
+ if (typeof value === 'number' && Number.isFinite(value)) {
55008
+ if (isCurrencyKey(key)) return USD_FORMATTER.format(value);
55009
+ return formatNum(value);
55010
+ }
55011
+ return prettifyLabel(value);
55012
+ }
55013
+
55014
+ // Axis-tick formatter: month-number columns (column matches /month/i,
55015
+ // value 1..12) render as 3-letter month names; everything else gets
55016
+ // prettifyLabel applied so "office_supplies" reads "Office Supplies".
55017
+ function formatAxisTick(value, dataKey) {
55018
+ if (typeof dataKey === 'string' && MONTH_KEY_RE.test(dataKey)) {
55019
+ const n = typeof value === 'number' ? value : Number(value);
55020
+ if (Number.isInteger(n) && n >= 1 && n <= 12) return MONTH_NAMES[n - 1];
55021
+ }
55022
+ return prettifyLabel(value);
55023
+ }
55024
+ function formatCell(v, columnKey) {
54957
55025
  if (v == null) return '';
54958
- if (typeof v === 'number') return Number.isInteger(v) ? String(v) : v.toFixed(2);
55026
+ if (typeof v === 'number') {
55027
+ // Currency-keyed numeric cells in tables also get $ + decimals so
55028
+ // the table next to a chart agrees on its number formatting.
55029
+ if (isCurrencyKey(columnKey)) return USD_FORMATTER.format(v);
55030
+ return Number.isInteger(v) ? String(v) : v.toFixed(2);
55031
+ }
54959
55032
  if (typeof v === 'object') return JSON.stringify(v);
54960
55033
  return String(v);
54961
55034
  }
@@ -58040,6 +58113,30 @@
58040
58113
  window.cancelAnimationFrame(frame);
58041
58114
  };
58042
58115
  }, [autoScrollKey, isLoading]);
58116
+
58117
+ // When voice connects, scroll so the listening indicator (rendered after the
58118
+ // topic tags and quick questions) is visible at the bottom. Neither
58119
+ // autoScrollKey nor isLoading changes on voice-connect, so the main scroll
58120
+ // effect never fires — without this the indicator is pushed below the viewport
58121
+ // while the topic tags remain in view above it.
58122
+ reactExports.useEffect(() => {
58123
+ if (!isVoiceSessionActive || voiceStatus !== "connected") {
58124
+ return undefined;
58125
+ }
58126
+ if (typeof window === "undefined") {
58127
+ return undefined;
58128
+ }
58129
+ const frame = window.requestAnimationFrame(() => {
58130
+ var _messagesEndRef$curre3;
58131
+ (_messagesEndRef$curre3 = messagesEndRef.current) === null || _messagesEndRef$curre3 === void 0 || _messagesEndRef$curre3.scrollIntoView({
58132
+ behavior: "smooth",
58133
+ block: "end"
58134
+ });
58135
+ });
58136
+ return () => {
58137
+ window.cancelAnimationFrame(frame);
58138
+ };
58139
+ }, [isVoiceSessionActive, voiceStatus]);
58043
58140
  const normalizedDisclaimerLinkUrl = reactExports.useMemo(() => normalizeExternalUrl(disclaimerLinkUrl), [disclaimerLinkUrl]);
58044
58141
  return /*#__PURE__*/jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, {
58045
58142
  children: [disclaimerEnabled && disclaimerText && disclaimerPosition === "top" && /*#__PURE__*/jsxRuntimeExports.jsx("div", {