@open-slot-ui/pixi 0.7.0 → 0.8.1
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 +323 -30
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +17 -3
- package/dist/index.d.ts +17 -3
- package/dist/index.js +324 -32
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.d.cts
CHANGED
|
@@ -625,6 +625,13 @@ interface ReplayInfo {
|
|
|
625
625
|
}
|
|
626
626
|
interface HudOptions extends OpenUIPixiOptions {
|
|
627
627
|
hooks?: HostHooks;
|
|
628
|
+
/**
|
|
629
|
+
* Render the library's white HTML **info menu** (Settings · Paytable · Rules) — the Figma
|
|
630
|
+
* design, driven by `spec.menu`, label-left / control-right, fully modular + localized —
|
|
631
|
+
* instead of the in-canvas Pixi menu. Default `true`. `menu: false` opts out of BOTH (the
|
|
632
|
+
* host supplies its own menu). Set `infoMenu: false` to keep the old Pixi menu.
|
|
633
|
+
*/
|
|
634
|
+
infoMenu?: boolean;
|
|
628
635
|
}
|
|
629
636
|
/**
|
|
630
637
|
* The complete-HUD handle returned by `mountHud`. Wraps the headless OpenUI + the
|
|
@@ -674,8 +681,9 @@ interface BootedHud {
|
|
|
674
681
|
* amounts format with `info.currency` (defaults to the balance currency). Also flips
|
|
675
682
|
* the HUD into replay mode (badge + lock). */
|
|
676
683
|
replayStart(info: ReplayInfo, onPlay?: () => void): void;
|
|
677
|
-
/** At the END of a replay, show a "Replay" button
|
|
678
|
-
|
|
684
|
+
/** At the END of a replay, show the SAME facts + a "Replay" button that re-runs it.
|
|
685
|
+
* Non-dismissible (no ✕ / backdrop close) — it's a strict modal → round → modal loop. */
|
|
686
|
+
replayEnd(info: ReplayInfo, onReplay: () => void): void;
|
|
679
687
|
/** Show a buy-feature confirm modal (Cancel / Confirm). Texts are literal-or-key. */
|
|
680
688
|
confirmBuy(opts: {
|
|
681
689
|
title?: string;
|
|
@@ -721,6 +729,12 @@ interface BootedHud {
|
|
|
721
729
|
*/
|
|
722
730
|
declare function mountHud(app: Application, spec?: UISpec, opts?: HudOptions): BootedHud;
|
|
723
731
|
|
|
732
|
+
/**
|
|
733
|
+
* Mount the info menu overlay for a booted `OpenUI`. Reads `ui.spec.menu`; opens/closes with
|
|
734
|
+
* `ui.settingsPanel` (the canvas ☰ drives it). Returns a leak-free teardown.
|
|
735
|
+
*/
|
|
736
|
+
declare function mountInfoMenu(app: Application, ui: OpenUI): () => void;
|
|
737
|
+
|
|
724
738
|
interface BootErrorOptions {
|
|
725
739
|
title?: string;
|
|
726
740
|
message?: string;
|
|
@@ -803,4 +817,4 @@ declare class Tweener {
|
|
|
803
817
|
/** Treat touch devices as non-desktop so we don't fire phantom hover states. */
|
|
804
818
|
declare function isDesktop(): boolean;
|
|
805
819
|
|
|
806
|
-
export { AutoplayDrawerView, AutoplayView, type AutoplayViewOptions, type BootErrorOptions, type BootedHud, type ButtonGlyph, ButtonView, type ButtonViewOptions, ControlView, ControlViewFactory, DialogView, type DialogViewOptions, type HudOptions, type InfoContentBuilder, MenuView, type MenuViewOptions, OpenUIPixi, OpenUIPixiOptions, type PanelBodyOptions, PanelBodyView, PanelView, type PanelViewOptions, type PopoverArt, type PopoverParts, PopoverView, ReadoutView, type ReadoutViewOptions, type ReplayInfo, SelectView, type SelectViewOptions, SliderView, SpinSkin, SpinSkinFactory, SpinView, type SpinViewOptions, StepperView, type SvgSpinTextures, TextCellRenderer, type TextCellRendererOptions, ToggleView, type ToggleViewOptions, TurboView, type TurboViewOptions, Tweener, ValueDisplayView, defaultSpinSkin, drawSpin, hideBootError, isDesktop, mountHud, showBootError, svgSpinSkin };
|
|
820
|
+
export { AutoplayDrawerView, AutoplayView, type AutoplayViewOptions, type BootErrorOptions, type BootedHud, type ButtonGlyph, ButtonView, type ButtonViewOptions, ControlView, ControlViewFactory, DialogView, type DialogViewOptions, type HudOptions, type InfoContentBuilder, MenuView, type MenuViewOptions, OpenUIPixi, OpenUIPixiOptions, type PanelBodyOptions, PanelBodyView, PanelView, type PanelViewOptions, type PopoverArt, type PopoverParts, PopoverView, ReadoutView, type ReadoutViewOptions, type ReplayInfo, SelectView, type SelectViewOptions, SliderView, SpinSkin, SpinSkinFactory, SpinView, type SpinViewOptions, StepperView, type SvgSpinTextures, TextCellRenderer, type TextCellRendererOptions, ToggleView, type ToggleViewOptions, TurboView, type TurboViewOptions, Tweener, ValueDisplayView, defaultSpinSkin, drawSpin, hideBootError, isDesktop, mountHud, mountInfoMenu, showBootError, svgSpinSkin };
|
package/dist/index.d.ts
CHANGED
|
@@ -625,6 +625,13 @@ interface ReplayInfo {
|
|
|
625
625
|
}
|
|
626
626
|
interface HudOptions extends OpenUIPixiOptions {
|
|
627
627
|
hooks?: HostHooks;
|
|
628
|
+
/**
|
|
629
|
+
* Render the library's white HTML **info menu** (Settings · Paytable · Rules) — the Figma
|
|
630
|
+
* design, driven by `spec.menu`, label-left / control-right, fully modular + localized —
|
|
631
|
+
* instead of the in-canvas Pixi menu. Default `true`. `menu: false` opts out of BOTH (the
|
|
632
|
+
* host supplies its own menu). Set `infoMenu: false` to keep the old Pixi menu.
|
|
633
|
+
*/
|
|
634
|
+
infoMenu?: boolean;
|
|
628
635
|
}
|
|
629
636
|
/**
|
|
630
637
|
* The complete-HUD handle returned by `mountHud`. Wraps the headless OpenUI + the
|
|
@@ -674,8 +681,9 @@ interface BootedHud {
|
|
|
674
681
|
* amounts format with `info.currency` (defaults to the balance currency). Also flips
|
|
675
682
|
* the HUD into replay mode (badge + lock). */
|
|
676
683
|
replayStart(info: ReplayInfo, onPlay?: () => void): void;
|
|
677
|
-
/** At the END of a replay, show a "Replay" button
|
|
678
|
-
|
|
684
|
+
/** At the END of a replay, show the SAME facts + a "Replay" button that re-runs it.
|
|
685
|
+
* Non-dismissible (no ✕ / backdrop close) — it's a strict modal → round → modal loop. */
|
|
686
|
+
replayEnd(info: ReplayInfo, onReplay: () => void): void;
|
|
679
687
|
/** Show a buy-feature confirm modal (Cancel / Confirm). Texts are literal-or-key. */
|
|
680
688
|
confirmBuy(opts: {
|
|
681
689
|
title?: string;
|
|
@@ -721,6 +729,12 @@ interface BootedHud {
|
|
|
721
729
|
*/
|
|
722
730
|
declare function mountHud(app: Application, spec?: UISpec, opts?: HudOptions): BootedHud;
|
|
723
731
|
|
|
732
|
+
/**
|
|
733
|
+
* Mount the info menu overlay for a booted `OpenUI`. Reads `ui.spec.menu`; opens/closes with
|
|
734
|
+
* `ui.settingsPanel` (the canvas ☰ drives it). Returns a leak-free teardown.
|
|
735
|
+
*/
|
|
736
|
+
declare function mountInfoMenu(app: Application, ui: OpenUI): () => void;
|
|
737
|
+
|
|
724
738
|
interface BootErrorOptions {
|
|
725
739
|
title?: string;
|
|
726
740
|
message?: string;
|
|
@@ -803,4 +817,4 @@ declare class Tweener {
|
|
|
803
817
|
/** Treat touch devices as non-desktop so we don't fire phantom hover states. */
|
|
804
818
|
declare function isDesktop(): boolean;
|
|
805
819
|
|
|
806
|
-
export { AutoplayDrawerView, AutoplayView, type AutoplayViewOptions, type BootErrorOptions, type BootedHud, type ButtonGlyph, ButtonView, type ButtonViewOptions, ControlView, ControlViewFactory, DialogView, type DialogViewOptions, type HudOptions, type InfoContentBuilder, MenuView, type MenuViewOptions, OpenUIPixi, OpenUIPixiOptions, type PanelBodyOptions, PanelBodyView, PanelView, type PanelViewOptions, type PopoverArt, type PopoverParts, PopoverView, ReadoutView, type ReadoutViewOptions, type ReplayInfo, SelectView, type SelectViewOptions, SliderView, SpinSkin, SpinSkinFactory, SpinView, type SpinViewOptions, StepperView, type SvgSpinTextures, TextCellRenderer, type TextCellRendererOptions, ToggleView, type ToggleViewOptions, TurboView, type TurboViewOptions, Tweener, ValueDisplayView, defaultSpinSkin, drawSpin, hideBootError, isDesktop, mountHud, showBootError, svgSpinSkin };
|
|
820
|
+
export { AutoplayDrawerView, AutoplayView, type AutoplayViewOptions, type BootErrorOptions, type BootedHud, type ButtonGlyph, ButtonView, type ButtonViewOptions, ControlView, ControlViewFactory, DialogView, type DialogViewOptions, type HudOptions, type InfoContentBuilder, MenuView, type MenuViewOptions, OpenUIPixi, OpenUIPixiOptions, type PanelBodyOptions, PanelBodyView, PanelView, type PanelViewOptions, type PopoverArt, type PopoverParts, PopoverView, ReadoutView, type ReadoutViewOptions, type ReplayInfo, SelectView, type SelectViewOptions, SliderView, SpinSkin, SpinSkinFactory, SpinView, type SpinViewOptions, StepperView, type SvgSpinTextures, TextCellRenderer, type TextCellRendererOptions, ToggleView, type ToggleViewOptions, TurboView, type TurboViewOptions, Tweener, ValueDisplayView, defaultSpinSkin, drawSpin, hideBootError, isDesktop, mountHud, mountInfoMenu, showBootError, svgSpinSkin };
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ControlView, ButtonView, SliderView, buildBlockColumn, OpenUIPixi } from './chunk-PYXZIGXG.js';
|
|
2
2
|
export { AutoplayDrawerView, AutoplayView, ButtonView, ControlView, DialogView, MenuView, OpenUIPixi, ReadoutView, SelectView, SliderView, SpinView, StepperView, TextCellRenderer, ToggleView, TurboView, Tweener, ValueDisplayView, buildBlockColumn, defaultSpinSkin, drawSpin, isDesktop, svgSpinSkin } from './chunk-PYXZIGXG.js';
|
|
3
3
|
import { Text, Graphics, Container, Rectangle } from 'pixi.js';
|
|
4
|
-
import { createUI, composeMenu, buildPanel, formatAmount } from '@open-slot-ui/core';
|
|
4
|
+
import { LOCALE_LABELS, createUI, composeMenu, buildPanel, formatAmount } from '@open-slot-ui/core';
|
|
5
5
|
|
|
6
6
|
var RISE = 26;
|
|
7
7
|
var STAGGER = 38;
|
|
@@ -278,16 +278,327 @@ var PanelBodyView = class extends ControlView {
|
|
|
278
278
|
super.dispose();
|
|
279
279
|
}
|
|
280
280
|
};
|
|
281
|
+
var esc = (s) => s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
282
|
+
var rich = (s) => esc(s).replace(/\*\*(.+?)\*\*/g, "<b>$1</b>");
|
|
283
|
+
function renderBlocks(blocks, tr) {
|
|
284
|
+
const out = [];
|
|
285
|
+
for (const b of blocks) {
|
|
286
|
+
switch (b.kind) {
|
|
287
|
+
case "text":
|
|
288
|
+
out.push(`<p>${rich(tr(b.text))}</p>`);
|
|
289
|
+
break;
|
|
290
|
+
case "heading":
|
|
291
|
+
out.push(`<div class="ohm-sec"><span>${esc(tr(b.text))}</span></div>`);
|
|
292
|
+
break;
|
|
293
|
+
case "subheading":
|
|
294
|
+
out.push(`<h4 class="ohm-subh">${esc(tr(b.text))}</h4>`);
|
|
295
|
+
break;
|
|
296
|
+
case "legal":
|
|
297
|
+
out.push(`<p class="ohm-legal">${rich(tr(b.text))}</p>`);
|
|
298
|
+
break;
|
|
299
|
+
case "divider":
|
|
300
|
+
out.push('<hr class="ohm-hr">');
|
|
301
|
+
break;
|
|
302
|
+
case "image":
|
|
303
|
+
out.push(`<img class="ohm-feature" alt="${esc(tr(b.alt ?? ""))}" src="${b.src}" loading="lazy">`);
|
|
304
|
+
break;
|
|
305
|
+
case "media": {
|
|
306
|
+
const img = `<img alt="${esc(tr(b.alt ?? ""))}" src="${b.src}" loading="lazy">`;
|
|
307
|
+
const body = `<div class="ohm-media-body">${b.title ? `<h4>${esc(tr(b.title))}</h4>` : ""}<p>${rich(tr(b.text))}</p></div>`;
|
|
308
|
+
out.push(`<div class="ohm-media ohm-media--${b.side ?? "left"}">${img}${body}</div>`);
|
|
309
|
+
break;
|
|
310
|
+
}
|
|
311
|
+
case "cards": {
|
|
312
|
+
const cards = b.items.map((it) => `<div class="ohm-fcard">${it.icon ? `<img src="${it.icon}" alt="" loading="lazy">` : ""}<h5>${esc(tr(it.title))}</h5>${it.text ? `<p>${rich(tr(it.text))}</p>` : ""}</div>`).join("");
|
|
313
|
+
out.push(`<div class="ohm-cards">${cards}</div>`);
|
|
314
|
+
break;
|
|
315
|
+
}
|
|
316
|
+
case "paytable":
|
|
317
|
+
out.push(`<div class="ohm-grid">${renderPaytable(b.rows, tr)}</div>`);
|
|
318
|
+
break;
|
|
319
|
+
case "table": {
|
|
320
|
+
const head = b.columns?.length ? `<thead><tr>${b.columns.map((c) => `<th>${esc(tr(c))}</th>`).join("")}</tr></thead>` : "";
|
|
321
|
+
const body = b.rows.map((r) => `<tr>${r.map((c) => `<td>${esc(tr(c))}</td>`).join("")}</tr>`).join("");
|
|
322
|
+
out.push(`<table class="ohm-table">${head}<tbody>${body}</tbody></table>`);
|
|
323
|
+
break;
|
|
324
|
+
}
|
|
325
|
+
case "stat-grid": {
|
|
326
|
+
const rows = b.items.map((it) => `<div><dt>${esc(tr(it.label))}</dt><dd>${esc(tr(it.value))}</dd></div>`).join("");
|
|
327
|
+
out.push(`<dl class="ohm-stats">${rows}</dl>`);
|
|
328
|
+
break;
|
|
329
|
+
}
|
|
330
|
+
case "steps": {
|
|
331
|
+
const items = b.items.map((s) => `<li>${rich(tr(s))}</li>`).join("");
|
|
332
|
+
out.push(b.ordered ? `<ol class="ohm-steps">${items}</ol>` : `<ul class="ohm-steps">${items}</ul>`);
|
|
333
|
+
break;
|
|
334
|
+
}
|
|
335
|
+
case "callout":
|
|
336
|
+
out.push(`<div class="ohm-callout ohm-callout--${b.tone ?? "info"}">${b.title ? `<b>${esc(tr(b.title))}</b>` : ""}<p>${rich(tr(b.text))}</p></div>`);
|
|
337
|
+
break;
|
|
338
|
+
case "group":
|
|
339
|
+
out.push(`<div class="ohm-group">${b.title ? `<h4 class="ohm-subh">${esc(tr(b.title))}</h4>` : ""}${renderBlocks(b.children, tr)}</div>`);
|
|
340
|
+
break;
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
return out.join("\n");
|
|
344
|
+
}
|
|
345
|
+
function renderPaytable(rows, tr) {
|
|
346
|
+
return rows.map((r) => {
|
|
347
|
+
const icon = r.icon ? `<img class="ohm-symimg" src="${r.icon}" alt="" loading="lazy">` : `<span class="ohm-emoji">${esc(tr(r.symbol ?? ""))}</span>`;
|
|
348
|
+
const lines = r.payouts.split("\n").map((line) => {
|
|
349
|
+
const i = line.indexOf(":");
|
|
350
|
+
return i >= 0 ? `<div><b>${esc(line.slice(0, i))}</b><span>${esc(line.slice(i + 1))}</span></div>` : `<div><span>${esc(line)}</span></div>`;
|
|
351
|
+
}).join("");
|
|
352
|
+
return `<div class="ohm-sym">${icon}<div class="ohm-pay">${lines}</div></div>`;
|
|
353
|
+
}).join("");
|
|
354
|
+
}
|
|
355
|
+
function renderSettingBlock(b, tr) {
|
|
356
|
+
const hint = "hint" in b && b.hint ? `<div class="ohm-hint">${esc(tr(b.hint))}</div>` : "";
|
|
357
|
+
const label = "label" in b && b.label ? esc(tr(b.label)) : "";
|
|
358
|
+
switch (b.kind) {
|
|
359
|
+
case "toggle":
|
|
360
|
+
return `<div class="ohm-setting"><label class="ohm-row ohm-check"><span>${label}</span><span class="ohm-ctl"><input data-set="${b.id}" type="checkbox"${b.on ? " checked" : ""}></span></label>${hint}</div>`;
|
|
361
|
+
case "slider":
|
|
362
|
+
return `<div class="ohm-setting"><label class="ohm-row"><span>${label}</span><input data-set="${b.id}" type="range" min="0" max="1" step="0.01" value="${b.initial ?? 1}"></label>${hint}</div>`;
|
|
363
|
+
case "select": {
|
|
364
|
+
const opts = b.options.map((o, i) => `<option value="${esc(o.value)}"${i === (b.index ?? 0) ? " selected" : ""}>${esc(tr(o.label))}</option>`).join("");
|
|
365
|
+
return `<div class="ohm-setting"><label class="ohm-row"><span>${label}</span><select data-set="${b.id}">${opts}</select></label>${hint}</div>`;
|
|
366
|
+
}
|
|
367
|
+
default:
|
|
368
|
+
return "";
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
function mountInfoMenu(app, ui) {
|
|
372
|
+
const menu = ui.spec?.menu ?? {};
|
|
373
|
+
const tr = (k) => ui.t(k);
|
|
374
|
+
const disposers = [];
|
|
375
|
+
const host2 = document.createElement("div");
|
|
376
|
+
host2.className = "ohm-root";
|
|
377
|
+
const vars = {
|
|
378
|
+
"--accent": "#d99000",
|
|
379
|
+
"--accent-text": "#1a1200",
|
|
380
|
+
"--surface": "#ffffff",
|
|
381
|
+
"--surface-alt": "#eef1f6",
|
|
382
|
+
"--text": "#181b20",
|
|
383
|
+
"--text-dim": "#5b6472",
|
|
384
|
+
"--card-radius": "8px",
|
|
385
|
+
"--font": ui.theme.type.family
|
|
386
|
+
};
|
|
387
|
+
for (const [k, v] of Object.entries(vars)) host2.style.setProperty(k, v);
|
|
388
|
+
const locales = ui.spec?.locale ? Array.from(/* @__PURE__ */ new Set([ui.spec.locale.locale, ...Object.keys(ui.spec.locale.messages)])) : [];
|
|
389
|
+
const langRow = locales.length > 1 ? `<div class="ohm-setting"><label class="ohm-row"><span>${tr("Language")}</span><select id="ohm-lang">${locales.map((c) => `<option value="${c}"${c === ui.locale.get() ? " selected" : ""}>${esc(LOCALE_LABELS[c] ?? c)}</option>`).join("")}</select></label></div>` : "";
|
|
390
|
+
const turbo = ui.turbo;
|
|
391
|
+
const cap = (m) => m.charAt(0).toUpperCase() + m.slice(1);
|
|
392
|
+
const turboCtl = turbo.modeCount <= 2 ? `<span class="ohm-ctl"><input id="ohm-turbo" type="checkbox"></span>` : `<span class="ohm-ctl"><div class="ohm-segmented" id="ohm-turbo-seg">${turbo.modes.map((m, i) => `<button class="ohm-seg" data-i="${i}">${esc(tr(cap(m)))}</button>`).join("")}</div></span>`;
|
|
393
|
+
const customSettings = (menu.settings ?? []).map((b) => renderSettingBlock(b, tr)).join("");
|
|
394
|
+
const soundMode = menu.sound ?? "sliders";
|
|
395
|
+
const volRow = (id, label, hint, val) => `<div class="ohm-setting"><label class="ohm-row"><span>${esc(tr(label))}</span><input class="ohm-slider" data-vol="${id}" type="range" min="0" max="1" step="0.01" value="${val}"></label><div class="ohm-hint">${esc(tr(hint))}</div></div>`;
|
|
396
|
+
const soundSliders = soundMode === "master" ? volRow("master", "Volume", "Adjust the overall game volume.", ui.sfxSlider.value.get()) : soundMode === "sliders" ? volRow("music", "Music", "Adjust the background music volume.", ui.musicSlider.value.get()) + volRow("sfx", "Effects", "Adjust the sound-effects volume.", ui.sfxSlider.value.get()) : "";
|
|
397
|
+
const paytableHtml = menu.paytable ? renderBlocks(menu.paytable, tr) : "";
|
|
398
|
+
const rulesHtml = menu.rules ? renderBlocks(menu.rules, tr) : "";
|
|
399
|
+
const banner = menu.banner ? `<img class="ohm-logo" alt="" src="${menu.banner.src}"${menu.banner.width ? ` width="${menu.banner.width}"` : ""}${menu.banner.height ? ` height="${menu.banner.height}"` : ""}>` : ui.gameInfo.name ? `<h1 class="ohm-logo-text">${esc(ui.gameInfo.name)}</h1>` : "";
|
|
400
|
+
host2.innerHTML = `
|
|
401
|
+
<div class="ohm-backdrop" data-close></div>
|
|
402
|
+
<button class="ohm-x" data-close aria-label="Close">\u2715</button>
|
|
403
|
+
<div class="ohm-card" role="dialog" aria-modal="true">
|
|
404
|
+
<div class="ohm-body">
|
|
405
|
+
${banner}
|
|
406
|
+
<div class="ohm-sec"><span>${tr("Settings")}</span></div>
|
|
407
|
+
<div class="ohm-setting"><label class="ohm-row ohm-check"><span>${tr("Sound")}</span><span class="ohm-ctl"><input id="ohm-sound" type="checkbox" checked></span></label><div class="ohm-hint">${tr("Turn all game sound and music on or off.")}</div></div>
|
|
408
|
+
${soundSliders}
|
|
409
|
+
${langRow}
|
|
410
|
+
<div class="ohm-setting"><div class="ohm-row ohm-check"><span>${tr("Quick spin")}</span>${turboCtl}</div><div class="ohm-hint">${tr("Speed up rounds by shortening the animation. The result is identical.")}</div></div>
|
|
411
|
+
${customSettings}
|
|
412
|
+
${paytableHtml ? `<div class="ohm-sec"><span>${tr("Paytable")}</span></div>${paytableHtml}` : ""}
|
|
413
|
+
${rulesHtml ? `<div class="ohm-sec"><span>${tr("Rules")}</span></div><div class="ohm-rules" id="ohm-rules">${rulesHtml}</div>` : ""}
|
|
414
|
+
</div>
|
|
415
|
+
</div>`;
|
|
416
|
+
const style = document.createElement("style");
|
|
417
|
+
style.textContent = OHM_CSS;
|
|
418
|
+
host2.appendChild(style);
|
|
419
|
+
document.body.appendChild(host2);
|
|
420
|
+
const $ = (sel) => host2.querySelector(sel);
|
|
421
|
+
const sound = $("#ohm-sound");
|
|
422
|
+
if (sound) {
|
|
423
|
+
sound.checked = !ui.muted.get();
|
|
424
|
+
sound.addEventListener("change", () => ui.setMuted(!sound.checked));
|
|
425
|
+
disposers.push(ui.muted.subscribe((m) => {
|
|
426
|
+
sound.checked = !m;
|
|
427
|
+
}));
|
|
428
|
+
}
|
|
429
|
+
host2.querySelectorAll("[data-vol]").forEach((el) => {
|
|
430
|
+
const which = el.dataset.vol;
|
|
431
|
+
el.addEventListener("input", () => {
|
|
432
|
+
const v = Number(el.value);
|
|
433
|
+
if (which === "music") ui.musicSlider.setNormalized(v);
|
|
434
|
+
else if (which === "sfx") ui.sfxSlider.setNormalized(v);
|
|
435
|
+
else {
|
|
436
|
+
ui.musicSlider.setNormalized(v);
|
|
437
|
+
ui.sfxSlider.setNormalized(v);
|
|
438
|
+
}
|
|
439
|
+
});
|
|
440
|
+
const src = which === "music" ? ui.musicSlider : ui.sfxSlider;
|
|
441
|
+
disposers.push(src.value.subscribe((v) => {
|
|
442
|
+
el.value = String(v);
|
|
443
|
+
}));
|
|
444
|
+
});
|
|
445
|
+
const lang = $("#ohm-lang");
|
|
446
|
+
if (lang) lang.addEventListener("change", () => ui.setLocale(lang.value));
|
|
447
|
+
if (turbo.modeCount <= 2) {
|
|
448
|
+
const tg = $("#ohm-turbo");
|
|
449
|
+
if (tg) {
|
|
450
|
+
tg.checked = turbo.isOn;
|
|
451
|
+
tg.addEventListener("change", () => turbo.set(tg.checked));
|
|
452
|
+
disposers.push(turbo.index.subscribe(() => {
|
|
453
|
+
tg.checked = turbo.isOn;
|
|
454
|
+
}));
|
|
455
|
+
}
|
|
456
|
+
} else {
|
|
457
|
+
const segs = Array.from(host2.querySelectorAll(".ohm-seg"));
|
|
458
|
+
segs.forEach((b) => b.addEventListener("click", () => turbo.setIndex(Number(b.dataset.i))));
|
|
459
|
+
const sync = () => segs.forEach((b, i) => b.classList.toggle("active", i === turbo.index.get()));
|
|
460
|
+
disposers.push(turbo.index.subscribe(sync));
|
|
461
|
+
sync();
|
|
462
|
+
}
|
|
463
|
+
host2.querySelectorAll("[data-set]").forEach((el) => {
|
|
464
|
+
const id = el.dataset.set;
|
|
465
|
+
if (el instanceof HTMLInputElement && el.type === "checkbox") el.addEventListener("change", () => ui.bus.emit("toggled", { id, on: el.checked }));
|
|
466
|
+
else if (el instanceof HTMLInputElement && el.type === "range") el.addEventListener("input", () => ui.bus.emit("valueChanged", { id, value: Number(el.value) }));
|
|
467
|
+
else if (el instanceof HTMLSelectElement) el.addEventListener("change", () => ui.bus.emit("optionSelected", { id, value: el.value, index: el.selectedIndex }));
|
|
468
|
+
});
|
|
469
|
+
host2.querySelectorAll("[data-close]").forEach((b) => b.addEventListener("click", () => ui.settingsPanel.closePanel()));
|
|
470
|
+
disposers.push(
|
|
471
|
+
ui.locale.subscribe(() => {
|
|
472
|
+
const body = host2.querySelector(".ohm-body");
|
|
473
|
+
if (body) {
|
|
474
|
+
const rulesEl = host2.querySelector("#ohm-rules");
|
|
475
|
+
if (rulesEl && menu.rules) rulesEl.innerHTML = renderBlocks(menu.rules, tr);
|
|
476
|
+
if (lang) lang.value = ui.locale.get();
|
|
477
|
+
}
|
|
478
|
+
})
|
|
479
|
+
);
|
|
480
|
+
disposers.push(ui.settingsPanel.state.subscribe(() => host2.classList.toggle("open", ui.settingsPanel.isOpen)));
|
|
481
|
+
return () => {
|
|
482
|
+
for (const d of disposers.splice(0)) d();
|
|
483
|
+
host2.remove();
|
|
484
|
+
};
|
|
485
|
+
}
|
|
486
|
+
var OHM_CSS = `
|
|
487
|
+
.ohm-root { position: fixed; inset: 0; z-index: 10000; display: grid; place-items: center; font-family: var(--font); opacity: 0; pointer-events: none; transition: opacity .18s ease; }
|
|
488
|
+
.ohm-root.open { opacity: 1; pointer-events: auto; }
|
|
489
|
+
.ohm-backdrop { position: absolute; inset: 0; background: rgba(8,6,4,0); backdrop-filter: blur(0px) saturate(1); -webkit-backdrop-filter: blur(0px) saturate(1); transition: background .4s ease, backdrop-filter .4s ease, -webkit-backdrop-filter .4s ease; }
|
|
490
|
+
.ohm-root.open .ohm-backdrop { background: rgba(8,6,4,.34); backdrop-filter: blur(6px) saturate(1.1); -webkit-backdrop-filter: blur(6px) saturate(1.1); }
|
|
491
|
+
.ohm-card { position: relative; width: min(92%, 1100px); max-height: 86vh; display: flex; flex-direction: column; background: var(--surface); color: var(--text); border: 1.5px solid #000; border-radius: var(--card-radius); box-shadow: 0 30px 80px rgba(0,0,0,.5); overflow: hidden; transform: translateY(8px) scale(.99); transition: transform .18s ease; }
|
|
492
|
+
.ohm-root.open .ohm-card { transform: none; }
|
|
493
|
+
.ohm-x { position: absolute; top: 18px; right: 22px; width: 46px; height: 46px; border-radius: 999px; border: 0; background: rgba(18,14,10,.82); color: #fff; font-size: 18px; cursor: pointer; display: grid; place-items: center; box-shadow: 0 6px 18px rgba(0,0,0,.45); z-index: 2; transition: transform .12s, background .12s; }
|
|
494
|
+
.ohm-x:hover { transform: scale(1.08); background: rgba(18,14,10,.95); }
|
|
495
|
+
.ohm-body { padding: 24px 26px 26px; overflow-y: scroll; }
|
|
496
|
+
.ohm-body::-webkit-scrollbar { width: 18px; }
|
|
497
|
+
.ohm-body::-webkit-scrollbar-track { background: transparent; margin: 12px 0; }
|
|
498
|
+
.ohm-body::-webkit-scrollbar-thumb { background-color: #111; border: 6px solid transparent; background-clip: padding-box; border-radius: 999px; min-height: 44px; }
|
|
499
|
+
.ohm-body::-webkit-scrollbar-thumb:hover { background-color: #000; }
|
|
500
|
+
.ohm-logo { display: block; margin: 6px auto 18px; max-width: 64%; height: auto; }
|
|
501
|
+
.ohm-logo-text { margin: 6px 0 18px; text-align: center; font-size: 30px; font-weight: 900; letter-spacing: 1px; color: var(--text); }
|
|
502
|
+
.ohm-sec { display: flex; align-items: center; gap: 14px; margin: 26px 0 14px; color: var(--text); font-weight: 800; letter-spacing: 1px; }
|
|
503
|
+
.ohm-sec::before, .ohm-sec::after { content: ""; flex: 1; height: 2px; background: color-mix(in srgb, var(--text) 80%, transparent); border-radius: 2px; }
|
|
504
|
+
.ohm-root *, .ohm-root *::before, .ohm-root *::after { box-sizing: border-box; }
|
|
505
|
+
.ohm-row { display: flex; align-items: center; gap: 16px; margin: 14px 0; font-weight: 700; }
|
|
506
|
+
.ohm-setting { margin: 14px 0; }
|
|
507
|
+
.ohm-setting .ohm-row { margin: 0; }
|
|
508
|
+
.ohm-hint { margin: 3px 0 0; font-size: 12.5px; font-weight: 500; color: var(--text-dim); }
|
|
509
|
+
.ohm-row > span:first-child { flex: none; min-width: 110px; }
|
|
510
|
+
.ohm-row input[type=range] { flex: 1; min-width: 0; max-width: min(440px, 60dvw); accent-color: var(--accent); height: 6px; }
|
|
511
|
+
.ohm-row select { flex: 1; min-width: 0; max-width: min(440px, 60dvw); padding: 11px 14px; border-radius: 4px; border: 2px solid var(--accent); background: var(--surface-alt); color: var(--text); font-weight: 700; font-size: 15px; cursor: pointer; }
|
|
512
|
+
.ohm-row select option { background: var(--surface); color: var(--text); font-weight: 600; }
|
|
513
|
+
.ohm-row select option:checked { background: var(--accent); color: var(--accent-text); }
|
|
514
|
+
.ohm-ctl { flex: 1; min-width: 0; max-width: min(440px, 60dvw); display: flex; align-items: center; justify-content: flex-end; }
|
|
515
|
+
.ohm-check input[type=checkbox] { appearance: none; -webkit-appearance: none; width: 50px; height: 28px; border-radius: 999px; background: color-mix(in srgb, var(--text-dim) 38%, transparent); position: relative; cursor: pointer; transition: background .15s; flex: none; }
|
|
516
|
+
.ohm-check input[type=checkbox]:checked { background: var(--accent); }
|
|
517
|
+
.ohm-check input[type=checkbox]::before { content: ""; position: absolute; top: 3px; left: 3px; width: 22px; height: 22px; border-radius: 999px; background: #fff; transition: left .15s; box-shadow: 0 1px 3px rgba(0,0,0,.3); }
|
|
518
|
+
.ohm-check input[type=checkbox]:checked::before { left: 25px; }
|
|
519
|
+
.ohm-segmented { display: inline-flex; gap: 4px; padding: 4px; background: var(--surface-alt); border-radius: 999px; border: 1px solid color-mix(in srgb, var(--text-dim) 30%, transparent); }
|
|
520
|
+
.ohm-seg { border: 0; background: transparent; color: var(--text-dim); font-weight: 700; font-size: 14px; padding: 8px 18px; border-radius: 999px; cursor: pointer; transition: background .12s, color .12s; }
|
|
521
|
+
.ohm-seg.active { background: var(--accent); color: var(--accent-text); }
|
|
522
|
+
.ohm-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 14px; }
|
|
523
|
+
.ohm-sym { display: flex; align-items: center; gap: 16px; padding: 6px 4px; }
|
|
524
|
+
.ohm-emoji { font-size: 48px; line-height: 1; filter: drop-shadow(0 2px 4px rgba(0,0,0,.3)); }
|
|
525
|
+
.ohm-symimg { width: 56px; height: 56px; object-fit: contain; flex: none; }
|
|
526
|
+
.ohm-pay { font-size: 13px; line-height: 1.6; }
|
|
527
|
+
.ohm-pay div { display: flex; gap: 6px; }
|
|
528
|
+
.ohm-pay b { min-width: 42px; }
|
|
529
|
+
.ohm-pay span { color: var(--accent); font-weight: 700; }
|
|
530
|
+
.ohm-body p { color: var(--text-dim); line-height: 1.6; }
|
|
531
|
+
.ohm-body p b { color: var(--text); }
|
|
532
|
+
.ohm-feature { display: block; width: 100%; height: auto; margin: 10px 0; }
|
|
533
|
+
.ohm-stats { margin: 12px 0; display: grid; grid-template-columns: 1fr 1fr; gap: 0 28px; }
|
|
534
|
+
.ohm-stats > div { display: flex; justify-content: space-between; padding: 9px 0; border-bottom: 1px solid color-mix(in srgb, var(--text-dim) 20%, transparent); }
|
|
535
|
+
.ohm-stats dt { color: var(--text-dim); margin: 0; } .ohm-stats dd { margin: 0; font-weight: 700; }
|
|
536
|
+
.ohm-callout { margin: 16px 0 4px; padding: 14px 16px; border-radius: 12px; border-left: 4px solid var(--accent); background: color-mix(in srgb, var(--accent) 9%, transparent); }
|
|
537
|
+
.ohm-callout b { color: var(--accent); } .ohm-callout p { margin: 4px 0 0; color: var(--text); }
|
|
538
|
+
.ohm-callout--warning { border-left-color: #e0a106; background: color-mix(in srgb, #e0a106 10%, transparent); }
|
|
539
|
+
.ohm-callout--warning b { color: #b07d09; }
|
|
540
|
+
.ohm-subh { margin: 22px 0 8px; font-size: 15px; font-weight: 800; letter-spacing: .5px; color: var(--text); }
|
|
541
|
+
.ohm-legal { font-size: 12px; line-height: 1.6; color: var(--text-dim); opacity: .85; }
|
|
542
|
+
.ohm-hr { border: 0; border-top: 1px solid color-mix(in srgb, var(--text-dim) 30%, transparent); margin: 18px 0; }
|
|
543
|
+
.ohm-media { display: flex; align-items: center; gap: 18px; margin: 14px 0; }
|
|
544
|
+
.ohm-media--right { flex-direction: row-reverse; }
|
|
545
|
+
.ohm-media > img { width: 40%; max-width: 320px; height: auto; flex: none; }
|
|
546
|
+
.ohm-media-body { flex: 1; }
|
|
547
|
+
.ohm-media-body h4 { margin: 0 0 6px; font-size: 16px; color: var(--text); }
|
|
548
|
+
.ohm-media-body p { margin: 0; }
|
|
549
|
+
.ohm-cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 12px; margin: 12px 0; }
|
|
550
|
+
.ohm-fcard { padding: 8px 6px; text-align: center; }
|
|
551
|
+
.ohm-fcard img { display: block; width: 48px; height: 48px; margin: 0 auto 8px; }
|
|
552
|
+
.ohm-fcard h5 { margin: 0 0 4px; font-size: 14px; color: var(--text); }
|
|
553
|
+
.ohm-fcard p { margin: 0; font-size: 12px; line-height: 1.5; }
|
|
554
|
+
.ohm-table { width: 100%; border-collapse: collapse; margin: 12px 0; font-size: 14px; }
|
|
555
|
+
.ohm-table th { text-align: left; padding: 8px 10px; font-weight: 800; color: var(--text); border-bottom: 2px solid color-mix(in srgb, var(--text-dim) 35%, transparent); }
|
|
556
|
+
.ohm-table td { padding: 8px 10px; border-bottom: 1px solid color-mix(in srgb, var(--text-dim) 18%, transparent); }
|
|
557
|
+
.ohm-table td:first-child { color: var(--text); font-weight: 700; }
|
|
558
|
+
.ohm-table td:not(:first-child) { color: var(--accent); font-weight: 700; }
|
|
559
|
+
.ohm-steps { margin: 12px 0; padding-left: 22px; color: var(--text-dim); line-height: 1.7; }
|
|
560
|
+
.ohm-steps li { margin: 5px 0; }
|
|
561
|
+
.ohm-steps b { color: var(--text); }
|
|
562
|
+
.ohm-group { margin: 8px 0; }
|
|
563
|
+
`;
|
|
564
|
+
|
|
565
|
+
// src/mountHud.ts
|
|
566
|
+
function showReplayModal(ui, info, buttonKey, onSelect) {
|
|
567
|
+
const cur = info.currency ?? ui.balance.currency.get();
|
|
568
|
+
const money = (n) => formatAmount(n, cur);
|
|
569
|
+
ui.showNotice(
|
|
570
|
+
[
|
|
571
|
+
{ kind: "heading", id: "openui-replay-h", text: "openui.replay.title" },
|
|
572
|
+
{
|
|
573
|
+
kind: "stat-grid",
|
|
574
|
+
id: "openui-replay-g",
|
|
575
|
+
items: [
|
|
576
|
+
{ label: "openui.replay.baseBet", value: money(info.baseBet) },
|
|
577
|
+
{ label: "openui.replay.costMultiplier", value: `${info.costMultiplier}\xD7` },
|
|
578
|
+
{ label: "openui.replay.payoutMultiplier", value: `${info.payoutMultiplier}\xD7` },
|
|
579
|
+
{ label: "openui.replay.amount", value: money(info.amount) }
|
|
580
|
+
]
|
|
581
|
+
}
|
|
582
|
+
],
|
|
583
|
+
[{ label: buttonKey, variant: "primary", onSelect: () => {
|
|
584
|
+
ui.hideNotice();
|
|
585
|
+
onSelect();
|
|
586
|
+
} }],
|
|
587
|
+
{ blocking: true }
|
|
588
|
+
);
|
|
589
|
+
}
|
|
281
590
|
function mountHud(app, spec = {}, opts = {}) {
|
|
282
|
-
const { hooks, ...pixiOpts } = opts;
|
|
591
|
+
const { hooks, infoMenu, ...pixiOpts } = opts;
|
|
283
592
|
const ui = createUI(spec, hooks);
|
|
593
|
+
const useInfoMenu = infoMenu !== false && pixiOpts.menu !== false;
|
|
284
594
|
const locales = spec.locale ? Array.from(/* @__PURE__ */ new Set([spec.locale.locale, ...Object.keys(spec.locale.messages)])) : [];
|
|
285
|
-
const menu = pixiOpts.menu === false ? false : composeMenu(spec.menu, { locales, localeSelectId: spec.localeSelectId, rulesFallback: spec.rules });
|
|
595
|
+
const menu = pixiOpts.menu === false || useInfoMenu ? false : composeMenu(spec.menu, { locales, localeSelectId: spec.localeSelectId, rulesFallback: spec.rules });
|
|
286
596
|
if (menu && spec.game && (spec.game.name || spec.game.version)) {
|
|
287
597
|
menu.push({ kind: "legal", id: "openui-game-info", text: [spec.game.name, spec.game.version ? `v${spec.game.version}` : ""].filter(Boolean).join(" \xB7 ") });
|
|
288
598
|
}
|
|
289
599
|
const pixi = new OpenUIPixi(ui, { ...pixiOpts, menu });
|
|
290
600
|
pixi.mount(app);
|
|
601
|
+
const disposeInfoMenu = useInfoMenu ? mountInfoMenu(app, ui) : void 0;
|
|
291
602
|
const extra = [];
|
|
292
603
|
if (spec.panels?.length) {
|
|
293
604
|
for (const ps of spec.panels) {
|
|
@@ -308,6 +619,7 @@ function mountHud(app, spec = {}, opts = {}) {
|
|
|
308
619
|
});
|
|
309
620
|
const teardown = () => {
|
|
310
621
|
offRelayout();
|
|
622
|
+
disposeInfoMenu?.();
|
|
311
623
|
for (const v of extra) v.dispose();
|
|
312
624
|
extra.length = 0;
|
|
313
625
|
pixi.unmount();
|
|
@@ -343,30 +655,10 @@ function mountHud(app, spec = {}, opts = {}) {
|
|
|
343
655
|
setReplay: (on2) => ui.setReplay(on2),
|
|
344
656
|
replayStart: (info, onPlay) => {
|
|
345
657
|
ui.setReplay(true);
|
|
346
|
-
|
|
347
|
-
const money = (n) => formatAmount(n, cur);
|
|
348
|
-
ui.showNotice(
|
|
349
|
-
[
|
|
350
|
-
{ kind: "heading", id: "openui-replay-h", text: "openui.replay.title" },
|
|
351
|
-
{
|
|
352
|
-
kind: "stat-grid",
|
|
353
|
-
id: "openui-replay-g",
|
|
354
|
-
items: [
|
|
355
|
-
{ label: "openui.replay.baseBet", value: money(info.baseBet) },
|
|
356
|
-
{ label: "openui.replay.costMultiplier", value: `${info.costMultiplier}\xD7` },
|
|
357
|
-
{ label: "openui.replay.payoutMultiplier", value: `${info.payoutMultiplier}\xD7` },
|
|
358
|
-
{ label: "openui.replay.amount", value: money(info.amount) }
|
|
359
|
-
]
|
|
360
|
-
}
|
|
361
|
-
],
|
|
362
|
-
[{ label: "openui.replay.play", variant: "primary", onSelect: onPlay }]
|
|
363
|
-
);
|
|
658
|
+
showReplayModal(ui, info, "openui.replay.play", () => onPlay?.());
|
|
364
659
|
},
|
|
365
|
-
replayEnd: (onReplay) => {
|
|
366
|
-
ui.
|
|
367
|
-
[{ kind: "heading", id: "openui-replay-end", text: "openui.replay.title" }],
|
|
368
|
-
[{ label: "openui.replay.again", variant: "primary", onSelect: onReplay }]
|
|
369
|
-
);
|
|
660
|
+
replayEnd: (info, onReplay) => {
|
|
661
|
+
showReplayModal(ui, info, "openui.replay.again", onReplay);
|
|
370
662
|
},
|
|
371
663
|
confirmBuy: (o) => {
|
|
372
664
|
if (ui.isDisabled("buyFeature")) return;
|
|
@@ -415,7 +707,7 @@ function mountHud(app, spec = {}, opts = {}) {
|
|
|
415
707
|
|
|
416
708
|
// src/bootError.ts
|
|
417
709
|
var host = null;
|
|
418
|
-
var
|
|
710
|
+
var esc2 = (s) => s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
419
711
|
function showBootError(opts = {}) {
|
|
420
712
|
const title = opts.title ?? "Connection lost";
|
|
421
713
|
const message = opts.message ?? "The game could not reach the game server. Please reload to reconnect and continue.";
|
|
@@ -429,10 +721,10 @@ function showBootError(opts = {}) {
|
|
|
429
721
|
<div class="openui-be-icon" aria-hidden="true">
|
|
430
722
|
<svg viewBox="0 0 48 48" width="48" height="48"><path d="M24 4 3 42h42L24 4Z" fill="none" stroke="#ffc935" stroke-width="3" stroke-linejoin="round"/><rect x="22" y="18" width="4" height="12" rx="2" fill="#ffc935"/><circle cx="24" cy="35" r="2.4" fill="#ffc935"/></svg>
|
|
431
723
|
</div>
|
|
432
|
-
<h1 class="openui-be-title">${
|
|
433
|
-
<p class="openui-be-msg">${
|
|
434
|
-
${opts.detail ? `<p class="openui-be-detail">${
|
|
435
|
-
${showReload ? `<button class="openui-be-reload" type="button">${
|
|
724
|
+
<h1 class="openui-be-title">${esc2(title)}</h1>
|
|
725
|
+
<p class="openui-be-msg">${esc2(message)}</p>
|
|
726
|
+
${opts.detail ? `<p class="openui-be-detail">${esc2(opts.detail)}</p>` : ""}
|
|
727
|
+
${showReload ? `<button class="openui-be-reload" type="button">${esc2(opts.reloadLabel ?? "Reload")}</button>` : ""}
|
|
436
728
|
</div>
|
|
437
729
|
<style>
|
|
438
730
|
.openui-boot-error { position: fixed; inset: 0; z-index: 2147483000; display: grid; place-items: center;
|
|
@@ -460,6 +752,6 @@ function hideBootError() {
|
|
|
460
752
|
host = null;
|
|
461
753
|
}
|
|
462
754
|
|
|
463
|
-
export { PanelBodyView, PanelView, PopoverView, hideBootError, mountHud, showBootError };
|
|
755
|
+
export { PanelBodyView, PanelView, PopoverView, hideBootError, mountHud, mountInfoMenu, showBootError };
|
|
464
756
|
//# sourceMappingURL=index.js.map
|
|
465
757
|
//# sourceMappingURL=index.js.map
|