@sensiblestats/widget-react 0.3.0 → 0.4.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.d.ts CHANGED
@@ -33,6 +33,6 @@ type StatsWidgetProps = {
33
33
  };
34
34
  declare function StatsWidget(props: StatsWidgetProps): JSX.Element | null;
35
35
 
36
- declare const WIDGET_CSS = ".ss-w-root { all: revert; }\n.ss-w-root, .ss-w-root * { box-sizing: border-box; margin: 0; padding: 0; }\n.ss-w-root {\n --ss-w-accent: #17936a;\n --ss-w-ink: #14181d; --ss-w-faint: #6b7580; --ss-w-line: #e4e8ec;\n --ss-w-panel: #ffffff; --ss-w-bubble: #f3f5f7; --ss-w-nav: #0e1622;\n --ss-w-radius: 16px; --ss-w-offset-x: 20px; --ss-w-offset-y: 20px;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Helvetica, Arial, sans-serif;\n font-size: 13px; line-height: 1.5; color: var(--ss-w-ink);\n}\n@media (prefers-color-scheme: dark) {\n .ss-w-root[data-ss-w-theme=\"auto\"] {\n --ss-w-accent: #37c891; --ss-w-ink: #e8edf2; --ss-w-faint: #8b95a1;\n --ss-w-line: #2a3441; --ss-w-panel: #141b24; --ss-w-bubble: #1e2732; --ss-w-nav: #0a0f16;\n }\n}\n.ss-w-root[data-ss-w-theme=\"dark\"] {\n --ss-w-accent: #37c891; --ss-w-ink: #e8edf2; --ss-w-faint: #8b95a1;\n --ss-w-line: #2a3441; --ss-w-panel: #141b24; --ss-w-bubble: #1e2732; --ss-w-nav: #0a0f16;\n}\n.ss-w-root[data-ss-w-theme=\"light\"] {\n --ss-w-accent: #17936a; --ss-w-ink: #14181d; --ss-w-faint: #6b7580;\n --ss-w-line: #e4e8ec; --ss-w-panel: #ffffff; --ss-w-bubble: #f3f5f7; --ss-w-nav: #0e1622;\n}\n\n/* launcher */\n.ss-w-fab-wrap { position: fixed; bottom: var(--ss-w-offset-y); z-index: 2147483000; display: flex; flex-direction: column; align-items: flex-end; gap: 10px; }\n.ss-w-root[data-ss-w-pos=\"right\"] .ss-w-fab-wrap { right: var(--ss-w-offset-x); }\n.ss-w-root[data-ss-w-pos=\"left\"] .ss-w-fab-wrap { left: var(--ss-w-offset-x); align-items: flex-start; }\n.ss-w-fab { width: 56px; height: 56px; border: none; border-radius: 50%; background: var(--ss-w-nav); color: #fff; cursor: pointer; display: grid; place-items: center; box-shadow: 0 6px 20px rgba(0,0,0,.28); }\n.ss-w-fab-mark { width: 30px; height: 30px; display: block; }\n.ss-w-greeting { max-width: 240px; display: flex; align-items: flex-start; background: var(--ss-w-panel); color: var(--ss-w-ink); border: 1px solid var(--ss-w-line); border-radius: 12px; box-shadow: 0 4px 14px rgba(0,0,0,.14); }\n.ss-w-greeting-body { flex: 1; min-width: 0; text-align: left; background: none; border: none; color: inherit; font: inherit; cursor: pointer; padding: 9px 4px 9px 12px; }\n.ss-w-greeting-x { flex: none; background: none; border: none; color: var(--ss-w-faint); cursor: pointer; font-size: 17px; line-height: 1; padding: 7px 9px 0 2px; }\n\n/* panel */\n.ss-w-panel { position: fixed; bottom: calc(var(--ss-w-offset-y) + 72px); z-index: 2147483000; display: flex; flex-direction: column; width: min(400px, calc(100vw - 40px)); height: min(620px, calc(100vh - 120px)); background: var(--ss-w-panel); border: 1px solid var(--ss-w-line); border-radius: var(--ss-w-radius); overflow: hidden; box-shadow: 0 12px 40px rgba(0,0,0,.24); }\n.ss-w-root[data-ss-w-pos=\"right\"] .ss-w-panel { right: var(--ss-w-offset-x); }\n.ss-w-root[data-ss-w-pos=\"left\"] .ss-w-panel { left: var(--ss-w-offset-x); }\n@media (max-width: 639px) {\n /* Fill via inset with no explicit height (like the site's working panel) so the\n input stays visible when the mobile keyboard/URL-bar shrinks the viewport. The\n position-specific selectors override the right/left offsets set above. */\n .ss-w-root[data-ss-w-pos=\"right\"] .ss-w-panel,\n .ss-w-root[data-ss-w-pos=\"left\"] .ss-w-panel { inset: 0; width: auto; height: auto; border: none; border-radius: 0; }\n}\n.ss-w-header { display: flex; align-items: center; gap: 8px; padding: 12px 14px; border-bottom: 1px solid var(--ss-w-line); }\n.ss-w-header-mark { width: 18px; height: 18px; display: block; }\n.ss-w-header-title { font-weight: 660; }\n.ss-w-close { margin-left: auto; background: none; border: none; color: var(--ss-w-faint); cursor: pointer; font-size: 16px; display: grid; place-items: center; }\n\n/* messages */\n.ss-w-list { flex: 1; overflow-y: auto; padding: 14px; display: flex; flex-direction: column; gap: 12px; }\n.ss-w-msg { display: flex; }\n.ss-w-user { justify-content: flex-end; }\n.ss-w-user .ss-w-bubble { background: color-mix(in srgb, var(--ss-w-accent) 14%, var(--ss-w-panel)); border: 1px solid color-mix(in srgb, var(--ss-w-accent) 30%, var(--ss-w-line)); border-radius: 14px; padding: 8px 12px; max-width: 80%; }\n.ss-w-bot { flex-direction: column; gap: 8px; }\n.ss-w-status { font-size: 11px; color: var(--ss-w-faint); font-style: italic; }\n.ss-w-error { display: flex; align-items: center; gap: 8px; font-size: 12px; color: #c0392b; }\n.ss-w-retry { background: none; border: 1px solid currentColor; border-radius: 10px; padding: 2px 8px; cursor: pointer; color: inherit; }\n\n/* markdown */\n.ss-w-md { display: flex; flex-direction: column; gap: 8px; }\n.ss-w-md :where(p, ul, ol, table, pre, blockquote, h1, h2, h3, h4) { margin: 0; }\n.ss-w-md > *:first-child { margin-top: 0; }\n.ss-w-md > *:last-child { margin-bottom: 0; }\n.ss-w-md-ul { list-style: disc outside; padding-left: 18px; }\n.ss-w-md-ol { list-style: decimal outside; padding-left: 18px; }\n.ss-w-md-ul li::marker, .ss-w-md-ol li::marker { color: var(--ss-w-accent); }\n.ss-w-md-link { color: var(--ss-w-accent); text-decoration: underline; }\n.ss-w-table-wrap { overflow-x: auto; max-width: 100%; -webkit-overflow-scrolling: touch; }\n.ss-w-md-table { border-collapse: collapse; width: max-content; min-width: 100%; font-variant-numeric: tabular-nums; }\n.ss-w-md-table td, .ss-w-md-table th { border: 1px solid var(--ss-w-line); padding: 4px 8px; text-align: left; white-space: nowrap; }\n.ss-w-md-table th { background: color-mix(in srgb, var(--ss-w-accent) 12%, var(--ss-w-panel)); font-weight: 700; }\n.ss-w-md-table tbody tr:nth-child(even) { background: var(--ss-w-bubble); }\n.ss-w-md-code { background: var(--ss-w-bubble); border-radius: 5px; padding: 1px 5px; font-family: ui-monospace, monospace; }\n\n/* entity cards */\n.ss-w-cards { display: flex; flex-direction: column; gap: 7px; }\n.ss-w-ecard { display: flex; align-items: center; gap: 11px; width: 100%; background: var(--ss-w-panel); border: 1px solid var(--ss-w-line); border-radius: 12px; padding: 8px 11px; text-align: left; }\n.ss-w-thumb { width: 40px; height: 40px; flex: none; border-radius: 50%; display: grid; place-items: center; font-size: 13px; font-weight: 700; color: #fff; background-color: var(--ss-w-nav); background-size: cover; background-position: center; }\n.ss-w-thumb.ss-w-team { border-radius: 10px; }\n.ss-w-ecard[data-kind=\"player\"] .ss-w-thumb { background-color: var(--ss-w-accent); }\n.ss-w-meta { display: flex; flex-direction: column; min-width: 0; }\n.ss-w-kind { font-size: 8.5px; font-weight: 700; letter-spacing: .05em; text-transform: uppercase; color: var(--ss-w-faint); line-height: 1.3; }\n.ss-w-nm { font-weight: 660; }\n.ss-w-sub { font-size: 11px; color: var(--ss-w-faint); }\n.ss-w-stat { margin-left: auto; text-align: center; }\n.ss-w-stat b { display: block; font-variant-numeric: tabular-nums; }\n.ss-w-stat span { font-size: 9px; text-transform: uppercase; color: var(--ss-w-faint); }\n.ss-w-cards-more { align-self: flex-start; font-size: 11.5px; font-weight: 560; color: var(--ss-w-accent); background: none; border: none; cursor: pointer; padding: 3px 2px; }\n.ss-w-cards-more:hover:not(:disabled) { text-decoration: underline; }\n.ss-w-cards-more:disabled { opacity: .5; cursor: default; }\n\n/* action buttons */\n.ss-w-actions { display: flex; flex-direction: column; align-items: flex-start; gap: 6px; }\n.ss-w-abtn { font-size: 12px; font-weight: 560; color: var(--ss-w-accent); background: var(--ss-w-panel); border: 1px solid var(--ss-w-accent); border-radius: 16px; padding: 6px 12px; cursor: pointer; }\n.ss-w-abtn:disabled { opacity: .5; cursor: default; }\n.ss-w-abtn-shimmer { width: 140px; height: 30px; border-radius: 16px; background: linear-gradient(90deg, var(--ss-w-bubble), var(--ss-w-line), var(--ss-w-bubble)); background-size: 200% 100%; animation: ss-w-shimmer 1.2s infinite; }\n@keyframes ss-w-shimmer { from { background-position: 200% 0; } to { background-position: -200% 0; } }\n\n/* intent chips */\n.ss-w-chips { display: flex; flex-wrap: wrap; gap: 6px; }\n.ss-w-chip { font-size: 11.5px; font-weight: 550; color: var(--ss-w-ink); background: var(--ss-w-bubble); border: 1px solid var(--ss-w-line); border-radius: 14px; padding: 5px 11px; cursor: pointer; }\n\n/* input */\n.ss-w-input { display: flex; align-items: flex-end; gap: 8px; padding: 10px 12px; border-top: 1px solid var(--ss-w-line); }\n.ss-w-textarea { flex: 1; resize: none; border: 1px solid var(--ss-w-line); border-radius: 12px; padding: 8px 10px; font: inherit; color: inherit; background: var(--ss-w-panel); max-height: 96px; }\n.ss-w-textarea:disabled { opacity: .55; cursor: not-allowed; }\n/* iOS Safari zooms the page when a focused field's font-size is < 16px. Bump it\n on touch devices only, so the desktop 13px design is unchanged. */\n@media (pointer: coarse) {\n .ss-w-textarea { font-size: 16px; }\n}\n.ss-w-send { width: 36px; height: 36px; flex: none; border: none; border-radius: 50%; background: var(--ss-w-accent); color: #fff; cursor: pointer; display: grid; place-items: center; font-size: 16px; }\n.ss-w-stop-glyph { width: 11px; height: 11px; background: currentColor; border-radius: 2px; }\n\n@media (prefers-reduced-motion: reduce) {\n .ss-w-abtn-shimmer { animation: none; }\n}\n";
36
+ declare const WIDGET_CSS = ".ss-w-root { all: revert; }\n.ss-w-root, .ss-w-root * { box-sizing: border-box; margin: 0; padding: 0; }\n.ss-w-root {\n --ss-w-accent: #17936a;\n --ss-w-ink: #14181d; --ss-w-faint: #6b7580; --ss-w-line: #e4e8ec;\n --ss-w-panel: #ffffff; --ss-w-bubble: #f3f5f7; --ss-w-nav: #0e1622;\n --ss-w-radius: 16px; --ss-w-offset-x: 20px; --ss-w-offset-y: 20px;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Helvetica, Arial, sans-serif;\n font-size: 13px; line-height: 1.5; color: var(--ss-w-ink);\n}\n@media (prefers-color-scheme: dark) {\n .ss-w-root[data-ss-w-theme=\"auto\"] {\n --ss-w-accent: #37c891; --ss-w-ink: #e8edf2; --ss-w-faint: #8b95a1;\n --ss-w-line: #2a3441; --ss-w-panel: #141b24; --ss-w-bubble: #1e2732; --ss-w-nav: #0a0f16;\n }\n}\n.ss-w-root[data-ss-w-theme=\"dark\"] {\n --ss-w-accent: #37c891; --ss-w-ink: #e8edf2; --ss-w-faint: #8b95a1;\n --ss-w-line: #2a3441; --ss-w-panel: #141b24; --ss-w-bubble: #1e2732; --ss-w-nav: #0a0f16;\n}\n.ss-w-root[data-ss-w-theme=\"light\"] {\n --ss-w-accent: #17936a; --ss-w-ink: #14181d; --ss-w-faint: #6b7580;\n --ss-w-line: #e4e8ec; --ss-w-panel: #ffffff; --ss-w-bubble: #f3f5f7; --ss-w-nav: #0e1622;\n}\n\n/* launcher */\n.ss-w-fab-wrap { position: fixed; bottom: var(--ss-w-offset-y); z-index: 2147483000; display: flex; flex-direction: column; align-items: flex-end; gap: 10px; }\n.ss-w-root[data-ss-w-pos=\"right\"] .ss-w-fab-wrap { right: var(--ss-w-offset-x); }\n.ss-w-root[data-ss-w-pos=\"left\"] .ss-w-fab-wrap { left: var(--ss-w-offset-x); align-items: flex-start; }\n.ss-w-fab { width: 56px; height: 56px; border: none; border-radius: 50%; background: var(--ss-w-nav); color: #fff; cursor: pointer; display: grid; place-items: center; box-shadow: 0 6px 20px rgba(0,0,0,.28); }\n.ss-w-fab-mark { width: 30px; height: 30px; display: block; }\n.ss-w-greeting { max-width: 240px; display: flex; align-items: flex-start; background: var(--ss-w-panel); color: var(--ss-w-ink); border: 1px solid var(--ss-w-line); border-radius: 12px; box-shadow: 0 4px 14px rgba(0,0,0,.14); }\n.ss-w-greeting-body { flex: 1; min-width: 0; text-align: left; background: none; border: none; color: inherit; font: inherit; cursor: pointer; padding: 9px 4px 9px 12px; }\n.ss-w-greeting-x { flex: none; background: none; border: none; color: var(--ss-w-faint); cursor: pointer; font-size: 17px; line-height: 1; padding: 7px 9px 0 2px; }\n\n/* panel */\n.ss-w-panel { position: fixed; bottom: calc(var(--ss-w-offset-y) + 72px); z-index: 2147483000; display: flex; flex-direction: column; width: min(400px, calc(100vw - 40px)); height: min(620px, calc(100vh - 120px)); background: var(--ss-w-panel); border: 1px solid var(--ss-w-line); border-radius: var(--ss-w-radius); overflow: hidden; box-shadow: 0 12px 40px rgba(0,0,0,.24); }\n.ss-w-root[data-ss-w-pos=\"right\"] .ss-w-panel { right: var(--ss-w-offset-x); }\n.ss-w-root[data-ss-w-pos=\"left\"] .ss-w-panel { left: var(--ss-w-offset-x); }\n@media (max-width: 639px) {\n /* Fill via inset with no explicit height (like the site's working panel) so the\n input stays visible when the mobile keyboard/URL-bar shrinks the viewport. The\n position-specific selectors override the right/left offsets set above. */\n .ss-w-root[data-ss-w-pos=\"right\"] .ss-w-panel,\n .ss-w-root[data-ss-w-pos=\"left\"] .ss-w-panel { inset: 0; width: auto; height: auto; border: none; border-radius: 0; }\n}\n.ss-w-header { display: flex; align-items: center; gap: 8px; padding: 12px 14px; border-bottom: 1px solid var(--ss-w-line); }\n.ss-w-header-mark { width: 18px; height: 18px; display: block; }\n.ss-w-header-title { font-weight: 660; }\n.ss-w-close { margin-left: auto; background: none; border: none; color: var(--ss-w-faint); cursor: pointer; font-size: 16px; display: grid; place-items: center; }\n\n/* messages */\n.ss-w-list { flex: 1; overflow-y: auto; padding: 14px; display: flex; flex-direction: column; gap: 12px; }\n.ss-w-msg { display: flex; }\n.ss-w-user { justify-content: flex-end; }\n.ss-w-user .ss-w-bubble { background: color-mix(in srgb, var(--ss-w-accent) 14%, var(--ss-w-panel)); border: 1px solid color-mix(in srgb, var(--ss-w-accent) 30%, var(--ss-w-line)); border-radius: 14px; padding: 8px 12px; max-width: 80%; }\n.ss-w-bot { flex-direction: column; gap: 8px; }\n.ss-w-status { font-size: 11px; color: var(--ss-w-faint); font-style: italic; }\n.ss-w-error { display: flex; align-items: center; gap: 8px; font-size: 12px; color: #c0392b; }\n.ss-w-retry { background: none; border: 1px solid currentColor; border-radius: 10px; padding: 2px 8px; cursor: pointer; color: inherit; }\n\n/* markdown */\n.ss-w-md { display: flex; flex-direction: column; gap: 8px; }\n.ss-w-md :where(p, ul, ol, table, pre, blockquote, h1, h2, h3, h4) { margin: 0; }\n.ss-w-md > *:first-child { margin-top: 0; }\n.ss-w-md > *:last-child { margin-bottom: 0; }\n.ss-w-md-ul { list-style: disc outside; padding-left: 18px; }\n.ss-w-md-ol { list-style: decimal outside; padding-left: 18px; }\n.ss-w-md-ul li::marker, .ss-w-md-ol li::marker { color: var(--ss-w-accent); }\n.ss-w-md-link { color: var(--ss-w-accent); text-decoration: underline; }\n.ss-w-table-wrap { overflow-x: auto; max-width: 100%; -webkit-overflow-scrolling: touch; }\n.ss-w-md-table { border-collapse: collapse; width: max-content; min-width: 100%; font-variant-numeric: tabular-nums; }\n.ss-w-md-table td, .ss-w-md-table th { border: 1px solid var(--ss-w-line); padding: 4px 8px; text-align: left; white-space: nowrap; }\n.ss-w-md-table th { background: color-mix(in srgb, var(--ss-w-accent) 12%, var(--ss-w-panel)); font-weight: 700; }\n.ss-w-md-table tbody tr:nth-child(even) { background: var(--ss-w-bubble); }\n.ss-w-md-code { background: var(--ss-w-bubble); border-radius: 5px; padding: 1px 5px; font-family: ui-monospace, monospace; }\n\n/* entity cards */\n.ss-w-cards { display: flex; flex-direction: column; gap: 7px; }\n.ss-w-ecard { display: flex; align-items: center; gap: 11px; width: 100%; background: var(--ss-w-panel); border: 1px solid var(--ss-w-line); border-radius: 12px; padding: 8px 11px; text-align: left; }\n.ss-w-thumb { width: 40px; height: 40px; flex: none; border-radius: 50%; display: grid; place-items: center; font-size: 13px; font-weight: 700; color: #fff; background-color: var(--ss-w-nav); background-size: cover; background-position: center; }\n.ss-w-thumb.ss-w-team { border-radius: 10px; }\n.ss-w-ecard[data-kind=\"player\"] .ss-w-thumb { background-color: var(--ss-w-accent); }\n.ss-w-meta { display: flex; flex-direction: column; min-width: 0; }\n.ss-w-kind { font-size: 8.5px; font-weight: 700; letter-spacing: .05em; text-transform: uppercase; color: var(--ss-w-faint); line-height: 1.3; }\n.ss-w-nm { font-weight: 660; }\n.ss-w-sub { font-size: 11px; color: var(--ss-w-faint); }\n.ss-w-stat { margin-left: auto; text-align: center; }\n.ss-w-stat b { display: block; font-variant-numeric: tabular-nums; }\n.ss-w-stat span { font-size: 9px; text-transform: uppercase; color: var(--ss-w-faint); }\n.ss-w-cards-more { align-self: flex-start; font-size: 11.5px; font-weight: 560; color: var(--ss-w-accent); background: none; border: none; cursor: pointer; padding: 3px 2px; }\n.ss-w-cards-more:hover:not(:disabled) { text-decoration: underline; }\n.ss-w-cards-more:disabled { opacity: .5; cursor: default; }\n\n/* betting insights */\n.ss-w-binsights { display: flex; flex-direction: column; gap: 7px; }\n.ss-w-binsight { background: var(--ss-w-panel); border: 1px solid var(--ss-w-line); border-radius: 12px; border-left: 3px solid var(--ss-w-accent); padding: 9px 11px; display: flex; flex-direction: column; gap: 6px; }\n.ss-w-bi-head { display: flex; align-items: flex-start; gap: 10px; }\n.ss-w-bi-mkt { display: flex; flex-direction: column; min-width: 0; }\n.ss-w-bi-market { font-size: 8.5px; font-weight: 700; letter-spacing: .05em; text-transform: uppercase; color: var(--ss-w-faint); line-height: 1.3; }\n.ss-w-bi-sel { font-weight: 660; }\n.ss-w-bi-odds { margin-left: auto; display: flex; align-items: center; gap: 6px; flex: none; }\n.ss-w-bi-odds b { font-size: 15px; font-variant-numeric: tabular-nums; color: var(--ss-w-accent); }\n.ss-w-bi-book { font-size: 10px; color: var(--ss-w-faint); }\n.ss-w-bi-live { font-size: 8.5px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase; color: #fff; background: #c0392b; border-radius: 4px; padding: 1px 5px; }\n.ss-w-bi-text { font-size: 12px; color: var(--ss-w-ink); }\n.ss-w-bi-stats { display: flex; flex-wrap: wrap; gap: 12px; }\n.ss-w-bi-stat b { display: block; font-variant-numeric: tabular-nums; font-weight: 660; }\n.ss-w-bi-stat span { font-size: 9px; text-transform: uppercase; color: var(--ss-w-faint); }\n.ss-w-bi-disc { font-size: 10px; font-style: italic; color: var(--ss-w-faint); }\n\n/* action buttons */\n.ss-w-actions { display: flex; flex-direction: column; align-items: flex-start; gap: 6px; }\n.ss-w-abtn { font-size: 12px; font-weight: 560; color: var(--ss-w-accent); background: var(--ss-w-panel); border: 1px solid var(--ss-w-accent); border-radius: 16px; padding: 6px 12px; cursor: pointer; }\n.ss-w-abtn:disabled { opacity: .5; cursor: default; }\n.ss-w-abtn-shimmer { width: 140px; height: 30px; border-radius: 16px; background: linear-gradient(90deg, var(--ss-w-bubble), var(--ss-w-line), var(--ss-w-bubble)); background-size: 200% 100%; animation: ss-w-shimmer 1.2s infinite; }\n@keyframes ss-w-shimmer { from { background-position: 200% 0; } to { background-position: -200% 0; } }\n\n/* intent chips */\n.ss-w-chips { display: flex; flex-wrap: wrap; gap: 6px; }\n.ss-w-chip { font-size: 11.5px; font-weight: 550; color: var(--ss-w-ink); background: var(--ss-w-bubble); border: 1px solid var(--ss-w-line); border-radius: 14px; padding: 5px 11px; cursor: pointer; }\n\n/* input */\n.ss-w-input { display: flex; align-items: flex-end; gap: 8px; padding: 10px 12px; border-top: 1px solid var(--ss-w-line); }\n.ss-w-textarea { flex: 1; resize: none; border: 1px solid var(--ss-w-line); border-radius: 12px; padding: 8px 10px; font: inherit; color: inherit; background: var(--ss-w-panel); max-height: 96px; }\n.ss-w-textarea:disabled { opacity: .55; cursor: not-allowed; }\n/* iOS Safari zooms the page when a focused field's font-size is < 16px. Bump it\n on touch devices only, so the desktop 13px design is unchanged. */\n@media (pointer: coarse) {\n .ss-w-textarea { font-size: 16px; }\n}\n.ss-w-send { width: 36px; height: 36px; flex: none; border: none; border-radius: 50%; background: var(--ss-w-accent); color: #fff; cursor: pointer; display: grid; place-items: center; font-size: 16px; }\n.ss-w-stop-glyph { width: 11px; height: 11px; background: currentColor; border-radius: 2px; }\n\n@media (prefers-reduced-motion: reduce) {\n .ss-w-abtn-shimmer { animation: none; }\n}\n";
37
37
 
