@sensiblestats/widget-react 0.11.0 → 0.12.0

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.
package/dist/index.js CHANGED
@@ -213,6 +213,9 @@ function toInsightVM(insight) {
213
213
  const fixture = home && away ? `${home} vs ${away}` : void 0;
214
214
  const competition = str(insight.competitionName);
215
215
  const scopes = Array.isArray(insight.scopes) ? insight.scopes.map((s) => ({ key: str(s.key), label: str(s.label), stats: readStatCells(s.stats) })).filter((s) => !!s.key && !!s.label) : void 0;
216
+ const previousValue = insight.odds?.previousValue;
217
+ const previous = typeof previousValue === "number" && Number.isFinite(previousValue) ? previousValue : void 0;
218
+ const direction = insight.odds?.movementDirection === "shortened" || insight.odds?.movementDirection === "drifted" ? insight.odds.movementDirection : void 0;
216
219
  return {
217
220
  id: str(insight.insightId) ?? `${str(insight.ruleId) ?? "insight"}:${num(insight.matchId) ?? ""}`,
218
221
  market,
@@ -232,7 +235,9 @@ function toInsightVM(insight) {
232
235
  matchOddsId,
233
236
  addToSlipEnabled: insight.addToSlipEnabled === true,
234
237
  oddsDecimal: typeof oddsValue === "number" && Number.isFinite(oddsValue) ? oddsValue : void 0,
235
- quotedAtUtc: str(insight.odds?.quotedAtUtc)
238
+ quotedAtUtc: str(insight.odds?.quotedAtUtc),
239
+ previousOdds: previous !== void 0 && direction !== void 0 ? previous.toFixed(2) : void 0,
240
+ movementDirection: previous !== void 0 && direction !== void 0 ? direction : void 0
236
241
  };
237
242
  }
238
243
  function toSlipSelection(vm, conversationId) {
@@ -721,20 +726,41 @@ function AddToSlipButton({ it, slip, ui }) {
721
726
  return /* @__PURE__ */ jsx8(SlipButton, { selection, shownOdds: it.odds ?? "", marketName: it.market, slip, ui });
722
727
  }
723
728
 
724
- // src/components/BettingInsightList.tsx
729
+ // src/components/OddsMovementBadge.tsx
725
730
  import { jsx as jsx9, jsxs as jsxs6 } from "react/jsx-runtime";
731
+ function OddsMovementBadge({
732
+ previous,
733
+ current,
734
+ direction,
735
+ ui
736
+ }) {
737
+ if (previous === void 0 || direction === void 0) return null;
738
+ const directionWord = direction === "shortened" ? ui?.oddsShortened ?? "shortened" : ui?.oddsDrifted ?? "drifted";
739
+ const label = ui?.oddsMoved?.(previous, current, directionWord) ?? `was ${previous}, now ${current}, ${directionWord}`;
740
+ return (
741
+ // Strikethrough is invisible to screen readers, so the whole was/now/direction sentence rides
742
+ // on one aria-label and the visual glyphs are hidden from the accessibility tree.
743
+ /* @__PURE__ */ jsxs6("span", { className: "ss-w-move", role: "img", "aria-label": label, children: [
744
+ /* @__PURE__ */ jsx9("span", { className: "ss-w-move-prev", "aria-hidden": "true", children: previous }),
745
+ /* @__PURE__ */ jsx9("span", { className: "ss-w-move-arrow", "aria-hidden": "true", children: direction === "shortened" ? "\u25BC" : "\u25B2" })
746
+ ] })
747
+ );
748
+ }
749
+
750
+ // src/components/BettingInsightList.tsx
751
+ import { jsx as jsx10, jsxs as jsxs7 } from "react/jsx-runtime";
726
752
  function StatRows2({ stats }) {
727
- return /* @__PURE__ */ jsx9("div", { className: "ss-w-bi-rows", children: stats.map((s) => /* @__PURE__ */ jsxs6("div", { className: "ss-w-bi-row", children: [
728
- /* @__PURE__ */ jsx9("span", { className: "ss-w-bi-row-lbl", children: s.label }),
729
- /* @__PURE__ */ jsx9("span", { className: "ss-w-bi-row-num", children: s.value })
753
+ return /* @__PURE__ */ jsx10("div", { className: "ss-w-bi-rows", children: stats.map((s) => /* @__PURE__ */ jsxs7("div", { className: "ss-w-bi-row", children: [
754
+ /* @__PURE__ */ jsx10("span", { className: "ss-w-bi-row-lbl", children: s.label }),
755
+ /* @__PURE__ */ jsx10("span", { className: "ss-w-bi-row-num", children: s.value })
730
756
  ] }, `${s.label}:${s.value}`)) });
731
757
  }
732
758
  function ScopedStats({ scopes }) {
733
759
  const [active, setActive] = useState3(0);
734
760
  const current = scopes[active] ?? scopes[0] ?? { key: "", label: "", stats: [] };
735
- return /* @__PURE__ */ jsxs6("div", { className: "ss-w-bi-scoped", children: [
736
- /* @__PURE__ */ jsx9("div", { className: "ss-w-bi-scopes", role: "group", children: scopes.map((s, i) => /* @__PURE__ */ jsx9("button", { type: "button", className: "ss-w-bi-chip", "aria-pressed": i === active, onClick: () => setActive(i), children: s.label }, s.key)) }),
737
- /* @__PURE__ */ jsx9(StatRows2, { stats: current.stats }, current.key)
761
+ return /* @__PURE__ */ jsxs7("div", { className: "ss-w-bi-scoped", children: [
762
+ /* @__PURE__ */ jsx10("div", { className: "ss-w-bi-scopes", role: "group", children: scopes.map((s, i) => /* @__PURE__ */ jsx10("button", { type: "button", className: "ss-w-bi-chip", "aria-pressed": i === active, onClick: () => setActive(i), children: s.label }, s.key)) }),
763
+ /* @__PURE__ */ jsx10(StatRows2, { stats: current.stats }, current.key)
738
764
  ] });
739
765
  }
740
766
  function StatsDisclosure({ it, ui }) {
@@ -742,8 +768,8 @@ function StatsDisclosure({ it, ui }) {
742
768
  const hasScopes = !!it.scopes && it.scopes.length >= 2;
743
769
  const hasStats = hasScopes || it.stats.length > 0;
744
770
  if (!hasStats) return null;
745
- return /* @__PURE__ */ jsxs6("div", { className: "ss-w-bi-stats", children: [
746
- /* @__PURE__ */ jsxs6(
771
+ return /* @__PURE__ */ jsxs7("div", { className: "ss-w-bi-stats", children: [
772
+ /* @__PURE__ */ jsxs7(
747
773
  "button",
748
774
  {
749
775
  type: "button",
@@ -751,131 +777,123 @@ function StatsDisclosure({ it, ui }) {
751
777
  "aria-expanded": open,
752
778
  onClick: () => setOpen((v) => !v),
753
779
  children: [
754
- /* @__PURE__ */ jsx9("span", { children: open ? ui?.hideStats ?? "Hide stats" : ui?.showStats ?? "Show stats" }),
755
- /* @__PURE__ */ jsx9("span", { className: `ss-w-bi-chev${open ? " ss-w-bi-chev-up" : ""}`, "aria-hidden": "true" })
780
+ /* @__PURE__ */ jsx10("span", { children: open ? ui?.hideStats ?? "Hide stats" : ui?.showStats ?? "Show stats" }),
781
+ /* @__PURE__ */ jsx10("span", { className: `ss-w-bi-chev${open ? " ss-w-bi-chev-up" : ""}`, "aria-hidden": "true" })
756
782
  ]
757
783
  }
758
784
  ),
759
- open ? hasScopes ? /* @__PURE__ */ jsx9(ScopedStats, { scopes: it.scopes }) : /* @__PURE__ */ jsx9(StatRows2, { stats: it.stats }) : null
785
+ open ? hasScopes ? /* @__PURE__ */ jsx10(ScopedStats, { scopes: it.scopes }) : /* @__PURE__ */ jsx10(StatRows2, { stats: it.stats }) : null
760
786
  ] });
761
787
  }
762
788
  function InsightCard({ it, ui, slip }) {
763
789
  const live = it.isLive || it.phase === "live";
764
- return /* @__PURE__ */ jsxs6("div", { className: "ss-w-binsight", children: [
765
- live ? /* @__PURE__ */ jsx9("div", { className: "ss-w-bi-status", children: /* @__PURE__ */ jsxs6("span", { className: "ss-w-bi-live", children: [
766
- /* @__PURE__ */ jsx9("span", { className: "ss-w-bi-live-dot", "aria-hidden": "true" }),
790
+ return /* @__PURE__ */ jsxs7("div", { className: "ss-w-binsight", children: [
791
+ live ? /* @__PURE__ */ jsx10("div", { className: "ss-w-bi-status", children: /* @__PURE__ */ jsxs7("span", { className: "ss-w-bi-live", children: [
792
+ /* @__PURE__ */ jsx10("span", { className: "ss-w-bi-live-dot", "aria-hidden": "true" }),
767
793
  ui?.liveLabel ?? "Live"
768
794
  ] }) }) : null,
769
- /* @__PURE__ */ jsxs6("div", { className: "ss-w-bi-head", children: [
770
- /* @__PURE__ */ jsxs6("span", { className: "ss-w-bi-mkt", children: [
771
- /* @__PURE__ */ jsx9("span", { className: "ss-w-bi-market", children: it.market }),
772
- it.selection ? /* @__PURE__ */ jsx9("span", { className: "ss-w-bi-sel", children: it.selection }) : null,
773
- it.fixture || it.competition ? /* @__PURE__ */ jsxs6("span", { className: "ss-w-bi-match", children: [
774
- it.fixture ? /* @__PURE__ */ jsx9("span", { className: "ss-w-bi-fixture", children: it.fixture }) : null,
775
- it.competition ? /* @__PURE__ */ jsx9("span", { className: "ss-w-bi-comp", children: it.competition }) : null
795
+ /* @__PURE__ */ jsxs7("div", { className: "ss-w-bi-head", children: [
796
+ /* @__PURE__ */ jsxs7("span", { className: "ss-w-bi-mkt", children: [
797
+ /* @__PURE__ */ jsx10("span", { className: "ss-w-bi-market", children: it.market }),
798
+ it.selection ? /* @__PURE__ */ jsx10("span", { className: "ss-w-bi-sel", children: it.selection }) : null,
799
+ it.fixture || it.competition ? /* @__PURE__ */ jsxs7("span", { className: "ss-w-bi-match", children: [
800
+ it.fixture ? /* @__PURE__ */ jsx10("span", { className: "ss-w-bi-fixture", children: it.fixture }) : null,
801
+ it.competition ? /* @__PURE__ */ jsx10("span", { className: "ss-w-bi-comp", children: it.competition }) : null
776
802
  ] }) : null
777
803
  ] }),
778
- /* @__PURE__ */ jsxs6("div", { className: "ss-w-bi-action", children: [
779
- it.odds ? /* @__PURE__ */ jsxs6("span", { className: "ss-w-bi-odds", children: [
780
- /* @__PURE__ */ jsx9("span", { className: "ss-w-bi-odds-cap", children: ui?.oddsLabel ?? "Odds" }),
781
- /* @__PURE__ */ jsx9("b", { children: it.odds }),
782
- it.bookmaker ? /* @__PURE__ */ jsx9("span", { className: "ss-w-bi-book", children: it.bookmaker }) : null
804
+ /* @__PURE__ */ jsxs7("div", { className: "ss-w-bi-action", children: [
805
+ it.odds ? /* @__PURE__ */ jsxs7("span", { className: "ss-w-bi-odds", children: [
806
+ /* @__PURE__ */ jsx10("span", { className: "ss-w-bi-odds-cap", children: ui?.oddsLabel ?? "Odds" }),
807
+ /* @__PURE__ */ jsx10("b", { children: it.odds }),
808
+ /* @__PURE__ */ jsx10(OddsMovementBadge, { previous: it.previousOdds, current: it.odds, direction: it.movementDirection, ui }),
809
+ it.bookmaker ? /* @__PURE__ */ jsx10("span", { className: "ss-w-bi-book", children: it.bookmaker }) : null
783
810
  ] }) : null,
784
- /* @__PURE__ */ jsx9(AddToSlipButton, { it, slip, ui })
811
+ /* @__PURE__ */ jsx10(AddToSlipButton, { it, slip, ui })
785
812
  ] })
786
813
  ] }),
787
- it.text ? /* @__PURE__ */ jsx9("p", { className: "ss-w-bi-text", children: it.text }) : null,
788
- /* @__PURE__ */ jsx9(StatsDisclosure, { it, ui }),
789
- it.disclaimer ? /* @__PURE__ */ jsx9("p", { className: "ss-w-bi-disc", children: it.disclaimer }) : null
814
+ it.text ? /* @__PURE__ */ jsx10("p", { className: "ss-w-bi-text", children: it.text }) : null,
815
+ /* @__PURE__ */ jsx10(StatsDisclosure, { it, ui }),
816
+ it.disclaimer ? /* @__PURE__ */ jsx10("p", { className: "ss-w-bi-disc", children: it.disclaimer }) : null
790
817
  ] });
791
818
  }
792
819
  function BettingInsightList({ insights, ui, slip }) {
793
820
  if (!insights || insights.length === 0) return null;
794
- return /* @__PURE__ */ jsx9("div", { className: "ss-w-binsights", children: insights.map((it) => /* @__PURE__ */ jsx9(InsightCard, { it, ui, slip }, it.id)) });
821
+ return /* @__PURE__ */ jsx10("div", { className: "ss-w-binsights", children: insights.map((it) => /* @__PURE__ */ jsx10(InsightCard, { it, ui, slip }, it.id)) });
795
822
  }
796
823
 
797
824
  // src/components/MarketOddsCardList.tsx
798
825
  import { useState as useState4 } from "react";
799
- import { jsx as jsx10, jsxs as jsxs7 } from "react/jsx-runtime";
826
+ import { jsx as jsx11, jsxs as jsxs8 } from "react/jsx-runtime";
800
827
  function OddsRow({ vm, row, ui, slip }) {
801
828
  const selection = vm.addToSlipEnabled ? marketOddsRowToSlipSelection(vm, row, slip.conversationId) : null;
802
- const directionWord = row.movementDirection === "shortened" ? ui?.oddsShortened ?? "shortened" : ui?.oddsDrifted ?? "drifted";
803
- const movementLabel = row.previousOdds !== void 0 ? ui?.oddsMoved?.(row.previousOdds, row.odds, directionWord) ?? `was ${row.previousOdds}, now ${row.odds}, ${directionWord}` : void 0;
804
- return /* @__PURE__ */ jsxs7("div", { className: "ss-w-mo-row", children: [
805
- /* @__PURE__ */ jsx10("span", { className: "ss-w-mo-sel", children: row.selection }),
806
- row.previousOdds !== void 0 ? (
807
- // Strikethrough is invisible to screen readers, so the whole was/now/direction sentence
808
- // rides on one aria-label and the visual glyphs are hidden from the accessibility tree.
809
- /* @__PURE__ */ jsxs7("span", { className: "ss-w-mo-move", role: "img", "aria-label": movementLabel, children: [
810
- /* @__PURE__ */ jsx10("span", { className: "ss-w-mo-prev", "aria-hidden": "true", children: row.previousOdds }),
811
- /* @__PURE__ */ jsx10("span", { className: "ss-w-mo-arrow", "aria-hidden": "true", children: row.movementDirection === "shortened" ? "\u25BC" : "\u25B2" })
812
- ] })
813
- ) : null,
814
- /* @__PURE__ */ jsx10("span", { className: "ss-w-mo-price", children: row.odds }),
815
- /* @__PURE__ */ jsx10(SlipButton, { selection, shownOdds: row.odds, marketName: vm.market, slip, ui })
829
+ return /* @__PURE__ */ jsxs8("div", { className: "ss-w-mo-row", children: [
830
+ /* @__PURE__ */ jsx11("span", { className: "ss-w-mo-sel", children: row.selection }),
831
+ /* @__PURE__ */ jsx11(OddsMovementBadge, { previous: row.previousOdds, current: row.odds, direction: row.movementDirection, ui }),
832
+ /* @__PURE__ */ jsx11("span", { className: "ss-w-mo-price", children: row.odds }),
833
+ /* @__PURE__ */ jsx11(SlipButton, { selection, shownOdds: row.odds, marketName: vm.market, slip, ui })
816
834
  ] });
817
835
  }
818
836
  function MarketOddsCard({ vm, ui, slip }) {
819
837
  const [open, setOpen] = useState4(false);
820
- return /* @__PURE__ */ jsxs7("div", { className: "ss-w-mocard", children: [
821
- /* @__PURE__ */ jsxs7("div", { className: "ss-w-mo-head", children: [
822
- /* @__PURE__ */ jsx10("span", { className: "ss-w-mo-market", children: vm.market }),
823
- /* @__PURE__ */ jsx10("span", { className: "ss-w-mo-tag", children: ui?.marketPrices ?? "Market prices" })
838
+ return /* @__PURE__ */ jsxs8("div", { className: "ss-w-mocard", children: [
839
+ /* @__PURE__ */ jsxs8("div", { className: "ss-w-mo-head", children: [
840
+ /* @__PURE__ */ jsx11("span", { className: "ss-w-mo-market", children: vm.market }),
841
+ /* @__PURE__ */ jsx11("span", { className: "ss-w-mo-tag", children: ui?.marketPrices ?? "Market prices" })
824
842
  ] }),
825
- vm.fixture || vm.competition ? /* @__PURE__ */ jsxs7("div", { className: "ss-w-mo-fixture", children: [
826
- vm.fixture ? /* @__PURE__ */ jsx10("span", { children: vm.fixture }) : null,
827
- vm.competition ? /* @__PURE__ */ jsx10("span", { className: "ss-w-mo-comp", children: vm.competition }) : null
843
+ vm.fixture || vm.competition ? /* @__PURE__ */ jsxs8("div", { className: "ss-w-mo-fixture", children: [
844
+ vm.fixture ? /* @__PURE__ */ jsx11("span", { children: vm.fixture }) : null,
845
+ vm.competition ? /* @__PURE__ */ jsx11("span", { className: "ss-w-mo-comp", children: vm.competition }) : null
828
846
  ] }) : null,
829
- /* @__PURE__ */ jsxs7("div", { className: "ss-w-mo-rows", children: [
830
- vm.mainRows.map((row) => /* @__PURE__ */ jsx10(OddsRow, { vm, row, ui, slip }, row.key)),
831
- open ? vm.moreRows.map((row) => /* @__PURE__ */ jsx10(OddsRow, { vm, row, ui, slip }, row.key)) : null
847
+ /* @__PURE__ */ jsxs8("div", { className: "ss-w-mo-rows", children: [
848
+ vm.mainRows.map((row) => /* @__PURE__ */ jsx11(OddsRow, { vm, row, ui, slip }, row.key)),
849
+ open ? vm.moreRows.map((row) => /* @__PURE__ */ jsx11(OddsRow, { vm, row, ui, slip }, row.key)) : null
832
850
  ] }),
833
- vm.moreRows.length > 0 ? /* @__PURE__ */ jsxs7("button", { type: "button", className: "ss-w-bi-toggle", "aria-expanded": open, onClick: () => setOpen((v) => !v), children: [
834
- /* @__PURE__ */ jsx10("span", { children: open ? ui?.hideLines ?? "Hide lines" : `${ui?.moreLines ?? "More lines"} (${vm.moreRows.length})` }),
835
- /* @__PURE__ */ jsx10("span", { className: `ss-w-bi-chev${open ? " ss-w-bi-chev-up" : ""}`, "aria-hidden": "true" })
851
+ vm.moreRows.length > 0 ? /* @__PURE__ */ jsxs8("button", { type: "button", className: "ss-w-bi-toggle", "aria-expanded": open, onClick: () => setOpen((v) => !v), children: [
852
+ /* @__PURE__ */ jsx11("span", { children: open ? ui?.hideLines ?? "Hide lines" : `${ui?.moreLines ?? "More lines"} (${vm.moreRows.length})` }),
853
+ /* @__PURE__ */ jsx11("span", { className: `ss-w-bi-chev${open ? " ss-w-bi-chev-up" : ""}`, "aria-hidden": "true" })
836
854
  ] }) : null,
837
- vm.bookmaker ? /* @__PURE__ */ jsx10("div", { className: "ss-w-mo-foot", children: /* @__PURE__ */ jsx10("span", { children: vm.bookmaker }) }) : null,
838
- vm.disclaimer ? /* @__PURE__ */ jsx10("p", { className: "ss-w-bi-disc", children: vm.disclaimer }) : null
855
+ vm.bookmaker ? /* @__PURE__ */ jsx11("div", { className: "ss-w-mo-foot", children: /* @__PURE__ */ jsx11("span", { children: vm.bookmaker }) }) : null,
856
+ vm.disclaimer ? /* @__PURE__ */ jsx11("p", { className: "ss-w-bi-disc", children: vm.disclaimer }) : null
839
857
  ] });
840
858
  }
841
859
  function MarketOddsCardList({ cards, ui, slip }) {
842
860
  if (!cards || cards.length === 0) return null;
843
- return /* @__PURE__ */ jsx10("div", { className: "ss-w-mocards", children: cards.map((vm) => /* @__PURE__ */ jsx10(MarketOddsCard, { vm, ui, slip }, vm.id)) });
861
+ return /* @__PURE__ */ jsx11("div", { className: "ss-w-mocards", children: cards.map((vm) => /* @__PURE__ */ jsx11(MarketOddsCard, { vm, ui, slip }, vm.id)) });
844
862
  }
845
863
 
846
864
  // src/components/ActionButtons.tsx
847
- import { Fragment, jsx as jsx11, jsxs as jsxs8 } from "react/jsx-runtime";
865
+ import { Fragment, jsx as jsx12, jsxs as jsxs9 } from "react/jsx-runtime";
848
866
  function ActionButtons({ actions, loading, disabled, onAct }) {
849
867
  if (actions.length === 0 && !loading) return null;
850
- return /* @__PURE__ */ jsxs8("div", { className: "ss-w-actions", children: [
851
- actions.map((a, i) => /* @__PURE__ */ jsx11("button", { type: "button", className: "ss-w-abtn", disabled, onClick: () => onAct(a), children: a.label }, `${a.label}-${i}`)),
852
- actions.length === 0 && loading ? /* @__PURE__ */ jsxs8(Fragment, { children: [
853
- /* @__PURE__ */ jsx11("span", { className: "ss-w-abtn-shimmer" }),
854
- /* @__PURE__ */ jsx11("span", { className: "ss-w-abtn-shimmer" })
868
+ return /* @__PURE__ */ jsxs9("div", { className: "ss-w-actions", children: [
869
+ actions.map((a, i) => /* @__PURE__ */ jsx12("button", { type: "button", className: "ss-w-abtn", disabled, onClick: () => onAct(a), children: a.label }, `${a.label}-${i}`)),
870
+ actions.length === 0 && loading ? /* @__PURE__ */ jsxs9(Fragment, { children: [
871
+ /* @__PURE__ */ jsx12("span", { className: "ss-w-abtn-shimmer" }),
872
+ /* @__PURE__ */ jsx12("span", { className: "ss-w-abtn-shimmer" })
855
873
  ] }) : null
856
874
  ] });
857
875
  }
858
876
 
859
877
  // src/components/ChatMessage.tsx
860
- import { jsx as jsx12, jsxs as jsxs9 } from "react/jsx-runtime";
878
+ import { jsx as jsx13, jsxs as jsxs10 } from "react/jsx-runtime";
861
879
  function ChatMessage({ message, disabled, onAct, ui, slip }) {
862
880
  if (message.role === "user") {
863
- return /* @__PURE__ */ jsx12("div", { className: "ss-w-msg ss-w-user", children: /* @__PURE__ */ jsx12("div", { className: "ss-w-bubble", children: message.text }) });
881
+ return /* @__PURE__ */ jsx13("div", { className: "ss-w-msg ss-w-user", children: /* @__PURE__ */ jsx13("div", { className: "ss-w-bubble", children: message.text }) });
864
882
  }
865
- return /* @__PURE__ */ jsxs9("div", { className: "ss-w-msg ss-w-bot", children: [
866
- message.text ? /* @__PURE__ */ jsx12(Markdown, { text: message.text }) : null,
867
- /* @__PURE__ */ jsx12(EntityCardList, { cards: message.cards, disabled, showLessLabel: ui?.showLess, showMoreLabel: ui?.showMore }),
868
- /* @__PURE__ */ jsx12(BettingInsightList, { insights: message.insights, ui, slip }),
869
- /* @__PURE__ */ jsx12(MarketOddsCardList, { cards: message.marketOdds, ui, slip }),
870
- /* @__PURE__ */ jsx12(ActionButtons, { actions: message.actions, loading: false, disabled, onAct })
883
+ return /* @__PURE__ */ jsxs10("div", { className: "ss-w-msg ss-w-bot", children: [
884
+ message.text ? /* @__PURE__ */ jsx13(Markdown, { text: message.text }) : null,
885
+ /* @__PURE__ */ jsx13(EntityCardList, { cards: message.cards, disabled, showLessLabel: ui?.showLess, showMoreLabel: ui?.showMore }),
886
+ /* @__PURE__ */ jsx13(BettingInsightList, { insights: message.insights, ui, slip }),
887
+ /* @__PURE__ */ jsx13(MarketOddsCardList, { cards: message.marketOdds, ui, slip }),
888
+ /* @__PURE__ */ jsx13(ActionButtons, { actions: message.actions, loading: false, disabled, onAct })
871
889
  ] });
872
890
  }
873
891
 
874
892
  // src/components/IntentChips.tsx
875
- import { jsx as jsx13 } from "react/jsx-runtime";
893
+ import { jsx as jsx14 } from "react/jsx-runtime";
876
894
  function IntentChips({ chips, onPick }) {
877
895
  if (chips.length === 0) return null;
878
- return /* @__PURE__ */ jsx13("div", { className: "ss-w-chips", children: chips.map((c, i) => /* @__PURE__ */ jsx13("button", { type: "button", className: "ss-w-chip", onClick: () => onPick(c.text), children: c.text }, `${c.text}-${i}`)) });
896
+ return /* @__PURE__ */ jsx14("div", { className: "ss-w-chips", children: chips.map((c, i) => /* @__PURE__ */ jsx14("button", { type: "button", className: "ss-w-chip", onClick: () => onPick(c.text), children: c.text }, `${c.text}-${i}`)) });
879
897
  }
880
898
 
881
899
  // src/scroll.ts
@@ -885,7 +903,7 @@ function isPinnedToBottom(el, threshold = PIN_THRESHOLD) {
885
903
  }
886
904
 
887
905
  // src/components/MessageList.tsx
888
- import { jsx as jsx14, jsxs as jsxs10 } from "react/jsx-runtime";
906
+ import { jsx as jsx15, jsxs as jsxs11 } from "react/jsx-runtime";
889
907
  function MessageList({ controller, ui, slip }) {
890
908
  const { state, send } = controller;
891
909
  const listRef = useRef2(null);
@@ -895,7 +913,7 @@ function MessageList({ controller, ui, slip }) {
895
913
  if (pinnedRef.current) endRef.current?.scrollIntoView({ block: "end" });
896
914
  }, [state.messages, state.status]);
897
915
  const empty = state.messages.length === 0;
898
- return /* @__PURE__ */ jsxs10(
916
+ return /* @__PURE__ */ jsxs11(
899
917
  "div",
900
918
  {
901
919
  className: "ss-w-list",
@@ -905,7 +923,7 @@ function MessageList({ controller, ui, slip }) {
905
923
  if (listRef.current) pinnedRef.current = isPinnedToBottom(listRef.current);
906
924
  },
907
925
  children: [
908
- state.messages.map((m) => /* @__PURE__ */ jsx14(
926
+ state.messages.map((m) => /* @__PURE__ */ jsx15(
909
927
  ChatMessage,
910
928
  {
911
929
  message: m,
@@ -916,15 +934,15 @@ function MessageList({ controller, ui, slip }) {
916
934
  },
917
935
  m.id
918
936
  )),
919
- state.isStreaming && state.actionsLoading ? /* @__PURE__ */ jsx14(ActionButtons, { actions: [], loading: true, onAct: () => {
937
+ state.isStreaming && state.actionsLoading ? /* @__PURE__ */ jsx15(ActionButtons, { actions: [], loading: true, onAct: () => {
920
938
  } }) : null,
921
- state.status ? /* @__PURE__ */ jsx14("div", { className: "ss-w-status", children: state.status }) : null,
922
- state.error ? /* @__PURE__ */ jsxs10("div", { className: "ss-w-error", role: "alert", children: [
923
- /* @__PURE__ */ jsx14("span", { children: "message" in state.error ? state.error.message : ui.somethingWentWrong }),
924
- /* @__PURE__ */ jsx14("button", { type: "button", className: "ss-w-retry", onClick: () => controller.retry(), children: ui.retry })
939
+ state.status ? /* @__PURE__ */ jsx15("div", { className: "ss-w-status", children: state.status }) : null,
940
+ state.error ? /* @__PURE__ */ jsxs11("div", { className: "ss-w-error", role: "alert", children: [
941
+ /* @__PURE__ */ jsx15("span", { children: "message" in state.error ? state.error.message : ui.somethingWentWrong }),
942
+ /* @__PURE__ */ jsx15("button", { type: "button", className: "ss-w-retry", onClick: () => controller.retry(), children: ui.retry })
925
943
  ] }) : null,
926
- empty ? /* @__PURE__ */ jsx14(IntentChips, { chips: state.starters, onPick: (t) => send(t) }) : null,
927
- /* @__PURE__ */ jsx14("div", { ref: endRef })
944
+ empty ? /* @__PURE__ */ jsx15(IntentChips, { chips: state.starters, onPick: (t) => send(t) }) : null,
945
+ /* @__PURE__ */ jsx15("div", { ref: endRef })
928
946
  ]
929
947
  }
930
948
  );
@@ -932,7 +950,7 @@ function MessageList({ controller, ui, slip }) {
932
950
 
933
951
  // src/components/ChatInput.tsx
934
952
  import { useState as useState5 } from "react";
935
- import { jsx as jsx15, jsxs as jsxs11 } from "react/jsx-runtime";
953
+ import { jsx as jsx16, jsxs as jsxs12 } from "react/jsx-runtime";
936
954
  function ChatInput({ placeholder, streaming, onSend, onStop, stopLabel = "Stop response", sendLabel = "Send message" }) {
937
955
  const [value, setValue] = useState5("");
938
956
  const submit = () => {
@@ -947,14 +965,14 @@ function ChatInput({ placeholder, streaming, onSend, onStop, stopLabel = "Stop r
947
965
  submit();
948
966
  }
949
967
  };
950
- return /* @__PURE__ */ jsxs11("div", { className: "ss-w-input", children: [
951
- /* @__PURE__ */ jsx15("textarea", { className: "ss-w-textarea", rows: 1, placeholder, value, disabled: streaming, onChange: (e) => setValue(e.target.value), onKeyDown }),
952
- streaming ? /* @__PURE__ */ jsx15("button", { type: "button", className: "ss-w-send ss-w-stop", "aria-label": stopLabel, onClick: onStop, children: /* @__PURE__ */ jsx15("span", { className: "ss-w-stop-glyph" }) }) : /* @__PURE__ */ jsx15("button", { type: "button", className: "ss-w-send", "aria-label": sendLabel, onClick: submit, children: /* @__PURE__ */ jsx15(SendIcon, {}) })
968
+ return /* @__PURE__ */ jsxs12("div", { className: "ss-w-input", children: [
969
+ /* @__PURE__ */ jsx16("textarea", { className: "ss-w-textarea", rows: 1, placeholder, value, disabled: streaming, onChange: (e) => setValue(e.target.value), onKeyDown }),
970
+ streaming ? /* @__PURE__ */ jsx16("button", { type: "button", className: "ss-w-send ss-w-stop", "aria-label": stopLabel, onClick: onStop, children: /* @__PURE__ */ jsx16("span", { className: "ss-w-stop-glyph" }) }) : /* @__PURE__ */ jsx16("button", { type: "button", className: "ss-w-send", "aria-label": sendLabel, onClick: submit, children: /* @__PURE__ */ jsx16(SendIcon, {}) })
953
971
  ] });
954
972
  }
955
973
 
956
974
  // src/components/ChatPanel.tsx
957
- import { jsx as jsx16, jsxs as jsxs12 } from "react/jsx-runtime";
975
+ import { jsx as jsx17, jsxs as jsxs13 } from "react/jsx-runtime";
958
976
  var FULLSCREEN_QUERY = "(max-width: 639px)";
959
977
  function useIsModal() {
960
978
  const [modal, setModal] = useState6(false);
@@ -978,10 +996,10 @@ function ChatPanel({ controller, ui, slip }) {
978
996
  window.addEventListener("keydown", onKey);
979
997
  return () => window.removeEventListener("keydown", onKey);
980
998
  }, [close]);
981
- return /* @__PURE__ */ jsxs12("div", { className: "ss-w-panel", role: "dialog", "aria-modal": isModal, "aria-label": ui.chatDialogLabel, children: [
982
- /* @__PURE__ */ jsx16(ChatHeader, { onClose: close, closeLabel: ui.closeChat }),
983
- /* @__PURE__ */ jsx16(MessageList, { controller, ui, slip }),
984
- /* @__PURE__ */ jsx16(ChatInput, { placeholder: ui.placeholder, streaming: state.isStreaming, onSend: (t) => send(t), onStop: stop, stopLabel: ui.stopResponse, sendLabel: ui.sendMessage })
999
+ return /* @__PURE__ */ jsxs13("div", { className: "ss-w-panel", role: "dialog", "aria-modal": isModal, "aria-label": ui.chatDialogLabel, children: [
1000
+ /* @__PURE__ */ jsx17(ChatHeader, { onClose: close, closeLabel: ui.closeChat }),
1001
+ /* @__PURE__ */ jsx17(MessageList, { controller, ui, slip }),
1002
+ /* @__PURE__ */ jsx17(ChatInput, { placeholder: ui.placeholder, streaming: state.isStreaming, onSend: (t) => send(t), onStop: stop, stopLabel: ui.stopResponse, sendLabel: ui.sendMessage })
985
1003
  ] });
986
1004
  }
987
1005
 
@@ -1170,10 +1188,13 @@ var WIDGET_CSS = `.ss-w-root { all: revert; }
1170
1188
  .ss-w-mo-price { margin-left: auto; font-family: ui-monospace, monospace; font-variant-numeric: tabular-nums; font-size: 14px; font-weight: 700; color: var(--ss-w-accent); }
1171
1189
  /* Movement: BOTH directions share --ss-w-faint on purpose. Green-good/red-bad would frame a
1172
1190
  shortening price as a discount, which inverts what it actually means for the bettor. */
1173
- .ss-w-mo-move { margin-left: auto; display: inline-flex; align-items: baseline; gap: 4px; color: var(--ss-w-faint); }
1174
- .ss-w-mo-prev { font-family: ui-monospace, monospace; font-variant-numeric: tabular-nums; font-size: 12px; text-decoration: line-through; }
1175
- .ss-w-mo-arrow { font-size: 9px; line-height: 1; }
1176
- .ss-w-mo-move + .ss-w-mo-price { margin-left: 0; }
1191
+ .ss-w-move { display: inline-flex; align-items: baseline; gap: 4px; color: var(--ss-w-faint); }
1192
+ .ss-w-move-prev { font-family: ui-monospace, monospace; font-variant-numeric: tabular-nums; font-size: 12px; text-decoration: line-through; }
1193
+ .ss-w-move-arrow { font-size: 9px; line-height: 1; }
1194
+ /* margin-left:auto is row layout, not badge styling -- it belongs to the market-odds row, which is
1195
+ a flex row, and must NOT follow the badge into the insight card's odds column. */
1196
+ .ss-w-mo-row .ss-w-move { margin-left: auto; }
1197
+ .ss-w-mo-row .ss-w-move + .ss-w-mo-price { margin-left: 0; }
1177
1198
  .ss-w-mo-row .ss-w-slip { margin: 0; }
1178
1199
  .ss-w-mo-foot { display: flex; align-items: center; gap: 8px; font-size: 10.5px; color: var(--ss-w-faint); }
1179
1200
 
@@ -1214,7 +1235,7 @@ var WIDGET_CSS = `.ss-w-root { all: revert; }
1214
1235
  `;
1215
1236
 
1216
1237
  // src/StatsWidget.tsx
1217
- import { jsx as jsx17 } from "react/jsx-runtime";
1238
+ import { jsx as jsx18 } from "react/jsx-runtime";
1218
1239
  function buildClient(props) {
1219
1240
  try {
1220
1241
  if ("client" in props && props.client) {
@@ -1263,7 +1284,7 @@ function StatsWidget(props) {
1263
1284
  }, [built, ui.injectStyles]);
1264
1285
  if (!built) return null;
1265
1286
  const attrs = themeAttrs(ui);
1266
- return /* @__PURE__ */ jsx17("div", { ref: rootRef, className: "ss-w-root", "data-ss-w-theme": attrs["data-ss-w-theme"], "data-ss-w-pos": attrs["data-ss-w-pos"], style: attrs.style, children: controller.state.isOpen ? /* @__PURE__ */ jsx17(ChatPanel, { controller, ui, slip }) : /* @__PURE__ */ jsx17(
1287
+ return /* @__PURE__ */ jsx18("div", { ref: rootRef, className: "ss-w-root", "data-ss-w-theme": attrs["data-ss-w-theme"], "data-ss-w-pos": attrs["data-ss-w-pos"], style: attrs.style, children: controller.state.isOpen ? /* @__PURE__ */ jsx18(ChatPanel, { controller, ui, slip }) : /* @__PURE__ */ jsx18(
1267
1288
  ChatFab,
1268
1289
  {
1269
1290
  label: ui.launcherLabel,