@open-slot-ui/core 0.5.0 → 0.6.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.cjs CHANGED
@@ -1249,6 +1249,9 @@ function validateSpec(spec) {
1249
1249
  if (spec.realityCheck != null && !(typeof spec.realityCheck.everyMinutes === "number" && spec.realityCheck.everyMinutes > 0)) {
1250
1250
  add("error", "realityCheck.everyMinutes", "bad-interval", "realityCheck.everyMinutes must be a number > 0");
1251
1251
  }
1252
+ if (spec.buyFeature?.confirmAboveCost != null && !(typeof spec.buyFeature.confirmAboveCost === "number" && Number.isFinite(spec.buyFeature.confirmAboveCost) && spec.buyFeature.confirmAboveCost >= 0)) {
1253
+ add("error", "buyFeature.confirmAboveCost", "bad-threshold", "buyFeature.confirmAboveCost must be a finite number >= 0");
1254
+ }
1252
1255
  if (spec.controls) {
1253
1256
  for (const [id, ov] of Object.entries(spec.controls)) {
1254
1257
  checkLayout(ov.layout?.anchor, `controls.${id}.layout.anchor`);
@@ -1401,6 +1404,9 @@ function applyJurisdiction(ui, jur) {
1401
1404
  if (typeof jur.minimumRoundDuration === "number" && Number.isFinite(jur.minimumRoundDuration)) {
1402
1405
  ui.minimumRoundDuration = Math.max(0, jur.minimumRoundDuration);
1403
1406
  }
1407
+ if (typeof jur.confirmBuyAboveCost === "number" && Number.isFinite(jur.confirmBuyAboveCost)) {
1408
+ ui.confirmBuyAboveCost = Math.max(0, jur.confirmBuyAboveCost);
1409
+ }
1404
1410
  }
1405
1411
 
1406
1412
  // src/notice.ts
@@ -1458,6 +1464,8 @@ var openuiDefaults = {
1458
1464
  "openui.replay": "Replay",
1459
1465
  "openui.buyFeature.title": "Buy feature",
1460
1466
  "openui.buyFeature.message": "Buy this feature now?",
1467
+ // Confirm step for a higher-cost play — {{name}} / {{price}} are interpolated.
1468
+ "openui.buyFeature.confirm": "Buy {{name}} for {{price}}?",
1461
1469
  "openui.freeSpins": "FS",
1462
1470
  // reality check (RTS 13) — {{minutes}} is interpolated by open-ui's scheduler
1463
1471
  "openui.realityCheck.title": "Reality check",
@@ -1493,6 +1501,7 @@ var openuiSocialDefaults = {
1493
1501
  "openui.err.insufficient.title": "Insufficient balance",
1494
1502
  "openui.buyFeature.title": "Play bonus",
1495
1503
  "openui.buyFeature.message": "Play this bonus now?",
1504
+ "openui.buyFeature.confirm": "Play {{name}} for {{price}}?",
1496
1505
  "openui.err.insufficient.message": "You don't have enough balance for this play.",
1497
1506
  "openui.err.limit.message": "A play limit has been reached. Please try again later."
1498
1507
  };
@@ -1673,6 +1682,11 @@ var OpenUI = class {
1673
1682
  gameInfo = {};
1674
1683
  /** Minimum round duration (ms) from jurisdiction — stored for the GAME to enforce. */
1675
1684
  minimumRoundDuration = 0;
1685
+ /** Buy-feature confirm threshold, as a multiple of the base bet. A play (buy or
1686
+ * bet-mode activation) that costs STRICTLY MORE than this must show a confirm step
1687
+ * — Stake requires that bet modes above 2× never activate on a single click. `0`
1688
+ * (the default) = off. Set via `spec.buyFeature.confirmAboveCost` or a jurisdiction. */
1689
+ confirmBuyAboveCost = 0;
1676
1690
  sessionNet = 0;
1677
1691
  prevVolumes = null;
1678
1692
  /** The frozen UISpec `createUI` built this from, if any — authored = run = tested. */
@@ -2022,6 +2036,13 @@ var OpenUI = class {
2022
2036
  const j = this.jurisdiction;
2023
2037
  return feature === "autoplay" && !!j.disabledAutoplay || feature === "buyFeature" && !!j.disabledBuyFeature || feature === "turbo" && !!j.disabledTurbo || feature === "fullscreen" && !!j.disabledFullscreen || feature === "slamstop" && !!j.disabledSlamstop || feature === "spacebar" && !!j.disabledSpacebar;
2024
2038
  }
2039
+ /** True when a buy / bet-mode activation of `cost`× the base bet must be confirmed
2040
+ * before it commits (Stake: no one-click activation above `confirmBuyAboveCost`×).
2041
+ * Always false when the threshold is 0/unset or `cost` isn't a finite number. */
2042
+ shouldConfirmBuy(cost) {
2043
+ const t = this.confirmBuyAboveCost;
2044
+ return t > 0 && Number.isFinite(cost) && cost > t;
2045
+ }
2025
2046
  /**
2026
2047
  * Start a reality-check reminder (RTS 13). Every `everyMinutes` (WALL-CLOCK, so a
2027
2048
  * backgrounded tab can't cheat it) it emits a `realityCheck` event, stops autoplay,
@@ -2253,15 +2274,18 @@ function collectSocialCopy(spec) {
2253
2274
  if (spec.game?.name) out.push({ source: "game.name", text: spec.game.name });
2254
2275
  const en = spec.locale?.messages?.en;
2255
2276
  if (en) {
2256
- for (const [k, v] of Object.entries(en)) if (typeof v === "string") out.push({ source: `locale.en[${JSON.stringify(k)}]`, text: v });
2277
+ for (const [k, v] of Object.entries(en)) if (typeof v === "string") out.push({ source: `locale.en[${JSON.stringify(k)}]`, text: v, key: k });
2257
2278
  }
2258
2279
  return out;
2259
2280
  }
2260
2281
  function checkSocialPhrases(spec) {
2282
+ const socialEn = spec.locale?.socialMessages?.en ?? {};
2283
+ const render = (e) => socialEn[e.key ?? e.text] ?? openuiSocialDefaults[e.key ?? e.text] ?? e.text;
2261
2284
  const issues = [];
2262
- for (const { source, text } of collectSocialCopy(spec)) {
2285
+ for (const e of collectSocialCopy(spec)) {
2286
+ const text = render(e);
2263
2287
  const matches = findForbiddenPhrases(text);
2264
- if (matches.length) issues.push({ source, text, matches });
2288
+ if (matches.length) issues.push({ source: e.source, text, matches });
2265
2289
  }
2266
2290
  return issues;
2267
2291
  }
@@ -2352,6 +2376,9 @@ function createUI(spec = {}, hooks = {}) {
2352
2376
  }
2353
2377
  }
2354
2378
  }
2379
+ if (spec.buyFeature?.confirmAboveCost != null && Number.isFinite(spec.buyFeature.confirmAboveCost)) {
2380
+ ui.confirmBuyAboveCost = Math.max(0, spec.buyFeature.confirmAboveCost);
2381
+ }
2355
2382
  if (spec.jurisdiction) ui.applyJurisdiction(spec.jurisdiction);
2356
2383
  const portraitControls = {};
2357
2384
  for (const [id, layout] of Object.entries(portraitDefaultLayouts)) {