@open-slot-ui/core 0.1.0 → 0.2.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
@@ -1172,9 +1172,6 @@ function validateSpec(spec) {
1172
1172
  if (spec.rtp != null && !(typeof spec.rtp === "number" && Number.isFinite(spec.rtp))) {
1173
1173
  add("error", "rtp", "bad-rtp", `rtp must be a number, got ${String(spec.rtp)}`);
1174
1174
  }
1175
- if (spec.statusBar != null && spec.statusBar !== "top" && spec.statusBar !== "bottom") {
1176
- add("error", "statusBar", "bad-statusbar", `statusBar must be 'top' or 'bottom', got "${String(spec.statusBar)}"`);
1177
- }
1178
1175
  if (spec.realityCheck != null && !(typeof spec.realityCheck.everyMinutes === "number" && spec.realityCheck.everyMinutes > 0)) {
1179
1176
  add("error", "realityCheck.everyMinutes", "bad-interval", "realityCheck.everyMinutes must be a number > 0");
1180
1177
  }
@@ -1314,7 +1311,6 @@ function applyJurisdiction(ui, jur) {
1314
1311
  lockHidden("bonus");
1315
1312
  ui.bonusButton.disable();
1316
1313
  }
1317
- if (jur.disabledSuperTurbo && ui.turbo.modeCount > 2) ui.turbo.setModes(["off", "on"]);
1318
1314
  if (jur.disabledSlamstop) ui.spin.allowSlamStop.set(false);
1319
1315
  if (jur.disabledSpacebar) {
1320
1316
  ui.spin.holdToSpin = false;
@@ -1388,13 +1384,6 @@ var openuiDefaults = {
1388
1384
  "openui.buyFeature.title": "Buy feature",
1389
1385
  "openui.buyFeature.message": "Buy this feature now?",
1390
1386
  "openui.freeSpins": "FS",
1391
- // ── social / sweepstakes wording (used when `ui.social` is on; see OpenUI.t) ──
1392
- // open-ui resolves `<key>.social` first in social mode, falling back to the base
1393
- // key. These are the gambling-loaded terms a sweepstakes jurisdiction must avoid;
1394
- // override any of them (or add `openui.bet.social` etc.) in your messages dict.
1395
- "openui.buyFeature.title.social": "Play bonus",
1396
- "openui.buyFeature.message.social": "Play this bonus now?",
1397
- "openui.win.social": "Prize",
1398
1387
  // reality check (RTS 13) — {{minutes}} is interpolated by open-ui's scheduler
1399
1388
  "openui.realityCheck.title": "Reality check",
1400
1389
  "openui.realityCheck.message": "You've been playing for {{minutes}} minutes. Take a moment before continuing.",
@@ -1416,17 +1405,27 @@ var openuiDefaults = {
1416
1405
  "openui.err.connection.title": "Connection lost",
1417
1406
  "openui.err.connection.message": "A stable connection is required. Reload to finish any open round."
1418
1407
  };
1408
+ var openuiSocialDefaults = {
1409
+ "openui.bet": "Play",
1410
+ "openui.balance": "Credits",
1411
+ "openui.win": "Prize",
1412
+ "openui.buyFeature.title": "Play bonus",
1413
+ "openui.buyFeature.message": "Play this bonus now?"
1414
+ };
1419
1415
  function interpolate(template, vars) {
1420
1416
  if (!vars) return template;
1421
1417
  return template.replace(/\{\{(\w+)\}\}/g, (_m, k) => String(vars[k] ?? `{{${k}}}`));
1422
1418
  }
1423
1419
  var DictionaryTranslator = class {
1424
- constructor(messages, locale) {
1420
+ constructor(messages, locale, socialMessages = {}) {
1425
1421
  this.messages = messages;
1422
+ this.socialMessages = socialMessages;
1426
1423
  this._locale = locale;
1427
1424
  }
1428
1425
  messages;
1426
+ socialMessages;
1429
1427
  _locale;
1428
+ _social = false;
1430
1429
  subs = /* @__PURE__ */ new Set();
1431
1430
  get locale() {
1432
1431
  return this._locale;
@@ -1436,8 +1435,12 @@ var DictionaryTranslator = class {
1436
1435
  this._locale = next;
1437
1436
  for (const cb of [...this.subs]) cb(next);
1438
1437
  }
1438
+ setSocial(on) {
1439
+ this._social = on;
1440
+ }
1439
1441
  t(key, vars) {
1440
- const resolved = this.messages[this._locale]?.[key] ?? openuiDefaults[key] ?? key;
1442
+ const social = this._social ? this.socialMessages[this._locale]?.[key] ?? openuiSocialDefaults[key] : void 0;
1443
+ const resolved = social ?? this.messages[this._locale]?.[key] ?? openuiDefaults[key] ?? key;
1441
1444
  return interpolate(resolved, vars);
1442
1445
  }
1443
1446
  onChange(cb) {
@@ -1447,8 +1450,8 @@ var DictionaryTranslator = class {
1447
1450
  };
1448
1451
  }
1449
1452
  };
1450
- function dictionary(messages, locale) {
1451
- return new DictionaryTranslator(messages, locale);
1453
+ function dictionary(messages, locale, socialMessages) {
1454
+ return new DictionaryTranslator(messages, locale, socialMessages);
1452
1455
  }
1453
1456
 
1454
1457
  // src/format/currency.ts
@@ -1554,9 +1557,6 @@ var OpenUI = class {
1554
1557
  settingsPanel;
1555
1558
  musicSlider;
1556
1559
  sfxSlider;
1557
- rulesButton;
1558
- infoPanel;
1559
- infoClose;
1560
1560
  turbo;
1561
1561
  autoplay;
1562
1562
  bonusButton;
@@ -1611,9 +1611,6 @@ var OpenUI = class {
1611
1611
  );
1612
1612
  this.musicSlider = new SliderControl({ id: "music", label: "Music", layout: { anchor: "center" }, initial: 0.7 }, this.bus);
1613
1613
  this.sfxSlider = new SliderControl({ id: "sfx", label: "Sound", layout: { anchor: "center" }, initial: 0.5 }, this.bus);
1614
- this.rulesButton = new ButtonControl({ id: "rules", label: "Rules", layout: { anchor: "center" } }, this.bus);
1615
- this.infoPanel = new PanelControl({ id: "info-panel", variant: "modal", title: "Game Info", layout: { anchor: "center" } }, this.bus);
1616
- this.infoClose = new ButtonControl({ id: "info-close", layout: { anchor: "top-right" } }, this.bus);
1617
1614
  this.bonusButton = new ButtonControl({ id: "bonus", layout: { anchor: "bottom-center", offset: [-400, -440] } }, this.bus);
1618
1615
  this.autoplay = new AutoplayControl(
1619
1616
  { id: "autoplay", layout: { anchor: "bottom-center", offset: [-225, -440] }, options: [5, 10, 25, 50, 100, Infinity] },
@@ -1637,9 +1634,6 @@ var OpenUI = class {
1637
1634
  this.settingsPanel,
1638
1635
  this.musicSlider,
1639
1636
  this.sfxSlider,
1640
- this.rulesButton,
1641
- this.infoPanel,
1642
- this.infoClose,
1643
1637
  this.bonusButton,
1644
1638
  this.autoplay,
1645
1639
  this.turbo,
@@ -1660,12 +1654,7 @@ var OpenUI = class {
1660
1654
  this.hidden.add("session-timer");
1661
1655
  this.bus.on("buttonActivated", ({ id }) => {
1662
1656
  if (id === "settings") this.settingsPanel.toggle();
1663
- else if (id === "rules") {
1664
- this.settingsPanel.closePanel();
1665
- this.infoPanel.openPanel();
1666
- } else if (id === "info-close") {
1667
- this.infoPanel.closePanel();
1668
- } else if (id === "bet-plus") {
1657
+ else if (id === "bet-plus") {
1669
1658
  this.betStepper.inc();
1670
1659
  } else if (id === "bet-minus") {
1671
1660
  this.betStepper.dec();
@@ -1860,26 +1849,23 @@ var OpenUI = class {
1860
1849
  setLocale(next) {
1861
1850
  this.translator.setLocale(next);
1862
1851
  }
1863
- /** Translate a key (or pass plain text through) via the active translator. In
1864
- * social mode, a `<key>.social` variant wins when one resolves so gambling
1865
- * wording (Bet/Buy feature) swaps to sweepstakes terms automatically. */
1852
+ /** Translate a key (or pass plain text through) via the active translator. Social
1853
+ * wording is resolved by the translator from a SEPARATE social dictionary (see
1854
+ * `setSocial`), so gambling and social copy can never be mixed by accident. */
1866
1855
  t(key, vars) {
1867
- if (this.social.get()) {
1868
- const sk = `${key}.social`;
1869
- const s = this.translator.t(sk, vars);
1870
- if (s !== sk) return s;
1871
- }
1872
1856
  return this.translator.t(key, vars);
1873
1857
  }
1874
1858
  /**
1875
- * Turn social / sweepstakes mode on or off (one switch). Swaps gambling wording
1876
- * (Bet/Buy feature social terms via `<key>.social` i18n) AND, when a `coin` is
1877
- * given (e.g. `'GC'`/`'SC'`/`'XGC'`), shows balance/bet/net in that coin. Wording
1878
- * re-renders live; the host can still override any `.social` key.
1859
+ * Turn social / sweepstakes mode on or off (one switch). Flips the translator to
1860
+ * its SEPARATE social dictionary (Bet→Play, Buy feature→Play bonus, etc.) and,
1861
+ * when a `coin` is given (e.g. `'GC'`/`'SC'`), shows balance/bet/net in that coin.
1862
+ * Wording re-renders live. Social copy lives in `locale.socialMessages` kept
1863
+ * apart from your normal `locale.messages` by design.
1879
1864
  */
1880
1865
  setSocial(on, coin) {
1881
1866
  if (on !== this.social.get()) {
1882
1867
  this.social.set(on);
1868
+ this.translator.setSocial?.(on);
1883
1869
  this.locale.update(() => {
1884
1870
  });
1885
1871
  }
@@ -1894,7 +1880,7 @@ var OpenUI = class {
1894
1880
  * just a hide — `confirmBuy`/autoplay entry points consult this). */
1895
1881
  isDisabled(feature) {
1896
1882
  const j = this.jurisdiction;
1897
- return feature === "autoplay" && !!j.disabledAutoplay || feature === "buyFeature" && !!j.disabledBuyFeature || feature === "turbo" && !!j.disabledTurbo || feature === "superTurbo" && !!j.disabledSuperTurbo || feature === "fullscreen" && !!j.disabledFullscreen || feature === "slamstop" && !!j.disabledSlamstop || feature === "spacebar" && !!j.disabledSpacebar;
1883
+ 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;
1898
1884
  }
1899
1885
  /**
1900
1886
  * Start a reality-check reminder (RTS 13). Every `everyMinutes` (WALL-CLOCK, so a
@@ -1938,7 +1924,7 @@ var OpenUI = class {
1938
1924
  this.showNotice(
1939
1925
  [
1940
1926
  { kind: "heading", id: "rc-title", text: this.t(opts.title ?? "openui.realityCheck.title", vars) },
1941
- { kind: "callout", id: "rc-body", tone: "info", text: this.t(opts.message ?? "openui.realityCheck.message", vars) }
1927
+ { kind: "text", id: "rc-body", text: this.t(opts.message ?? "openui.realityCheck.message", vars) }
1942
1928
  ],
1943
1929
  opts.actions ?? [{ label: "openui.continue", variant: "primary" }]
1944
1930
  );
@@ -2027,7 +2013,7 @@ var ANALYTICS_EVENTS = [
2027
2013
  function createUI(spec = {}, hooks = {}) {
2028
2014
  const { issues } = validateSpec(spec);
2029
2015
  for (const issue of issues) hooks.onDataIssue?.(issue);
2030
- const translator = spec.locale ? new DictionaryTranslator(spec.locale.messages, spec.locale.locale) : void 0;
2016
+ const translator = spec.locale ? new DictionaryTranslator(spec.locale.messages, spec.locale.locale, spec.locale.socialMessages) : void 0;
2031
2017
  const theme = resolveTheme(spec.theme, (i) => hooks.onDataIssue?.(i));
2032
2018
  const ui = new OpenUI({ theme, layout: spec.layout, translator, startMuted: spec.audio?.startMuted });
2033
2019
  if (spec.currency) {
@@ -2257,20 +2243,6 @@ function clampBet(amount, cfg, divisor = API_AMOUNT_DIVISOR) {
2257
2243
  return Math.min(max, Math.max(min, v));
2258
2244
  }
2259
2245
 
2260
- // src/win.ts
2261
- var DEFAULT_WIN_TIERS = Object.freeze([
2262
- { name: "big", minMultiplier: 10 },
2263
- { name: "mega", minMultiplier: 50 },
2264
- { name: "epic", minMultiplier: 100 }
2265
- ]);
2266
- function winTier(win, stake, tiers = DEFAULT_WIN_TIERS) {
2267
- if (!Number.isFinite(win) || !Number.isFinite(stake) || stake <= 0 || win <= stake) return "none";
2268
- const mult = win / stake;
2269
- let name = "win";
2270
- for (const t of tiers) if (mult >= t.minMultiplier) name = t.name;
2271
- return name;
2272
- }
2273
-
2274
2246
  exports.API_AMOUNT_DIVISOR = API_AMOUNT_DIVISOR;
2275
2247
  exports.AutoplayControl = AutoplayControl;
2276
2248
  exports.BLOCK_KINDS = BLOCK_KINDS;
@@ -2281,7 +2253,6 @@ exports.Control = Control;
2281
2253
  exports.ControlRegistry = ControlRegistry;
2282
2254
  exports.DEFAULT_MENU_TITLES = DEFAULT_MENU_TITLES;
2283
2255
  exports.DEFAULT_NOTICE_ACTION = DEFAULT_NOTICE_ACTION;
2284
- exports.DEFAULT_WIN_TIERS = DEFAULT_WIN_TIERS;
2285
2256
  exports.DictionaryTranslator = DictionaryTranslator;
2286
2257
  exports.EventBus = EventBus;
2287
2258
  exports.EventLog = EventLog;
@@ -2332,6 +2303,7 @@ exports.integerDigits = integerDigits;
2332
2303
  exports.isSafeColor = isSafeColor;
2333
2304
  exports.isSocialCurrency = isSocialCurrency;
2334
2305
  exports.openuiDefaults = openuiDefaults;
2306
+ exports.openuiSocialDefaults = openuiSocialDefaults;
2335
2307
  exports.resolveCurrency = resolveCurrency;
2336
2308
  exports.resolvePlacement = resolvePlacement;
2337
2309
  exports.resolveTheme = resolveTheme;
@@ -2341,6 +2313,5 @@ exports.sanitizeThemeOverrides = sanitizeThemeOverrides;
2341
2313
  exports.themePresets = themePresets;
2342
2314
  exports.validateSpec = validateSpec;
2343
2315
  exports.valueFitMaxWidth = valueFitMaxWidth;
2344
- exports.winTier = winTier;
2345
2316
  //# sourceMappingURL=index.cjs.map
2346
2317
  //# sourceMappingURL=index.cjs.map