38
38
  export { StatsWidget, type StatsWidgetConfig, type StatsWidgetProps, WIDGET_CSS };
package/dist/index.js CHANGED
@@ -95,16 +95,19 @@ function str(v) {
95
95
  function num(v) {
96
96
  return typeof v === "number" && Number.isFinite(v) ? String(v) : void 0;
97
97
  }
98
- function readStats(card) {
99
- if (!Array.isArray(card.stats)) return [];
98
+ function readStatCells(raw) {
99
+ if (!Array.isArray(raw)) return [];
100
100
  const cells = [];
101
- for (const entry of card.stats) {
101
+ for (const entry of raw) {
102
102
  const label = str(entry.label);
103
103
  const value = num(entry.value) ?? str(entry.value);
104
104
  if (label !== void 0 && value !== void 0) cells.push({ label, value });
105
105
  }
106
106
  return cells;
107
107
  }
108
+ function readStats(card) {
109
+ return readStatCells(card.stats);
110
+ }
108
111
  function toCardVM(card) {
109
112
  const entityType = str(card.entityType)?.toLowerCase();
110
113
  const kind = entityType === "player" ? "player" : entityType === "team" ? "team" : "generic";
@@ -130,6 +133,24 @@ function toChipVM(intent) {
130
133
  function chipFromText(text) {
131
134
  return { text };
132
135
  }
136
+ function toInsightVM(insight) {
137
+ const market = str(insight.marketLabel) ?? str(insight.marketDeveloperName) ?? "";
138
+ const selection = [str(insight.selectionLabel), str(insight.line)].filter(Boolean).join(" ") || void 0;
139
+ const value = insight.odds?.value;
140
+ const odds = typeof value === "number" && Number.isFinite(value) ? value.toFixed(2) : void 0;
141
+ return {
142
+ id: str(insight.insightId) ?? `${str(insight.ruleId) ?? "insight"}:${num(insight.matchId) ?? ""}`,
143
+ market,
144
+ selection,
145
+ odds,
146
+ // The wire payload carries bookmakerId (not a name); render a name only if one is present.
147
+ bookmaker: str(insight.odds?.bookmakerName),
148
+ isLive: insight.odds?.isLive === true,
149
+ text: str(insight.text) ?? "",
150
+ stats: readStatCells(insight.stats),
151
+ disclaimer: str(insight.disclaimer)
152
+ };
153
+ }
133
154
 
134
155
  // src/reducer.ts
135
156
  var POPUP_MAX = 160;
@@ -156,6 +177,8 @@ function applyEvent(state, event) {
156
177
  return { ...state, messages: mapActive(state, (t) => ({ ...t, cards: [...t.cards, toCardVM(event.data.card)] })) };
157
178
  case "action_button":
158
179
  return { ...state, actionsLoading: false, messages: mapActive(state, (t) => ({ ...t, actions: [...t.actions, toActionVM(event.data.action)] })) };
180
+ case "betting_insight":
181
+ return { ...state, messages: mapActive(state, (t) => ({ ...t, insights: [...t.insights, toInsightVM(event.data.insight)] })) };
159
182
  case "intent_chip": {
160
183
  const chip = toChipVM(event.data.intent);
161
184
  return chip ? { ...state, starters: [...state.starters, chip] } : state;
@@ -173,6 +196,7 @@ function applyEvent(state, event) {
173
196
  return { ...state, error: suppress ? state.error : event.data, isStreaming: false, status: null, actionsLoading: false, messages: mapActive(state, (t) => ({ ...t, done: true })) };
174
197
  }
175
198
  }
199
+ return state;
176
200
  }
177
201
  function reducer(state, action) {
178
202
  switch (action.kind) {
@@ -182,11 +206,11 @@ function reducer(state, action) {
182
206
  return { ...state, isOpen: false, isStreaming: false, status: null, actionsLoading: false };
183
207
  case "USER_SENT": {
184
208
  const user = { role: "user", id: action.userId, text: action.displayText ?? action.input.message };
185
- const assistant = { role: "assistant", id: action.assistantId, text: "", cards: [], actions: [], done: false };
209
+ const assistant = { role: "assistant", id: action.assistantId, text: "", cards: [], actions: [], insights: [], done: false };
186
210
  return { ...state, messages: [...state.messages, user, assistant], isStreaming: true, status: null, actionsLoading: false, starters: [], error: null, lastUserInput: action.input, lastDisplayText: action.displayText ?? null };
187
211
  }
188
212
  case "GREETING_SENT": {
189
- const assistant = { role: "assistant", id: action.assistantId, text: "", cards: [], actions: [], done: false, isGreeting: true };
213
+ const assistant = { role: "assistant", id: action.assistantId, text: "", cards: [], actions: [], insights: [], done: false, isGreeting: true };
190
214
  return { ...state, messages: [...state.messages, assistant], isStreaming: true, status: null, error: null };
191
215
  }
192
216
  case "DISMISS_POPUP":
@@ -374,37 +398,63 @@ function EntityCardList({ cards, disabled, showLessLabel = "Show less", showMore
374
398
  ] });
375
399
  }
376
400
 
401
+ // src/components/BettingInsightList.tsx
402
+ import { jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
403
+ function BettingInsightList({ insights }) {
404
+ if (!insights || insights.length === 0) return null;
405
+ return /* @__PURE__ */ jsx7("div", { className: "ss-w-binsights", children: insights.map((it) => /* @__PURE__ */ jsxs5("div", { className: "ss-w-binsight", children: [
406
+ /* @__PURE__ */ jsxs5("div", { className: "ss-w-bi-head", children: [
407
+ /* @__PURE__ */ jsxs5("span", { className: "ss-w-bi-mkt", children: [
408
+ /* @__PURE__ */ jsx7("span", { className: "ss-w-bi-market", children: it.market }),
409
+ it.selection ? /* @__PURE__ */ jsx7("span", { className: "ss-w-bi-sel", children: it.selection }) : null
410
+ ] }),
411
+ it.odds ? /* @__PURE__ */ jsxs5("span", { className: "ss-w-bi-odds", children: [
412
+ it.isLive ? /* @__PURE__ */ jsx7("span", { className: "ss-w-bi-live", children: "Live" }) : null,
413
+ /* @__PURE__ */ jsx7("b", { children: it.odds }),
414
+ it.bookmaker ? /* @__PURE__ */ jsx7("span", { className: "ss-w-bi-book", children: it.bookmaker }) : null
415
+ ] }) : null
416
+ ] }),
417
+ it.text ? /* @__PURE__ */ jsx7("p", { className: "ss-w-bi-text", children: it.text }) : null,
418
+ it.stats.length > 0 ? /* @__PURE__ */ jsx7("div", { className: "ss-w-bi-stats", children: it.stats.map((s) => /* @__PURE__ */ jsxs5("span", { className: "ss-w-bi-stat", children: [
419
+ /* @__PURE__ */ jsx7("b", { children: s.value }),
420
+ /* @__PURE__ */ jsx7("span", { children: s.label })
421
+ ] }, `${s.label}:${s.value}`)) }) : null,
422
+ it.disclaimer ? /* @__PURE__ */ jsx7("p", { className: "ss-w-bi-disc", children: it.disclaimer }) : null
423
+ ] }, it.id)) });
424
+ }
425
+
377
426
  // src/components/ActionButtons.tsx
378
- import { Fragment, jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
427
+ import { Fragment, jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
379
428
  function ActionButtons({ actions, loading, disabled, onAct }) {
380
429
  if (actions.length === 0 && !loading) return null;
381
- return /* @__PURE__ */ jsxs5("div", { className: "ss-w-actions", children: [
382
- actions.map((a, i) => /* @__PURE__ */ jsx7("button", { type: "button", className: "ss-w-abtn", disabled, onClick: () => onAct(a), children: a.label }, `${a.label}-${i}`)),
383
- actions.length === 0 && loading ? /* @__PURE__ */ jsxs5(Fragment, { children: [
384
- /* @__PURE__ */ jsx7("span", { className: "ss-w-abtn-shimmer" }),
385
- /* @__PURE__ */ jsx7("span", { className: "ss-w-abtn-shimmer" })
430
+ return /* @__PURE__ */ jsxs6("div", { className: "ss-w-actions", children: [
431
+ actions.map((a, i) => /* @__PURE__ */ jsx8("button", { type: "button", className: "ss-w-abtn", disabled, onClick: () => onAct(a), children: a.label }, `${a.label}-${i}`)),
432
+ actions.length === 0 && loading ? /* @__PURE__ */ jsxs6(Fragment, { children: [
433
+ /* @__PURE__ */ jsx8("span", { className: "ss-w-abtn-shimmer" }),
434
+ /* @__PURE__ */ jsx8("span", { className: "ss-w-abtn-shimmer" })
386
435
  ] }) : null
387
436
  ] });
388
437
  }
389
438
 
390
439
  // src/components/ChatMessage.tsx
391
- import { jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
440
+ import { jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
392
441
  function ChatMessage({ message, disabled, onAct, ui }) {
393
442
  if (message.role === "user") {
394
- return /* @__PURE__ */ jsx8("div", { className: "ss-w-msg ss-w-user", children: /* @__PURE__ */ jsx8("div", { className: "ss-w-bubble", children: message.text }) });
443
+ return /* @__PURE__ */ jsx9("div", { className: "ss-w-msg ss-w-user", children: /* @__PURE__ */ jsx9("div", { className: "ss-w-bubble", children: message.text }) });
395
444
  }
396
- return /* @__PURE__ */ jsxs6("div", { className: "ss-w-msg ss-w-bot", children: [
397
- message.text ? /* @__PURE__ */ jsx8(Markdown, { text: message.text }) : null,
398
- /* @__PURE__ */ jsx8(EntityCardList, { cards: message.cards, disabled, showLessLabel: ui?.showLess, showMoreLabel: ui?.showMore }),
399
- /* @__PURE__ */ jsx8(ActionButtons, { actions: message.actions, loading: false, disabled, onAct })
445
+ return /* @__PURE__ */ jsxs7("div", { className: "ss-w-msg ss-w-bot", children: [
446
+ message.text ? /* @__PURE__ */ jsx9(Markdown, { text: message.text }) : null,
447
+ /* @__PURE__ */ jsx9(EntityCardList, { cards: message.cards, disabled, showLessLabel: ui?.showLess, showMoreLabel: ui?.showMore }),
448
+ /* @__PURE__ */ jsx9(BettingInsightList, { insights: message.insights }),
449
+ /* @__PURE__ */ jsx9(ActionButtons, { actions: message.actions, loading: false, disabled, onAct })
400
450
  ] });
401
451
  }
402
452
 
403
453
  // src/components/IntentChips.tsx
404
- import { jsx as jsx9 } from "react/jsx-runtime";
454
+ import { jsx as jsx10 } from "react/jsx-runtime";
405
455
  function IntentChips({ chips, onPick }) {
406
456
  if (chips.length === 0) return null;
407
- return /* @__PURE__ */ jsx9("div", { className: "ss-w-chips", children: chips.map((c, i) => /* @__PURE__ */ jsx9("button", { type: "button", className: "ss-w-chip", onClick: () => onPick(c.text), children: c.text }, `${c.text}-${i}`)) });
457
+ return /* @__PURE__ */ jsx10("div", { className: "ss-w-chips", children: chips.map((c, i) => /* @__PURE__ */ jsx10("button", { type: "button", className: "ss-w-chip", onClick: () => onPick(c.text), children: c.text }, `${c.text}-${i}`)) });
408
458
  }
409
459
 
410
460
  // src/scroll.ts
@@ -414,7 +464,7 @@ function isPinnedToBottom(el, threshold = PIN_THRESHOLD) {
414
464
  }
415
465
 
416
466
  // src/components/MessageList.tsx
417
- import { jsx as jsx10, jsxs as jsxs7 } from "react/jsx-runtime";
467
+ import { jsx as jsx11, jsxs as jsxs8 } from "react/jsx-runtime";
418
468
  function MessageList({ controller, ui }) {
419
469
  const { state, send } = controller;
420
470
  const listRef = useRef2(null);
@@ -424,7 +474,7 @@ function MessageList({ controller, ui }) {
424
474
  if (pinnedRef.current) endRef.current?.scrollIntoView({ block: "end" });
425
475
  }, [state.messages, state.status]);
426
476
  const empty = state.messages.length === 0;
427
- return /* @__PURE__ */ jsxs7(
477
+ return /* @__PURE__ */ jsxs8(
428
478
  "div",
429
479
  {
430
480
  className: "ss-w-list",
@@ -434,7 +484,7 @@ function MessageList({ controller, ui }) {
434
484
  if (listRef.current) pinnedRef.current = isPinnedToBottom(listRef.current);
435
485
  },
436
486
  children: [
437
- state.messages.map((m) => /* @__PURE__ */ jsx10(
487
+ state.messages.map((m) => /* @__PURE__ */ jsx11(
438
488
  ChatMessage,
439
489
  {
440
490
  message: m,
@@ -444,15 +494,15 @@ function MessageList({ controller, ui }) {
444
494
  },
445
495
  m.id
446
496
  )),
447
- state.isStreaming && state.actionsLoading ? /* @__PURE__ */ jsx10(ActionButtons, { actions: [], loading: true, onAct: () => {
497
+ state.isStreaming && state.actionsLoading ? /* @__PURE__ */ jsx11(ActionButtons, { actions: [], loading: true, onAct: () => {
448
498
  } }) : null,
449
- state.status ? /* @__PURE__ */ jsx10("div", { className: "ss-w-status", children: state.status }) : null,
450
- state.error ? /* @__PURE__ */ jsxs7("div", { className: "ss-w-error", role: "alert", children: [
451
- /* @__PURE__ */ jsx10("span", { children: "message" in state.error ? state.error.message : ui.somethingWentWrong }),
452
- /* @__PURE__ */ jsx10("button", { type: "button", className: "ss-w-retry", onClick: () => controller.retry(), children: ui.retry })
499
+ state.status ? /* @__PURE__ */ jsx11("div", { className: "ss-w-status", children: state.status }) : null,
500
+ state.error ? /* @__PURE__ */ jsxs8("div", { className: "ss-w-error", role: "alert", children: [
501
+ /* @__PURE__ */ jsx11("span", { children: "message" in state.error ? state.error.message : ui.somethingWentWrong }),
502
+ /* @__PURE__ */ jsx11("button", { type: "button", className: "ss-w-retry", onClick: () => controller.retry(), children: ui.retry })
453
503
  ] }) : null,
454
- empty ? /* @__PURE__ */ jsx10(IntentChips, { chips: state.starters, onPick: (t) => send(t) }) : null,
455
- /* @__PURE__ */ jsx10("div", { ref: endRef })
504
+ empty ? /* @__PURE__ */ jsx11(IntentChips, { chips: state.starters, onPick: (t) => send(t) }) : null,
505
+ /* @__PURE__ */ jsx11("div", { ref: endRef })
456
506
  ]
457
507
  }
458
508
  );
@@ -460,7 +510,7 @@ function MessageList({ controller, ui }) {
460
510
 
461
511
  // src/components/ChatInput.tsx
462
512
  import { useState as useState2 } from "react";
463
- import { jsx as jsx11, jsxs as jsxs8 } from "react/jsx-runtime";
513
+ import { jsx as jsx12, jsxs as jsxs9 } from "react/jsx-runtime";
464
514
  function ChatInput({ placeholder, streaming, onSend, onStop, stopLabel = "Stop response", sendLabel = "Send message" }) {
465
515
  const [value, setValue] = useState2("");
466
516
  const submit = () => {
@@ -475,14 +525,14 @@ function ChatInput({ placeholder, streaming, onSend, onStop, stopLabel = "Stop r
475
525
  submit();
476
526
  }
477
527
  };
478
- return /* @__PURE__ */ jsxs8("div", { className: "ss-w-input", children: [
479
- /* @__PURE__ */ jsx11("textarea", { className: "ss-w-textarea", rows: 1, placeholder, value, disabled: streaming, onChange: (e) => setValue(e.target.value), onKeyDown }),
480
- streaming ? /* @__PURE__ */ jsx11("button", { type: "button", className: "ss-w-send ss-w-stop", "aria-label": stopLabel, onClick: onStop, children: /* @__PURE__ */ jsx11("span", { className: "ss-w-stop-glyph" }) }) : /* @__PURE__ */ jsx11("button", { type: "button", className: "ss-w-send", "aria-label": sendLabel, onClick: submit, children: /* @__PURE__ */ jsx11(SendIcon, {}) })
528
+ return /* @__PURE__ */ jsxs9("div", { className: "ss-w-input", children: [
529
+ /* @__PURE__ */ jsx12("textarea", { className: "ss-w-textarea", rows: 1, placeholder, value, disabled: streaming, onChange: (e) => setValue(e.target.value), onKeyDown }),
530
+ streaming ? /* @__PURE__ */ jsx12("button", { type: "button", className: "ss-w-send ss-w-stop", "aria-label": stopLabel, onClick: onStop, children: /* @__PURE__ */ jsx12("span", { className: "ss-w-stop-glyph" }) }) : /* @__PURE__ */ jsx12("button", { type: "button", className: "ss-w-send", "aria-label": sendLabel, onClick: submit, children: /* @__PURE__ */ jsx12(SendIcon, {}) })
481
531
  ] });
482
532
  }
483
533
 
484
534
  // src/components/ChatPanel.tsx
485
- import { jsx as jsx12, jsxs as jsxs9 } from "react/jsx-runtime";
535
+ import { jsx as jsx13, jsxs as jsxs10 } from "react/jsx-runtime";
486
536
  var FULLSCREEN_QUERY = "(max-width: 639px)";
487
537
  function useIsModal() {
488
538
  const [modal, setModal] = useState3(false);
@@ -506,10 +556,10 @@ function ChatPanel({ controller, ui }) {
506
556
  window.addEventListener("keydown", onKey);
507
557
  return () => window.removeEventListener("keydown", onKey);
508
558
  }, [close]);
509
- return /* @__PURE__ */ jsxs9("div", { className: "ss-w-panel", role: "dialog", "aria-modal": isModal, "aria-label": ui.chatDialogLabel, children: [
510
- /* @__PURE__ */ jsx12(ChatHeader, { onClose: close, closeLabel: ui.closeChat }),
511
- /* @__PURE__ */ jsx12(MessageList, { controller, ui }),
512
- /* @__PURE__ */ jsx12(ChatInput, { placeholder: ui.placeholder, streaming: state.isStreaming, onSend: (t) => send(t), onStop: stop, stopLabel: ui.stopResponse, sendLabel: ui.sendMessage })
559
+ return /* @__PURE__ */ jsxs10("div", { className: "ss-w-panel", role: "dialog", "aria-modal": isModal, "aria-label": ui.chatDialogLabel, children: [
560
+ /* @__PURE__ */ jsx13(ChatHeader, { onClose: close, closeLabel: ui.closeChat }),
561
+ /* @__PURE__ */ jsx13(MessageList, { controller, ui }),
562
+ /* @__PURE__ */ jsx13(ChatInput, { placeholder: ui.placeholder, streaming: state.isStreaming, onSend: (t) => send(t), onStop: stop, stopLabel: ui.stopResponse, sendLabel: ui.sendMessage })
513
563
  ] });
514
564
  }
515
565
 
@@ -608,6 +658,23 @@ var WIDGET_CSS = `.ss-w-root { all: revert; }
608
658
  .ss-w-cards-more:hover:not(:disabled) { text-decoration: underline; }
609
659
  .ss-w-cards-more:disabled { opacity: .5; cursor: default; }
610
660
 
661
+ /* betting insights */
662
+ .ss-w-binsights { display: flex; flex-direction: column; gap: 7px; }
663
+ .ss-w-binsight { background: var(--ss-w-panel); border: 1px solid var(--ss-w-line); border-radius: 12px; border-left: 3px solid var(--ss-w-accent); padding: 9px 11px; display: flex; flex-direction: column; gap: 6px; }
664
+ .ss-w-bi-head { display: flex; align-items: flex-start; gap: 10px; }
665
+ .ss-w-bi-mkt { display: flex; flex-direction: column; min-width: 0; }
666
+ .ss-w-bi-market { font-size: 8.5px; font-weight: 700; letter-spacing: .05em; text-transform: uppercase; color: var(--ss-w-faint); line-height: 1.3; }
667
+ .ss-w-bi-sel { font-weight: 660; }
668
+ .ss-w-bi-odds { margin-left: auto; display: flex; align-items: center; gap: 6px; flex: none; }
669
+ .ss-w-bi-odds b { font-size: 15px; font-variant-numeric: tabular-nums; color: var(--ss-w-accent); }
670
+ .ss-w-bi-book { font-size: 10px; color: var(--ss-w-faint); }
671
+ .ss-w-bi-live { font-size: 8.5px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase; color: #fff; background: #c0392b; border-radius: 4px; padding: 1px 5px; }
672
+ .ss-w-bi-text { font-size: 12px; color: var(--ss-w-ink); }
673
+ .ss-w-bi-stats { display: flex; flex-wrap: wrap; gap: 12px; }
674
+ .ss-w-bi-stat b { display: block; font-variant-numeric: tabular-nums; font-weight: 660; }
675
+ .ss-w-bi-stat span { font-size: 9px; text-transform: uppercase; color: var(--ss-w-faint); }
676
+ .ss-w-bi-disc { font-size: 10px; font-style: italic; color: var(--ss-w-faint); }
677
+
611
678
  /* action buttons */
612
679
  .ss-w-actions { display: flex; flex-direction: column; align-items: flex-start; gap: 6px; }
613
680
  .ss-w-abtn { font-size: 12px; font-weight: 560; color: var(--ss-w-accent); background: var(--ss-w-panel); border: 1px solid var(--ss-w-accent); border-radius: 16px; padding: 6px 12px; cursor: pointer; }
@@ -637,7 +704,7 @@ var WIDGET_CSS = `.ss-w-root { all: revert; }
637
704
  `;
638
705
 
639
706
  // src/StatsWidget.tsx
640
- import { jsx as jsx13 } from "react/jsx-runtime";
707
+ import { jsx as jsx14 } from "react/jsx-runtime";
641
708
  function buildClient(props) {
642
709
  try {
643
710
  if ("client" in props && props.client) {
@@ -673,7 +740,7 @@ function StatsWidget(props) {
673
740
  }, [built, ui.injectStyles]);
674
741
  if (!built) return null;
675
742
  const attrs = themeAttrs(ui);
676
- return /* @__PURE__ */ jsx13("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__ */ jsx13(ChatPanel, { controller, ui }) : /* @__PURE__ */ jsx13(
743
+ return /* @__PURE__ */ jsx14("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__ */ jsx14(ChatPanel, { controller, ui }) : /* @__PURE__ */ jsx14(
677
744
  ChatFab,
678
745
  {
679
746
  label: ui.launcherLabel,