@eshal-bot/chat-widget 0.2.23 → 0.2.26

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.
@@ -87777,29 +87777,65 @@
87777
87777
  return String(n);
87778
87778
  }
87779
87779
 
87780
- // ── Currency-aware value formatting ──────────────────────────────────────
87780
+ // ── Money formatting ───────────────────────────────────────────────────────
87781
87781
  // A2UI chart data is normalized to `{ label, value }` by A2uiBlock's
87782
- // numericData(), which drops the original Y-column key. A2uiBlock now threads
87783
- // that key through as `props.yKey` so these renderers can decide whether the
87784
- // axis / tooltip values are money. Heuristic mirrors the frontend's
87785
- // chartShared.tsx `isCurrencyKey` - keep the keyword list in lockstep.
87782
+ // numericData(), which drops the original Y-column key. A2uiBlock threads that
87783
+ // key through as `props.yKey`, and the spec's `currency` as `props.currency`.
87784
+ // A currency symbol comes only from the currency the chart states (an ISO code
87785
+ // such as "USD" or "INR", or a symbol such as "₹"): a column name says money,
87786
+ // never which money, and the old guess from it put "$" on rupee spend (dev,
87787
+ // Oct 2026). A money-named column without a stated currency still reads as
87788
+ // money (comma-grouped, up to 2 decimals). The keyword list mirrors the
87789
+ // frontend's chartShared.tsx `isCurrencyKey` - keep them in lockstep.
87786
87790
  // `(^|[_\W])` (not `\b`) so "average_salary" / "total_expenses" still match -
87787
87791
  // JS `\b` doesn't fire next to `_`.
87788
- const CURRENCY_KEY_RE$1 = /(^|[_\W])(price|salary|cost|amount|revenue|expense|total|value|budget|spend|income|sale|balance)/i;
87789
- function isCurrencyKey$1(key) {
87790
- return typeof key === 'string' && CURRENCY_KEY_RE$1.test(key);
87792
+ const MONEY_KEY_RE = /(^|[_\W])(price|salary|cost|amount|revenue|expense|total|value|budget|spend|income|sale|balance)/i;
87793
+ function groupedNumber(v) {
87794
+ return Number(v).toLocaleString('en-US', {
87795
+ maximumFractionDigits: 2
87796
+ });
87791
87797
  }
87792
87798
 
87793
- // Compact currency for axis ticks → "$120.0k". Full currency for tooltips →
87794
- // "$87,600" (comma-grouped, up to 2 decimals, no forced trailing zeros so a
87795
- // whole-dollar salary reads "$81,200" not "$81,200.00").
87796
- function formatCurrencyCompact(v) {
87797
- return `$${formatCompactNumber(v)}`;
87798
- }
87799
- function formatCurrencyFull(v) {
87800
- return `$${Number(v).toLocaleString('en-US', {
87801
- maximumFractionDigits: 2
87802
- })}`;
87799
+ // `{ full, compact }` formatters for a chart's figures, or null for plain
87800
+ // numbers. Full is for tooltips and slice labels ("₹87,600"), compact for axis
87801
+ // ticks and bar labels ("₹87.6K").
87802
+ function moneyFormatters(currency, yKey) {
87803
+ const stated = typeof currency === 'string' ? currency.trim() : '';
87804
+ if (/^[A-Za-z]{3}$/.test(stated)) {
87805
+ try {
87806
+ const code = stated.toUpperCase();
87807
+ const full = new Intl.NumberFormat('en-US', {
87808
+ style: 'currency',
87809
+ currency: code,
87810
+ maximumFractionDigits: 2
87811
+ });
87812
+ const compact = new Intl.NumberFormat('en-US', {
87813
+ style: 'currency',
87814
+ currency: code,
87815
+ notation: 'compact',
87816
+ maximumFractionDigits: 1
87817
+ });
87818
+ return {
87819
+ full: v => full.format(Number(v)),
87820
+ compact: v => compact.format(Number(v))
87821
+ };
87822
+ } catch (_) {
87823
+ // an unknown code is shown as written, below
87824
+ }
87825
+ }
87826
+ if (stated) {
87827
+ return {
87828
+ full: v => `${stated}${groupedNumber(v)}`,
87829
+ compact: v => `${stated}${formatCompactNumber(v)}`
87830
+ };
87831
+ }
87832
+ if (typeof yKey === 'string' && MONEY_KEY_RE.test(yKey)) {
87833
+ return {
87834
+ full: groupedNumber,
87835
+ compact: formatCompactNumber
87836
+ };
87837
+ }
87838
+ return null;
87803
87839
  }
87804
87840
 
87805
87841
  /**
@@ -87813,8 +87849,9 @@
87813
87849
  isDark = false
87814
87850
  }) {
87815
87851
  const data = Array.isArray(props.data) ? props.data : [];
87816
- // Whether the Y values are money - drives the `$` on axis ticks + tooltip.
87817
- const currency = isCurrencyKey$1(props.yKey);
87852
+ // How the Y values read as money on axis ticks + tooltip: the stated
87853
+ // currency's symbol, or grouped digits for a money-named column.
87854
+ const money = moneyFormatters(props.currency, props.yKey);
87818
87855
  // Human series name for the tooltip ("Average Salary"), so it no longer
87819
87856
  // reads the generic dataKey "value".
87820
87857
  const seriesName = props.valueLabel || undefined;
@@ -87901,11 +87938,12 @@
87901
87938
  },
87902
87939
  angle: xAngle,
87903
87940
  textAnchor: xAnchor,
87904
- interval: 0,
87905
- tickFormatter: v => {
87906
- const s = String(v ?? '');
87907
- return s.replace(/[_\-]+/g, ' ').replace(/\b\w/g, c => c.toUpperCase());
87908
- },
87941
+ interval: 0
87942
+ // Each label arrives formatted by A2uiBlock (formatAxisTick), so the
87943
+ // axis shows it as given, as the line and pie charts do. Capitalising
87944
+ // it again would split "CNBC-TV18" and turn "McDonald's" into
87945
+ // "McDonald'S".
87946
+ ,
87909
87947
  label: props.xAxisLabel ? {
87910
87948
  value: props.xAxisLabel,
87911
87949
  position: 'insideBottom',
@@ -87926,9 +87964,9 @@
87926
87964
  fontSize: 11,
87927
87965
  fill: labelColor
87928
87966
  } : undefined,
87929
- tickFormatter: v => currency ? formatCurrencyCompact(v) : props.unit ? `${formatCompactNumber(v)} ${props.unit}` : formatCompactNumber(v)
87967
+ tickFormatter: v => money ? money.compact(v) : props.unit ? `${formatCompactNumber(v)} ${props.unit}` : formatCompactNumber(v)
87930
87968
  }), /*#__PURE__*/jsxRuntimeExports.jsx(Tooltip, {
87931
- formatter: v => currency ? formatCurrencyFull(v) : props.unit ? `${Number(v).toLocaleString()} ${props.unit}` : Number(v).toLocaleString(),
87969
+ formatter: v => money ? money.full(v) : props.unit ? `${Number(v).toLocaleString()} ${props.unit}` : Number(v).toLocaleString(),
87932
87970
  contentStyle: {
87933
87971
  background: tooltipBg,
87934
87972
  border: `1px solid ${borderColor}`,
@@ -87945,7 +87983,7 @@
87945
87983
  position: 'top',
87946
87984
  fill: labelColor,
87947
87985
  fontSize: 10,
87948
- formatter: v => currency ? formatCurrencyCompact(v) : formatCompactNumber(v)
87986
+ formatter: v => money ? money.compact(v) : formatCompactNumber(v)
87949
87987
  }
87950
87988
  })]
87951
87989
  })
