@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 +30 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +36 -1
- package/dist/index.d.ts +36 -1
- package/dist/index.js +30 -3
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -1255,6 +1255,11 @@ declare class OpenUI {
|
|
|
1255
1255
|
};
|
|
1256
1256
|
/** Minimum round duration (ms) from jurisdiction — stored for the GAME to enforce. */
|
|
1257
1257
|
minimumRoundDuration: number;
|
|
1258
|
+
/** Buy-feature confirm threshold, as a multiple of the base bet. A play (buy or
|
|
1259
|
+
* bet-mode activation) that costs STRICTLY MORE than this must show a confirm step
|
|
1260
|
+
* — Stake requires that bet modes above 2× never activate on a single click. `0`
|
|
1261
|
+
* (the default) = off. Set via `spec.buyFeature.confirmAboveCost` or a jurisdiction. */
|
|
1262
|
+
confirmBuyAboveCost: number;
|
|
1258
1263
|
private sessionNet;
|
|
1259
1264
|
private prevVolumes;
|
|
1260
1265
|
/** The frozen UISpec `createUI` built this from, if any — authored = run = tested. */
|
|
@@ -1363,6 +1368,10 @@ declare class OpenUI {
|
|
|
1363
1368
|
/** Read-back: did the applied jurisdiction disable this feature? (real guard, not
|
|
1364
1369
|
* just a hide — `confirmBuy`/autoplay entry points consult this). */
|
|
1365
1370
|
isDisabled(feature: 'autoplay' | 'buyFeature' | 'turbo' | 'fullscreen' | 'slamstop' | 'spacebar'): boolean;
|
|
1371
|
+
/** True when a buy / bet-mode activation of `cost`× the base bet must be confirmed
|
|
1372
|
+
* before it commits (Stake: no one-click activation above `confirmBuyAboveCost`×).
|
|
1373
|
+
* Always false when the threshold is 0/unset or `cost` isn't a finite number. */
|
|
1374
|
+
shouldConfirmBuy(cost: number): boolean;
|
|
1366
1375
|
/**
|
|
1367
1376
|
* Start a reality-check reminder (RTS 13). Every `everyMinutes` (WALL-CLOCK, so a
|
|
1368
1377
|
* backgrounded tab can't cheat it) it emits a `realityCheck` event, stops autoplay,
|
|
@@ -1403,6 +1412,11 @@ interface JurisdictionConfig {
|
|
|
1403
1412
|
displaySessionTimer?: boolean;
|
|
1404
1413
|
/** Minimum ms a round must take — stored on the HUD for the GAME to enforce. */
|
|
1405
1414
|
minimumRoundDuration?: number;
|
|
1415
|
+
/** Buy-feature confirm threshold (× base bet). A play costing more than this must
|
|
1416
|
+
* be confirmed before it commits — Stake requires that bet modes above 2× never
|
|
1417
|
+
* activate on a single click, so a Stake preset sets this to `2`. Not part of the
|
|
1418
|
+
* wallet schema; a convenience so a jurisdiction can require the confirm step. */
|
|
1419
|
+
confirmBuyAboveCost?: number;
|
|
1406
1420
|
}
|
|
1407
1421
|
/**
|
|
1408
1422
|
* Apply a Stake Engine `jurisdiction` config to a live HUD — the whole switchboard
|
|
@@ -1679,6 +1693,15 @@ interface UISpec {
|
|
|
1679
1693
|
name?: string;
|
|
1680
1694
|
version?: string;
|
|
1681
1695
|
};
|
|
1696
|
+
/** Buy-feature / bet-mode confirmation. `confirmAboveCost` (× base bet) makes any
|
|
1697
|
+
* play costing MORE than that require a confirm step before it commits — Stake
|
|
1698
|
+
* requires bet modes above 2× to never activate on a single click, so set `2`.
|
|
1699
|
+
* The game routes a purchase/activation through `hud.requestBuyFeature(...)`, which
|
|
1700
|
+
* shows the confirm popup itself when the cost exceeds the threshold. A jurisdiction
|
|
1701
|
+
* can set the same threshold (`JurisdictionConfig.confirmBuyAboveCost`). */
|
|
1702
|
+
buyFeature?: {
|
|
1703
|
+
confirmAboveCost?: number;
|
|
1704
|
+
};
|
|
1682
1705
|
/** Reality-check reminder (RTS 13): open-ui runs a wall-clock timer, emits a
|
|
1683
1706
|
* `realityCheck` event, stops autoplay, and (unless `showModal:false`) shows an
|
|
1684
1707
|
* acknowledge modal. You supply only the cadence + text. `title`/`message` are
|
|
@@ -1791,14 +1814,26 @@ interface SocialPhraseIssue {
|
|
|
1791
1814
|
}
|
|
1792
1815
|
/** Every restricted phrase in `text` (empty when clean). Pure + total. */
|
|
1793
1816
|
declare function findForbiddenPhrases(text: string): ForbiddenMatch[];
|
|
1817
|
+
/** A collected string. `text` is the base English (what renders with no social
|
|
1818
|
+
* override); `key` is the i18n key used to look up a social override — it defaults
|
|
1819
|
+
* to `text`, since a game's menu/rules copy doubles as its own key. */
|
|
1794
1820
|
type Entry = {
|
|
1795
1821
|
source: string;
|
|
1796
1822
|
text: string;
|
|
1823
|
+
key?: string;
|
|
1797
1824
|
};
|
|
1798
1825
|
/** Collect every English string a spec ships (menu/rules blocks, titles, labels+hints,
|
|
1799
1826
|
* game name, the `en` dictionary) with a source path — the input to {@link checkSocialPhrases}. */
|
|
1800
1827
|
declare function collectSocialCopy(spec: UISpec): Entry[];
|
|
1801
|
-
/**
|
|
1828
|
+
/**
|
|
1829
|
+
* Scan a spec's English copy AS IT RENDERS IN SOCIAL MODE and return every restricted
|
|
1830
|
+
* phrase. Each collected string is first resolved through the social dictionary
|
|
1831
|
+
* (`locale.socialMessages.en` → built-in `openuiSocialDefaults` → the base text) —
|
|
1832
|
+
* exactly like the translator does — so a game that already swaps "bet"/"pay(table)"
|
|
1833
|
+
* for compliant wording in social mode is NOT flagged, while a phrase with NO social
|
|
1834
|
+
* override (the classic slipped-through "paytable") IS. English by design: the check
|
|
1835
|
+
* targets the base `en` copy that every jurisdiction review starts from.
|
|
1836
|
+
*/
|
|
1802
1837
|
declare function checkSocialPhrases(spec: UISpec): SocialPhraseIssue[];
|
|
1803
1838
|
|
|
1804
1839
|
/**
|
package/dist/index.d.ts
CHANGED
|
@@ -1255,6 +1255,11 @@ declare class OpenUI {
|
|
|
1255
1255
|
};
|
|
1256
1256
|
/** Minimum round duration (ms) from jurisdiction — stored for the GAME to enforce. */
|
|
1257
1257
|
minimumRoundDuration: number;
|
|
1258
|
+
/** Buy-feature confirm threshold, as a multiple of the base bet. A play (buy or
|
|
1259
|
+
* bet-mode activation) that costs STRICTLY MORE than this must show a confirm step
|
|
1260
|
+
* — Stake requires that bet modes above 2× never activate on a single click. `0`
|
|
1261
|
+
* (the default) = off. Set via `spec.buyFeature.confirmAboveCost` or a jurisdiction. */
|
|
1262
|
+
confirmBuyAboveCost: number;
|
|
1258
1263
|
private sessionNet;
|
|
1259
1264
|
private prevVolumes;
|
|
1260
1265
|
/** The frozen UISpec `createUI` built this from, if any — authored = run = tested. */
|
|
@@ -1363,6 +1368,10 @@ declare class OpenUI {
|
|
|
1363
1368
|
/** Read-back: did the applied jurisdiction disable this feature? (real guard, not
|
|
1364
1369
|
* just a hide — `confirmBuy`/autoplay entry points consult this). */
|
|
1365
1370
|
isDisabled(feature: 'autoplay' | 'buyFeature' | 'turbo' | 'fullscreen' | 'slamstop' | 'spacebar'): boolean;
|
|
1371
|
+
/** True when a buy / bet-mode activation of `cost`× the base bet must be confirmed
|
|
1372
|
+
* before it commits (Stake: no one-click activation above `confirmBuyAboveCost`×).
|
|
1373
|
+
* Always false when the threshold is 0/unset or `cost` isn't a finite number. */
|
|
1374
|
+
shouldConfirmBuy(cost: number): boolean;
|
|
1366
1375
|
/**
|
|
1367
1376
|
* Start a reality-check reminder (RTS 13). Every `everyMinutes` (WALL-CLOCK, so a
|
|
1368
1377
|
* backgrounded tab can't cheat it) it emits a `realityCheck` event, stops autoplay,
|
|
@@ -1403,6 +1412,11 @@ interface JurisdictionConfig {
|
|
|
1403
1412
|
displaySessionTimer?: boolean;
|
|
1404
1413
|
/** Minimum ms a round must take — stored on the HUD for the GAME to enforce. */
|
|
1405
1414
|
minimumRoundDuration?: number;
|
|
1415
|
+
/** Buy-feature confirm threshold (× base bet). A play costing more than this must
|
|
1416
|
+
* be confirmed before it commits — Stake requires that bet modes above 2× never
|
|
1417
|
+
* activate on a single click, so a Stake preset sets this to `2`. Not part of the
|
|
1418
|
+
* wallet schema; a convenience so a jurisdiction can require the confirm step. */
|
|
1419
|
+
confirmBuyAboveCost?: number;
|
|
1406
1420
|
}
|
|
1407
1421
|
/**
|
|
1408
1422
|
* Apply a Stake Engine `jurisdiction` config to a live HUD — the whole switchboard
|
|
@@ -1679,6 +1693,15 @@ interface UISpec {
|
|
|
1679
1693
|
name?: string;
|
|
1680
1694
|
version?: string;
|
|
1681
1695
|
};
|
|
1696
|
+
/** Buy-feature / bet-mode confirmation. `confirmAboveCost` (× base bet) makes any
|
|
1697
|
+
* play costing MORE than that require a confirm step before it commits — Stake
|
|
1698
|
+
* requires bet modes above 2× to never activate on a single click, so set `2`.
|
|
1699
|
+
* The game routes a purchase/activation through `hud.requestBuyFeature(...)`, which
|
|
1700
|
+
* shows the confirm popup itself when the cost exceeds the threshold. A jurisdiction
|
|
1701
|
+
* can set the same threshold (`JurisdictionConfig.confirmBuyAboveCost`). */
|
|
1702
|
+
buyFeature?: {
|
|
1703
|
+
confirmAboveCost?: number;
|
|
1704
|
+
};
|
|
1682
1705
|
/** Reality-check reminder (RTS 13): open-ui runs a wall-clock timer, emits a
|
|
1683
1706
|
* `realityCheck` event, stops autoplay, and (unless `showModal:false`) shows an
|
|
1684
1707
|
* acknowledge modal. You supply only the cadence + text. `title`/`message` are
|
|
@@ -1791,14 +1814,26 @@ interface SocialPhraseIssue {
|
|
|
1791
1814
|
}
|
|
1792
1815
|
/** Every restricted phrase in `text` (empty when clean). Pure + total. */
|
|
1793
1816
|
declare function findForbiddenPhrases(text: string): ForbiddenMatch[];
|
|
1817
|
+
/** A collected string. `text` is the base English (what renders with no social
|
|
1818
|
+
* override); `key` is the i18n key used to look up a social override — it defaults
|
|
1819
|
+
* to `text`, since a game's menu/rules copy doubles as its own key. */
|
|
1794
1820
|
type Entry = {
|
|
1795
1821
|
source: string;
|
|
1796
1822
|
text: string;
|
|
1823
|
+
key?: string;
|
|
1797
1824
|
};
|
|
1798
1825
|
/** Collect every English string a spec ships (menu/rules blocks, titles, labels+hints,
|
|
1799
1826
|
* game name, the `en` dictionary) with a source path — the input to {@link checkSocialPhrases}. */
|
|
1800
1827
|
declare function collectSocialCopy(spec: UISpec): Entry[];
|
|
1801
|
-
/**
|
|
1828
|
+
/**
|
|
1829
|
+
* Scan a spec's English copy AS IT RENDERS IN SOCIAL MODE and return every restricted
|
|
1830
|
+
* phrase. Each collected string is first resolved through the social dictionary
|
|
1831
|
+
* (`locale.socialMessages.en` → built-in `openuiSocialDefaults` → the base text) —
|
|
1832
|
+
* exactly like the translator does — so a game that already swaps "bet"/"pay(table)"
|
|
1833
|
+
* for compliant wording in social mode is NOT flagged, while a phrase with NO social
|
|
1834
|
+
* override (the classic slipped-through "paytable") IS. English by design: the check
|
|
1835
|
+
* targets the base `en` copy that every jurisdiction review starts from.
|
|
1836
|
+
*/
|
|
1802
1837
|
declare function checkSocialPhrases(spec: UISpec): SocialPhraseIssue[];
|
|
1803
1838
|
|
|
1804
1839
|
/**
|
package/dist/index.js
CHANGED
|
@@ -1247,6 +1247,9 @@ function validateSpec(spec) {
|
|
|
1247
1247
|
if (spec.realityCheck != null && !(typeof spec.realityCheck.everyMinutes === "number" && spec.realityCheck.everyMinutes > 0)) {
|
|
1248
1248
|
add("error", "realityCheck.everyMinutes", "bad-interval", "realityCheck.everyMinutes must be a number > 0");
|
|
1249
1249
|
}
|
|
1250
|
+
if (spec.buyFeature?.confirmAboveCost != null && !(typeof spec.buyFeature.confirmAboveCost === "number" && Number.isFinite(spec.buyFeature.confirmAboveCost) && spec.buyFeature.confirmAboveCost >= 0)) {
|
|
1251
|
+
add("error", "buyFeature.confirmAboveCost", "bad-threshold", "buyFeature.confirmAboveCost must be a finite number >= 0");
|
|
1252
|
+
}
|
|
1250
1253
|
if (spec.controls) {
|
|
1251
1254
|
for (const [id, ov] of Object.entries(spec.controls)) {
|
|
1252
1255
|
checkLayout(ov.layout?.anchor, `controls.${id}.layout.anchor`);
|
|
@@ -1399,6 +1402,9 @@ function applyJurisdiction(ui, jur) {
|
|
|
1399
1402
|
if (typeof jur.minimumRoundDuration === "number" && Number.isFinite(jur.minimumRoundDuration)) {
|
|
1400
1403
|
ui.minimumRoundDuration = Math.max(0, jur.minimumRoundDuration);
|
|
1401
1404
|
}
|
|
1405
|
+
if (typeof jur.confirmBuyAboveCost === "number" && Number.isFinite(jur.confirmBuyAboveCost)) {
|
|
1406
|
+
ui.confirmBuyAboveCost = Math.max(0, jur.confirmBuyAboveCost);
|
|
1407
|
+
}
|
|
1402
1408
|
}
|
|
1403
1409
|
|
|
1404
1410
|
// src/notice.ts
|
|
@@ -1456,6 +1462,8 @@ var openuiDefaults = {
|
|
|
1456
1462
|
"openui.replay": "Replay",
|
|
1457
1463
|
"openui.buyFeature.title": "Buy feature",
|
|
1458
1464
|
"openui.buyFeature.message": "Buy this feature now?",
|
|
1465
|
+
// Confirm step for a higher-cost play — {{name}} / {{price}} are interpolated.
|
|
1466
|
+
"openui.buyFeature.confirm": "Buy {{name}} for {{price}}?",
|
|
1459
1467
|
"openui.freeSpins": "FS",
|
|
1460
1468
|
// reality check (RTS 13) — {{minutes}} is interpolated by open-ui's scheduler
|
|
1461
1469
|
"openui.realityCheck.title": "Reality check",
|
|
@@ -1491,6 +1499,7 @@ var openuiSocialDefaults = {
|
|
|
1491
1499
|
"openui.err.insufficient.title": "Insufficient balance",
|
|
1492
1500
|
"openui.buyFeature.title": "Play bonus",
|
|
1493
1501
|
"openui.buyFeature.message": "Play this bonus now?",
|
|
1502
|
+
"openui.buyFeature.confirm": "Play {{name}} for {{price}}?",
|
|
1494
1503
|
"openui.err.insufficient.message": "You don't have enough balance for this play.",
|
|
1495
1504
|
"openui.err.limit.message": "A play limit has been reached. Please try again later."
|
|
1496
1505
|
};
|
|
@@ -1671,6 +1680,11 @@ var OpenUI = class {
|
|
|
1671
1680
|
gameInfo = {};
|
|
1672
1681
|
/** Minimum round duration (ms) from jurisdiction — stored for the GAME to enforce. */
|
|
1673
1682
|
minimumRoundDuration = 0;
|
|
1683
|
+
/** Buy-feature confirm threshold, as a multiple of the base bet. A play (buy or
|
|
1684
|
+
* bet-mode activation) that costs STRICTLY MORE than this must show a confirm step
|
|
1685
|
+
* — Stake requires that bet modes above 2× never activate on a single click. `0`
|
|
1686
|
+
* (the default) = off. Set via `spec.buyFeature.confirmAboveCost` or a jurisdiction. */
|
|
1687
|
+
confirmBuyAboveCost = 0;
|
|
1674
1688
|
sessionNet = 0;
|
|
1675
1689
|
prevVolumes = null;
|
|
1676
1690
|
/** The frozen UISpec `createUI` built this from, if any — authored = run = tested. */
|
|
@@ -2020,6 +2034,13 @@ var OpenUI = class {
|
|
|
2020
2034
|
const j = this.jurisdiction;
|
|
2021
2035
|
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;
|
|
2022
2036
|
}
|
|
2037
|
+
/** True when a buy / bet-mode activation of `cost`× the base bet must be confirmed
|
|
2038
|
+
* before it commits (Stake: no one-click activation above `confirmBuyAboveCost`×).
|
|
2039
|
+
* Always false when the threshold is 0/unset or `cost` isn't a finite number. */
|
|
2040
|
+
shouldConfirmBuy(cost) {
|
|
2041
|
+
const t = this.confirmBuyAboveCost;
|
|
2042
|
+
return t > 0 && Number.isFinite(cost) && cost > t;
|
|
2043
|
+
}
|
|
2023
2044
|
/**
|
|
2024
2045
|
* Start a reality-check reminder (RTS 13). Every `everyMinutes` (WALL-CLOCK, so a
|
|
2025
2046
|
* backgrounded tab can't cheat it) it emits a `realityCheck` event, stops autoplay,
|
|
@@ -2251,15 +2272,18 @@ function collectSocialCopy(spec) {
|
|
|
2251
2272
|
if (spec.game?.name) out.push({ source: "game.name", text: spec.game.name });
|
|
2252
2273
|
const en = spec.locale?.messages?.en;
|
|
2253
2274
|
if (en) {
|
|
2254
|
-
for (const [k, v] of Object.entries(en)) if (typeof v === "string") out.push({ source: `locale.en[${JSON.stringify(k)}]`, text: v });
|
|
2275
|
+
for (const [k, v] of Object.entries(en)) if (typeof v === "string") out.push({ source: `locale.en[${JSON.stringify(k)}]`, text: v, key: k });
|
|
2255
2276
|
}
|
|
2256
2277
|
return out;
|
|
2257
2278
|
}
|
|
2258
2279
|
function checkSocialPhrases(spec) {
|
|
2280
|
+
const socialEn = spec.locale?.socialMessages?.en ?? {};
|
|
2281
|
+
const render = (e) => socialEn[e.key ?? e.text] ?? openuiSocialDefaults[e.key ?? e.text] ?? e.text;
|
|
2259
2282
|
const issues = [];
|
|
2260
|
-
for (const
|
|
2283
|
+
for (const e of collectSocialCopy(spec)) {
|
|
2284
|
+
const text = render(e);
|
|
2261
2285
|
const matches = findForbiddenPhrases(text);
|
|
2262
|
-
if (matches.length) issues.push({ source, text, matches });
|
|
2286
|
+
if (matches.length) issues.push({ source: e.source, text, matches });
|
|
2263
2287
|
}
|
|
2264
2288
|
return issues;
|
|
2265
2289
|
}
|
|
@@ -2350,6 +2374,9 @@ function createUI(spec = {}, hooks = {}) {
|
|
|
2350
2374
|
}
|
|
2351
2375
|
}
|
|
2352
2376
|
}
|
|
2377
|
+
if (spec.buyFeature?.confirmAboveCost != null && Number.isFinite(spec.buyFeature.confirmAboveCost)) {
|
|
2378
|
+
ui.confirmBuyAboveCost = Math.max(0, spec.buyFeature.confirmAboveCost);
|
|
2379
|
+
}
|
|
2353
2380
|
if (spec.jurisdiction) ui.applyJurisdiction(spec.jurisdiction);
|
|
2354
2381
|
const portraitControls = {};
|
|
2355
2382
|
for (const [id, layout] of Object.entries(portraitDefaultLayouts)) {
|