@sensiblestats/widget-react 0.54.0 → 0.55.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/CHANGELOG.md CHANGED
@@ -1,5 +1,16 @@
1
1
  # @sensiblestats/widget-react
2
2
 
3
+ ## 0.55.0
4
+
5
+ ### Minor Changes
6
+
7
+ - ede9739: Fix the daily games resetting on the second guess, and tighten three screens to the design artifact:
8
+
9
+ - Guard the play lifecycle against a state-losing submit response: an in-progress `postPlay` that returns no `clientStateJson` (e.g. a server-side concurrency retry) no longer wipes the board — the widget reconciles from `getGameResult` instead of rendering an empty state. (The root-cause fix is server-side; this is defence-in-depth so the board can never blank mid-game.)
10
+ - Guess the XI pitch now reads as a football field — fixed grass-green with visible yard-lines and a halfway line, and smaller flag dots — instead of a flat dark box.
11
+ - Mystery Tiger's first load shows one lead line above the search (no empty attribute tiles), matching the artifact; the shirt tile is labelled "Shirt" not "Kit".
12
+ - Beat the Grid filled cells now name the placed player (compact "F. Last") above the rarity score, so the fan can see who they put where.
13
+
3
14
  ## 0.54.0
4
15
 
5
16
  ### Minor Changes
package/dist/index.cjs CHANGED
@@ -4189,6 +4189,24 @@ function useGamePlay(conversation, { gameId, gameDay, fixtureId, refresh }) {
4189
4189
  convRef.current = conversation;
4190
4190
  const optsRef = (0, import_react28.useRef)({ gameId, gameDay, fixtureId, refresh });
4191
4191
  optsRef.current = { gameId, gameDay, fixtureId, refresh };
4192
+ const resultRef = (0, import_react28.useRef)(null);
4193
+ resultRef.current = result;
4194
+ const refetch = (0, import_react28.useCallback)(async () => {
4195
+ const conv = convRef.current;
4196
+ if (!conv) return;
4197
+ const opts = optsRef.current;
4198
+ try {
4199
+ const payload = await conv.getGameResult(opts.gameId, opts.fixtureId, opts.gameDay);
4200
+ if (payload === null) return;
4201
+ if (payload.clientStateJson == null && resultRef.current?.clientStateJson != null) {
4202
+ setRawStatus(payload.status);
4203
+ return;
4204
+ }
4205
+ setResult(toSubmitResult(payload));
4206
+ setRawStatus(payload.status);
4207
+ } catch {
4208
+ }
4209
+ }, []);
4192
4210
  const submit = (0, import_react28.useCallback)(async (answerJson) => {
4193
4211
  const conv = convRef.current;
4194
4212
  if (!conv || inFlightRef.current) return;
@@ -4198,7 +4216,14 @@ function useGamePlay(conversation, { gameId, gameDay, fixtureId, refresh }) {
4198
4216
  const opts = optsRef.current;
4199
4217
  const playResult = await conv.postPlay(opts.gameId, answerJson, opts.gameDay, opts.fixtureId);
4200
4218
  if (playResult !== null) {
4201
- setResult(playResult);
4219
+ const hasFreshState = playResult.clientStateJson != null;
4220
+ const isTerminal2 = playResult.alreadyPlayed === true;
4221
+ const holdsState = resultRef.current?.clientStateJson != null;
4222
+ if (!hasFreshState && !isTerminal2 && holdsState) {
4223
+ await refetch();
4224
+ } else {
4225
+ setResult(playResult);
4226
+ }
4202
4227
  if (playResult.pointsDelta != null) {
4203
4228
  opts.refresh?.();
4204
4229
  }
@@ -4208,22 +4233,10 @@ function useGamePlay(conversation, { gameId, gameDay, fixtureId, refresh }) {
4208
4233
  inFlightRef.current = false;
4209
4234
  setSubmitting(false);
4210
4235
  }
4211
- }, []);
4236
+ }, [refetch]);
4212
4237
  const hydrate = (0, import_react28.useCallback)(() => {
4213
- const conv = convRef.current;
4214
- if (!conv) return;
4215
- const opts = optsRef.current;
4216
- void (async () => {
4217
- try {
4218
- const payload = await conv.getGameResult(opts.gameId, opts.fixtureId, opts.gameDay);
4219
- if (payload !== null) {
4220
- setResult(toSubmitResult(payload));
4221
- setRawStatus(payload.status);
4222
- }
4223
- } catch {
4224
- }
4225
- })();
4226
- }, []);
4238
+ void refetch();
4239
+ }, [refetch]);
4227
4240
  return { result, submitting, submit, hydrate, rawStatus };