@@ -88049,7 +88087,7 @@
88049
88087
  donut = false
88050
88088
  }) {
88051
88089
  const data = Array.isArray(props.data) ? props.data : [];
88052
- const currency = isCurrencyKey$1(props.yKey);
88090
+ const money = moneyFormatters(props.currency, props.yKey);
88053
88091
  const borderColor = isDark ? '#374151' : '#e2e8f0';
88054
88092
  const bgColor = isDark ? '#1f2937' : '#ffffff';
88055
88093
  const titleColor = isDark ? '#f3f4f6' : '#0f172a';
@@ -88080,7 +88118,7 @@
88080
88118
  const a = -Number(entry.midAngle) * RADIAN;
88081
88119
  const cos = Math.cos(a);
88082
88120
  const sin = Math.sin(a);
88083
- const text = currency ? formatCurrencyFull(v) : Number(v).toLocaleString();
88121
+ const text = money ? money.full(v) : Number(v).toLocaleString();
88084
88122
  if (placement === 'inside') {
88085
88123
  const r = rIn + (rOut - rIn) * (donut ? 0.78 : 0.6);
88086
88124
  return /*#__PURE__*/jsxRuntimeExports.jsx("text", {
@@ -88181,7 +88219,7 @@
88181
88219
  fill: PIE_COLORS[i % PIE_COLORS.length]
88182
88220
  }, i))
88183
88221
  }), /*#__PURE__*/jsxRuntimeExports.jsx(Tooltip, {
88184
- formatter: v => currency ? formatCurrencyFull(v) : Number(v).toLocaleString(),
88222
+ formatter: v => money ? money.full(v) : Number(v).toLocaleString(),
88185
88223
  contentStyle: {
88186
88224
  background: tooltipBg,
88187
88225
  border: `1px solid ${borderColor}`,
@@ -88214,7 +88252,7 @@
88214
88252
  area = false
88215
88253
  }) {
88216
88254
  const data = Array.isArray(props.data) ? props.data : [];
88217
- const currency = isCurrencyKey$1(props.yKey);
88255
+ const money = moneyFormatters(props.currency, props.yKey);
88218
88256
  const seriesName = props.valueLabel || undefined;
88219
88257
  const borderColor = isDark ? '#374151' : '#e2e8f0';
88220
88258
  const bgColor = isDark ? '#1f2937' : '#ffffff';
@@ -88309,9 +88347,9 @@
88309
88347
  fontSize: 11,
88310
88348
  fill: labelColor
88311
88349
  },
88312
- tickFormatter: v => currency ? formatCurrencyCompact(v) : props.unit ? `${formatCompactNumber(v)} ${props.unit}` : formatCompactNumber(v)
88350
+ tickFormatter: v => money ? money.compact(v) : props.unit ? `${formatCompactNumber(v)} ${props.unit}` : formatCompactNumber(v)
88313
88351
  }), /*#__PURE__*/jsxRuntimeExports.jsx(Tooltip, {
88314
- formatter: v => currency ? formatCurrencyFull(v) : props.unit ? `${Number(v).toLocaleString()} ${props.unit}` : Number(v).toLocaleString(),
88352
+ formatter: v => money ? money.full(v) : props.unit ? `${Number(v).toLocaleString()} ${props.unit}` : Number(v).toLocaleString(),
88315
88353
  contentStyle: {
88316
88354
  background: tooltipBg,
88317
88355
  border: `1px solid ${borderColor}`,
@@ -88721,12 +88759,14 @@
88721
88759
  xAxisLabel: spec.xAxisLabel,
88722
88760
  yAxisLabel: spec.yAxisLabel,
88723
88761
  // numericData() collapses the rows to { label, value }, dropping the
88724
- // original Y-column key. Pass it through so the renderer can detect a
88725
- // currency column (→ "$" on the Y-axis + tooltip) and label the tooltip
88726
- // series with the human column name ("Average Salary") instead of the
88727
- // generic dataKey "value".
88762
+ // original Y-column key. Pass it through so the renderer can read a
88763
+ // money column as money and label the tooltip series with the human
88764
+ // column name ("Average Salary") instead of the generic dataKey "value".
88728
88765
  yKey: y,
88729
- valueLabel: prettifyLabel(y)
88766
+ valueLabel: prettifyLabel(y),
88767
+ // The currency the chart states ("USD", "INR", "₹"): the only source of
88768
+ // a currency symbol (widgetCatalog moneyFormatters).
88769
+ currency: spec.currency
88730
88770
  };
88731
88771
  if (t === 'pie') return /*#__PURE__*/jsxRuntimeExports.jsx(PieChartRenderer, {
88732
88772
  props: chartProps,
@@ -90344,11 +90384,28 @@
90344
90384
  const MONTH_NAMES = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
90345
90385
  const CURRENCY_KEY_RE = /(^|[_\W])(price|salary|cost|amount|revenue|expense|total|value|budget|spend|income|sale|balance)/i;
90346
90386
  const MONTH_KEY_RE = /(^|[_\W])month/i;
90387
+
90388
+ // Only a column-style value is reworded: one with an underscore
90389
+ // (office_supplies), one without capitals (kebab-case, all-lower words)
90390
+ // or a camelCase key. A value written with capitals is a name and stays as
90391
+ // written ("DD Sports", "Republic TV", "GEC"), as does a word written with
90392
+ // capitals inside a key (GRP_pre -> "GRP Pre"). Lower-casing every word but
90393
+ // its first letter read those channels as "Dd Sports", "Republic Tv" and
90394
+ // "Gec" (Media Prowess, Oct 2026).
90347
90395
  function prettifyLabel(value) {
90348
90396
  if (value === null || value === undefined) return '';
90349
- const s = String(value);
90397
+ const s = String(value).replace(/\s+/g, ' ').trim();
90350
90398
  if (!s) return '';
90351
- 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(' ');
90399
+ if (!isColumnStyle(s)) return s;
90400
+ return s.replace(/[_\-]+/g, ' ').replace(/([a-z])([A-Z])/g, '$1 $2').replace(/\s+/g, ' ').trim().split(' ').map(w => w === w.toLowerCase() ? w.charAt(0).toUpperCase() + w.slice(1) : w).join(' ');
90401
+ }
90402
+
90403
+ // A camelCase key ("camelCaseKey", "totalSpend"): two lower-case letters
90404
+ // before the first capital, so names such as "iPhone", "eBay" and "kWh"
90405
+ // stay as written.
90406
+ const CAMEL_CASE_KEY_RE = /^[a-z]{2}[a-z0-9]*[A-Z][A-Za-z0-9]*$/;
90407
+ function isColumnStyle(s) {
90408
+ return s.includes('_') || s === s.toLowerCase() || CAMEL_CASE_KEY_RE.test(s);
90352
90409
  }
90353
90410
  function isCurrencyKey(key) {
90354
90411
  return typeof key === 'string' && CURRENCY_KEY_RE.test(key);