4228
4241
  }
4229
4242
 
@@ -4325,7 +4338,7 @@ var ATTR_LABELS = {
4325
4338
  position: "Pos",
4326
4339
  nationality: "Nat",
4327
4340
  age: "Age",
4328
- shirtNumber: "Kit",
4341
+ shirtNumber: "Shirt",
4329
4342
  apps: "Apps"
4330
4343
  };
4331
4344
  var ATTR_KEYS = [
@@ -4386,13 +4399,7 @@ function MysteryTigerGame({
4386
4399
  " left"
4387
4400
  ] })
4388
4401
  ] }),
4389
- guessResults.length === 0 && !isOver && /* @__PURE__ */ (0, import_jsx_runtime55.jsxs)("div", { className: "ss-w-gt-legend", children: [
4390
- /* @__PURE__ */ (0, import_jsx_runtime55.jsx)("div", { className: "ss-w-gt-legend-lead", children: "Guess any squad player \u2014 each guess reveals how close you are:" }),
4391
- /* @__PURE__ */ (0, import_jsx_runtime55.jsx)("div", { className: "ss-w-gtrow", children: ATTR_KEYS.map((key) => /* @__PURE__ */ (0, import_jsx_runtime55.jsxs)("div", { className: "ss-w-gtile ss-w-gtile--legend", children: [
4392
- /* @__PURE__ */ (0, import_jsx_runtime55.jsx)("span", { className: "ss-w-gtile-lab", children: ATTR_LABELS[key] }),
4393
- /* @__PURE__ */ (0, import_jsx_runtime55.jsx)("span", { className: "ss-w-gtile-v", "aria-hidden": "true", children: "\xB7" })
4394
- ] }, key)) })
4395
- ] }),
4402
+ guessResults.length === 0 && !isOver && /* @__PURE__ */ (0, import_jsx_runtime55.jsx)("div", { className: "ss-w-gt-lead", children: "Guess any squad player \u2014 each guess reveals how close you are on position, nationality, age, shirt and apps." }),
4396
4403
  guessResults.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime55.jsx)("div", { className: "ss-w-gtrows", children: guessResults.map((gr, i) => {
4397
4404
  const player = config.squadPlayers.find((p) => p.id === gr.playerId);
4398
4405
  return /* @__PURE__ */ (0, import_jsx_runtime55.jsxs)("div", { className: "ss-w-gtguess", children: [
@@ -4726,6 +4733,15 @@ function parseClientState4(json) {
4726
4733
  function findCellResult(cells, row, col) {
4727
4734
  return cells.find((c) => c.row === row && c.col === col);
4728
4735
  }
4736
+ function shortName(name) {
4737
+ if (!name) return "";
4738
+ const parts = name.trim().split(/\s+/).filter(Boolean);
4739
+ if (parts.length === 0) return "";
4740
+ const first = parts[0];
4741
+ if (parts.length === 1) return first;
4742
+ const last = parts[parts.length - 1];
4743
+ return `${first.charAt(0)}. ${last}`;
4744
+ }
4729
4745
  function SearchSheet({
4730
4746
  row,
4731
4747
  col,
@@ -4871,8 +4887,11 @@ function BeatTheGridGame({
4871
4887
  disabled: isLocked2 || submitting,
4872
4888
  onClick: () => handleCellClick(rowIdx, colIdx),
4873
4889
  children: filled ? /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)(import_jsx_runtime58.Fragment, { children: [
4874
- /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("span", { className: "ss-w-btg-cell-score", children: filled.rarityScore }),
4875
- filled.isCorrect && /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("span", { className: "ss-w-btg-cell-check", "aria-hidden": "true", children: "\u2713" })
4890
+ /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("span", { className: "ss-w-btg-cell-nm", children: shortName(playerPool.find((p) => p.id === filled.playerId)?.name) }),
4891
+ /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)("span", { className: "ss-w-btg-cell-score", children: [
4892
+ filled.rarityScore,
4893
+ filled.isCorrect && /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("span", { className: "ss-w-btg-cell-check", "aria-hidden": "true", children: " \u2713" })
4894
+ ] })
4876
4895
  ] }) : /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("span", { className: "ss-w-btg-cell-empty", "aria-hidden": "true", children: "+" })
4877
4896
  },
4878
4897
  `cell-${rowIdx}-${colIdx}`
@@ -6981,10 +7000,7 @@ var WIDGET_CSS = `.ss-w-root { all: revert; }
6981
7000
  /* Guess rows \u2014 one labelled block per guess */
6982
7001
  .ss-w-gtrows { display: flex; flex-direction: column; gap: 8px; }
6983
7002
  /* First-load legend (empty state) */
6984
- .ss-w-gt-legend { display: flex; flex-direction: column; gap: 8px; }
6985
- .ss-w-gt-legend-lead { font-size: 11.5px; color: var(--ss-w-faint); line-height: 1.35; }
6986
- .ss-w-gtile--legend { background: var(--ss-w-bubble); border: 1px dashed var(--ss-w-line); color: var(--ss-w-faint); }
6987
- .ss-w-gtile--legend .ss-w-gtile-lab { opacity: .9; }
7003
+ .ss-w-gt-lead { font-size: 11.5px; color: var(--ss-w-faint); line-height: 1.4; }
6988
7004
  .ss-w-gtguess { display: flex; flex-direction: column; gap: 4px; }
6989
7005
  .ss-w-gtguess-nm { font-size: 11px; font-weight: 600; color: var(--ss-w-faint); }
6990
7006
  .ss-w-gtrow { display: grid; grid-template-columns: repeat(5, 1fr); gap: 5px; }
@@ -7103,8 +7119,10 @@ var WIDGET_CSS = `.ss-w-root { all: revert; }
7103
7119
  .ss-w-btg-cell--correct { background: color-mix(in srgb, var(--ss-w-correct) 14%, var(--ss-w-panel)); border-color: color-mix(in srgb, var(--ss-w-correct) 35%, var(--ss-w-line)); }
7104
7120
  .ss-w-btg-cell--wrong { background: color-mix(in srgb, var(--ss-w-wrong) 10%, var(--ss-w-panel)); }
7105
7121
  .ss-w-btg-cell--active { border-color: var(--ss-w-accent); }
7106
- .ss-w-btg-cell-score { font-size: 14px; font-weight: 800; color: var(--ss-w-ink); font-variant-numeric: tabular-nums; }
7107
- .ss-w-btg-cell-check { font-size: 12px; color: var(--ss-w-correct); line-height: 1; }
7122
+ .ss-w-btg-cell-nm { font-size: 8.5px; font-weight: 600; line-height: 1.05; color: var(--ss-w-ink); text-align: center; max-width: 100%; padding: 0 2px; overflow: hidden; text-overflow: ellipsis; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; }
7123
+ .ss-w-btg-cell-score { font-size: 12px; font-weight: 800; color: var(--ss-w-accent); font-variant-numeric: tabular-nums; }
7124
+ .ss-w-btg-cell--correct .ss-w-btg-cell-score { color: var(--ss-w-correct); }
7125
+ .ss-w-btg-cell-check { color: var(--ss-w-correct); line-height: 1; }
7108
7126
  .ss-w-btg-cell-empty { font-size: 16px; color: var(--ss-w-faint); }
7109
7127
  /* Scoped search sheet: slides up over the grid */
7110
7128
  .ss-w-btg-sheet { display: flex; flex-direction: column; gap: 10px; padding: 12px; background: var(--ss-w-panel); border: 1.5px solid var(--ss-w-line); border-radius: 14px; box-shadow: 0 4px 20px rgba(0,0,0,.1); }
@@ -7126,12 +7144,16 @@ var WIDGET_CSS = `.ss-w-root { all: revert; }
7126
7144
  /* Pitch: a flag per slot laid out in formation rows on a green field. A real min-height plus
7127
7145
  space-between distributes the formation lines top\u2192bottom like a pitch (GK at the back, forwards
7128
7146
  forward); yard-line stripes reinforce the read. Without a height the lines collapse together. */
7129
- .ss-w-gxi-pitch { display: flex; flex-direction: column; justify-content: space-between; gap: 8px; min-height: 300px; padding: 18px 10px; border-radius: 14px; border: 1px solid color-mix(in srgb, var(--ss-w-correct) 22%, var(--ss-w-line));
7147
+ /* A football pitch is grass-green regardless of the operator accent, so the field uses fixed
7148
+ green values (not --ss-w-correct, which is a muted per-operator token that vanished on dark
7149
+ themes) plus visible horizontal yard-lines and a brighter halfway line. */
7150
+ .ss-w-gxi-pitch { position: relative; display: flex; flex-direction: column; justify-content: space-between; gap: 8px; min-height: 300px; padding: 18px 10px; border-radius: 14px; border: 1px solid rgba(46, 160, 86, .38);
7130
7151
  background:
7131
- linear-gradient(180deg, color-mix(in srgb, var(--ss-w-correct) 12%, var(--ss-w-panel)), color-mix(in srgb, var(--ss-w-correct) 4%, var(--ss-w-panel))),
7132
- repeating-linear-gradient(180deg, transparent 0 40px, color-mix(in srgb, var(--ss-w-ink) 4%, transparent) 40px 80px); }
7133
- .ss-w-gxi-prow { display: flex; justify-content: center; align-items: center; gap: 14px; }
7134
- .ss-w-gxi-flag { width: 36px; height: 36px; flex: none; border-radius: 50%; display: grid; place-items: center; font-size: 20px; line-height: 1; background: var(--ss-w-panel); border: 1px solid var(--ss-w-line); box-shadow: 0 1px 3px rgba(0,0,0,.14); overflow: hidden; }
7152
+ linear-gradient(180deg, rgba(46, 160, 86, .015) 0, rgba(46, 160, 86, .015) 49.5%, rgba(255,255,255,.14) 49.5%, rgba(255,255,255,.14) 50.5%, rgba(46, 160, 86, .015) 50.5%),
7153
+ repeating-linear-gradient(180deg, transparent 0 31px, rgba(255,255,255,.045) 31px 32px),
7154
+ linear-gradient(180deg, rgba(46, 160, 86, .28), rgba(38, 132, 71, .16)); }
7155
+ .ss-w-gxi-prow { display: flex; justify-content: center; align-items: center; gap: 12px; }
7156
+ .ss-w-gxi-flag { width: 32px; height: 32px; flex: none; border-radius: 50%; display: grid; place-items: center; font-size: 17px; line-height: 1; background: rgba(9, 12, 10, .55); border: 1px solid rgba(255,255,255,.16); box-shadow: 0 1px 3px rgba(0,0,0,.3); overflow: hidden; }
7135
7157
  /* Wrong-guess trail */
7136
7158
  .ss-w-gxi-wrong { display: flex; flex-wrap: wrap; gap: 6px; }
7137
7159
  .ss-w-gxi-wrong-pill { font-size: 11px; font-weight: 600; padding: 3px 9px; border-radius: 999px; background: color-mix(in srgb, var(--ss-w-wrong) 10%, var(--ss-w-panel)); border: 1px solid var(--ss-w-line); color: var(--ss-w-faint); text-decoration: line-through; }