@open-slot-ui/pixi 0.8.1 → 0.8.3
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 +265 -19
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +53 -1
- package/dist/index.d.ts +53 -1
- package/dist/index.js +264 -20
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.d.cts
CHANGED
|
@@ -735,6 +735,58 @@ declare function mountHud(app: Application, spec?: UISpec, opts?: HudOptions): B
|
|
|
735
735
|
*/
|
|
736
736
|
declare function mountInfoMenu(app: Application, ui: OpenUI): () => void;
|
|
737
737
|
|
|
738
|
+
/** A buy-feature card. */
|
|
739
|
+
interface FeatureSpec {
|
|
740
|
+
id: string;
|
|
741
|
+
name: string;
|
|
742
|
+
variant: 'buy' | 'boost';
|
|
743
|
+
/** Price as a multiple of the current bet (× bet). */
|
|
744
|
+
cost: number;
|
|
745
|
+
/** Card image URL (or data URI). Optional — a neutral gradient is used when absent. */
|
|
746
|
+
image?: string;
|
|
747
|
+
}
|
|
748
|
+
/** Configurable behavior of the activatable bet-boost cards. */
|
|
749
|
+
interface BuyFeatureOptions {
|
|
750
|
+
/** `'single'` → only one boost active at a time · `'multi'` → combine them. Default `'multi'`. */
|
|
751
|
+
activation?: 'single' | 'multi';
|
|
752
|
+
/** When any boost is active, disable the Buy buttons. Default `false`. */
|
|
753
|
+
activationBlocksBuy?: boolean;
|
|
754
|
+
/** Host hook for a confirmed Buy (the modal has just closed): deduct + start the feature. */
|
|
755
|
+
onBuy?: (id: string, cost: number) => void;
|
|
756
|
+
/** Host hook whenever the active-boost set changes (the modal stays open). */
|
|
757
|
+
onActivate?: (activeIds: string[], id: string, active: boolean) => void;
|
|
758
|
+
/** BASE bet source for pricing. Default reads ui.bet — pass this when ui.bet
|
|
759
|
+
* displays a modified EFFECTIVE stake (boost active) instead of the base bet. */
|
|
760
|
+
getBet?: () => number;
|
|
761
|
+
}
|
|
762
|
+
/** Mount the buy-feature modal. Returns a leak-free teardown. */
|
|
763
|
+
declare function mountBuyFeatureModal(_app: Application, hud: BootedHud, features: FeatureSpec[], opts?: BuyFeatureOptions): () => void;
|
|
764
|
+
|
|
765
|
+
/** Minimal HUD surface the confirm needs — the translator + theme font. */
|
|
766
|
+
interface ConfirmUI {
|
|
767
|
+
t(key: string, params?: Record<string, string | number>): string;
|
|
768
|
+
theme: {
|
|
769
|
+
type: {
|
|
770
|
+
family: string;
|
|
771
|
+
};
|
|
772
|
+
};
|
|
773
|
+
}
|
|
774
|
+
interface ConfirmOptions {
|
|
775
|
+
/** Heading (already resolved text, or an i18n key). Default `Buy Feature`. */
|
|
776
|
+
title?: string;
|
|
777
|
+
/** Body message (already resolved). */
|
|
778
|
+
message: string;
|
|
779
|
+
/** Confirm button label (resolved text or i18n key). Default `openui.confirm`. */
|
|
780
|
+
confirmLabel?: string;
|
|
781
|
+
/** Cancel button label. Default `openui.cancel`. */
|
|
782
|
+
cancelLabel?: string;
|
|
783
|
+
}
|
|
784
|
+
/**
|
|
785
|
+
* Show the universal confirm dialog. Resolves `true` on Confirm, `false` on Cancel / backdrop
|
|
786
|
+
* / Escape. Self-mounting + self-cleaning; layers above everything (z-index above the buy modal).
|
|
787
|
+
*/
|
|
788
|
+
declare function showConfirm(ui: ConfirmUI, opts: ConfirmOptions): Promise<boolean>;
|
|
789
|
+
|
|
738
790
|
interface BootErrorOptions {
|
|
739
791
|
title?: string;
|
|
740
792
|
message?: string;
|
|
@@ -817,4 +869,4 @@ declare class Tweener {
|
|
|
817
869
|
/** Treat touch devices as non-desktop so we don't fire phantom hover states. */
|
|
818
870
|
declare function isDesktop(): boolean;
|
|
819
871
|
|
|
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 };
|
|
872
|
+
export { AutoplayDrawerView, AutoplayView, type AutoplayViewOptions, type BootErrorOptions, type BootedHud, type ButtonGlyph, ButtonView, type ButtonViewOptions, type BuyFeatureOptions, type ConfirmOptions, ControlView, ControlViewFactory, DialogView, type DialogViewOptions, type FeatureSpec, 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, mountBuyFeatureModal, mountHud, mountInfoMenu, showBootError, showConfirm, svgSpinSkin };
|
package/dist/index.d.ts
CHANGED
|
@@ -735,6 +735,58 @@ declare function mountHud(app: Application, spec?: UISpec, opts?: HudOptions): B
|
|
|
735
735
|
*/
|
|
736
736
|
declare function mountInfoMenu(app: Application, ui: OpenUI): () => void;
|
|
737
737
|
|
|
738
|
+
/** A buy-feature card. */
|
|
739
|
+
interface FeatureSpec {
|
|
740
|
+
id: string;
|
|
741
|
+
name: string;
|
|
742
|
+
variant: 'buy' | 'boost';
|
|
743
|
+
/** Price as a multiple of the current bet (× bet). */
|
|
744
|
+
cost: number;
|
|
745
|
+
/** Card image URL (or data URI). Optional — a neutral gradient is used when absent. */
|
|
746
|
+
image?: string;
|
|
747
|
+
}
|
|
748
|
+
/** Configurable behavior of the activatable bet-boost cards. */
|
|
749
|
+
interface BuyFeatureOptions {
|
|
750
|
+
/** `'single'` → only one boost active at a time · `'multi'` → combine them. Default `'multi'`. */
|
|
751
|
+
activation?: 'single' | 'multi';
|
|
752
|
+
/** When any boost is active, disable the Buy buttons. Default `false`. */
|
|
753
|
+
activationBlocksBuy?: boolean;
|
|
754
|
+
/** Host hook for a confirmed Buy (the modal has just closed): deduct + start the feature. */
|
|
755
|
+
onBuy?: (id: string, cost: number) => void;
|
|
756
|
+
/** Host hook whenever the active-boost set changes (the modal stays open). */
|
|
757
|
+
onActivate?: (activeIds: string[], id: string, active: boolean) => void;
|
|
758
|
+
/** BASE bet source for pricing. Default reads ui.bet — pass this when ui.bet
|
|
759
|
+
* displays a modified EFFECTIVE stake (boost active) instead of the base bet. */
|
|
760
|
+
getBet?: () => number;
|
|
761
|
+
}
|
|
762
|
+
/** Mount the buy-feature modal. Returns a leak-free teardown. */
|
|
763
|
+
declare function mountBuyFeatureModal(_app: Application, hud: BootedHud, features: FeatureSpec[], opts?: BuyFeatureOptions): () => void;
|
|
764
|
+
|
|
765
|
+
/** Minimal HUD surface the confirm needs — the translator + theme font. */
|
|
766
|
+
interface ConfirmUI {
|
|
767
|
+
t(key: string, params?: Record<string, string | number>): string;
|
|
768
|
+
theme: {
|
|
769
|
+
type: {
|
|
770
|
+
family: string;
|
|
771
|
+
};
|
|
772
|
+
};
|
|
773
|
+
}
|
|
774
|
+
interface ConfirmOptions {
|
|
775
|
+
/** Heading (already resolved text, or an i18n key). Default `Buy Feature`. */
|
|
776
|
+
title?: string;
|
|
777
|
+
/** Body message (already resolved). */
|
|
778
|
+
message: string;
|
|
779
|
+
/** Confirm button label (resolved text or i18n key). Default `openui.confirm`. */
|
|
780
|
+
confirmLabel?: string;
|
|
781
|
+
/** Cancel button label. Default `openui.cancel`. */
|
|
782
|
+
cancelLabel?: string;
|
|
783
|
+
}
|
|
784
|
+
/**
|
|
785
|
+
* Show the universal confirm dialog. Resolves `true` on Confirm, `false` on Cancel / backdrop
|
|
786
|
+
* / Escape. Self-mounting + self-cleaning; layers above everything (z-index above the buy modal).
|
|
787
|
+
*/
|
|
788
|
+
declare function showConfirm(ui: ConfirmUI, opts: ConfirmOptions): Promise<boolean>;
|
|
789
|
+
|
|
738
790
|
interface BootErrorOptions {
|
|
739
791
|
title?: string;
|
|
740
792
|
message?: string;
|
|
@@ -817,4 +869,4 @@ declare class Tweener {
|
|
|
817
869
|
/** Treat touch devices as non-desktop so we don't fire phantom hover states. */
|
|
818
870
|
declare function isDesktop(): boolean;
|
|
819
871
|
|
|
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 };
|
|
872
|
+
export { AutoplayDrawerView, AutoplayView, type AutoplayViewOptions, type BootErrorOptions, type BootedHud, type ButtonGlyph, ButtonView, type ButtonViewOptions, type BuyFeatureOptions, type ConfirmOptions, ControlView, ControlViewFactory, DialogView, type DialogViewOptions, type FeatureSpec, 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, mountBuyFeatureModal, mountHud, mountInfoMenu, showBootError, showConfirm, svgSpinSkin };
|
package/dist/index.js
CHANGED
|
@@ -562,10 +562,67 @@ var OHM_CSS = `
|
|
|
562
562
|
.ohm-group { margin: 8px 0; }
|
|
563
563
|
`;
|
|
564
564
|
|
|
565
|
+
// src/confirmModal.ts
|
|
566
|
+
var esc2 = (s) => s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
567
|
+
function showConfirm(ui, opts) {
|
|
568
|
+
return new Promise((resolve) => {
|
|
569
|
+
const host2 = document.createElement("div");
|
|
570
|
+
host2.className = "osc-confirm";
|
|
571
|
+
host2.style.setProperty("--font", ui.theme.type.family);
|
|
572
|
+
const title = ui.t(opts.title ?? "Buy Feature");
|
|
573
|
+
const yes = ui.t(opts.confirmLabel ?? "openui.confirm");
|
|
574
|
+
const no = ui.t(opts.cancelLabel ?? "openui.cancel");
|
|
575
|
+
host2.innerHTML = `
|
|
576
|
+
<div class="osc-confirm-backdrop" data-no></div>
|
|
577
|
+
<div class="osc-confirm-card" role="dialog" aria-modal="true">
|
|
578
|
+
${title ? `<h3 class="osc-confirm-title">${esc2(title)}</h3>` : ""}
|
|
579
|
+
<p class="osc-confirm-msg">${esc2(opts.message)}</p>
|
|
580
|
+
<div class="osc-confirm-row">
|
|
581
|
+
<button class="osc-confirm-btn osc-confirm-no" data-no>${esc2(no)}</button>
|
|
582
|
+
<button class="osc-confirm-btn osc-confirm-yes" data-yes>${esc2(yes)}</button>
|
|
583
|
+
</div>
|
|
584
|
+
</div>`;
|
|
585
|
+
const style = document.createElement("style");
|
|
586
|
+
style.textContent = CONFIRM_CSS;
|
|
587
|
+
host2.appendChild(style);
|
|
588
|
+
document.body.appendChild(host2);
|
|
589
|
+
let settled = false;
|
|
590
|
+
const close = (val) => {
|
|
591
|
+
if (settled) return;
|
|
592
|
+
settled = true;
|
|
593
|
+
window.removeEventListener("keydown", onKey);
|
|
594
|
+
host2.remove();
|
|
595
|
+
resolve(val);
|
|
596
|
+
};
|
|
597
|
+
const onKey = (e) => {
|
|
598
|
+
if (e.key === "Escape") close(false);
|
|
599
|
+
else if (e.key === "Enter") close(true);
|
|
600
|
+
};
|
|
601
|
+
host2.querySelectorAll("[data-no]").forEach((b) => b.addEventListener("click", () => close(false)));
|
|
602
|
+
host2.querySelector("[data-yes]")?.addEventListener("click", () => close(true));
|
|
603
|
+
window.addEventListener("keydown", onKey);
|
|
604
|
+
});
|
|
605
|
+
}
|
|
606
|
+
var CONFIRM_CSS = `
|
|
607
|
+
.osc-confirm { position: fixed; inset: 0; z-index: 12000; display: grid; place-items: center; font-family: var(--font); }
|
|
608
|
+
.osc-confirm *, .osc-confirm *::before, .osc-confirm *::after { box-sizing: border-box; }
|
|
609
|
+
.osc-confirm-backdrop { position: absolute; inset: 0; background: rgba(8,6,4,.6); backdrop-filter: blur(4px); -webkit-backdrop-filter: blur(4px); }
|
|
610
|
+
.osc-confirm-card { position: relative; width: min(90vw, 420px); background: #ffffff; color: #181b20; border: 3px solid #000; border-radius: 14px; padding: 24px; box-shadow: 0 24px 60px rgba(0,0,0,.55); text-align: center; }
|
|
611
|
+
.osc-confirm-title { margin: 0 0 12px; font-size: 22px; font-weight: 800; letter-spacing: .3px; }
|
|
612
|
+
.osc-confirm-msg { margin: 0 0 22px; font-size: 19px; font-weight: 700; line-height: 1.4; }
|
|
613
|
+
.osc-confirm-row { display: flex; gap: 14px; }
|
|
614
|
+
.osc-confirm-btn { flex: 1; padding: 14px 10px; border-radius: 12px; border: 3px solid #000; font-size: 15px; font-weight: 800; letter-spacing: .5px; text-transform: uppercase; cursor: pointer; transition: transform .1s, background .12s; }
|
|
615
|
+
.osc-confirm-btn:active { transform: scale(.96); }
|
|
616
|
+
.osc-confirm-no { background: #ffffff; color: #181b20; }
|
|
617
|
+
.osc-confirm-no:hover { background: #eef1f6; }
|
|
618
|
+
.osc-confirm-yes { background: #d99000; color: #1a1200; }
|
|
619
|
+
.osc-confirm-yes:hover { filter: brightness(1.05); }
|
|
620
|
+
`;
|
|
621
|
+
|
|
565
622
|
// src/mountHud.ts
|
|
566
623
|
function showReplayModal(ui, info, buttonKey, onSelect) {
|
|
567
624
|
const cur = info.currency ?? ui.balance.currency.get();
|
|
568
|
-
const
|
|
625
|
+
const money2 = (n) => formatAmount(n, cur);
|
|
569
626
|
ui.showNotice(
|
|
570
627
|
[
|
|
571
628
|
{ kind: "heading", id: "openui-replay-h", text: "openui.replay.title" },
|
|
@@ -573,10 +630,10 @@ function showReplayModal(ui, info, buttonKey, onSelect) {
|
|
|
573
630
|
kind: "stat-grid",
|
|
574
631
|
id: "openui-replay-g",
|
|
575
632
|
items: [
|
|
576
|
-
{ label: "openui.replay.baseBet", value:
|
|
633
|
+
{ label: "openui.replay.baseBet", value: money2(info.baseBet) },
|
|
577
634
|
{ label: "openui.replay.costMultiplier", value: `${info.costMultiplier}\xD7` },
|
|
578
635
|
{ label: "openui.replay.payoutMultiplier", value: `${info.payoutMultiplier}\xD7` },
|
|
579
|
-
{ label: "openui.replay.amount", value:
|
|
636
|
+
{ label: "openui.replay.amount", value: money2(info.amount) }
|
|
580
637
|
]
|
|
581
638
|
}
|
|
582
639
|
],
|
|
@@ -680,17 +737,15 @@ function mountHud(app, spec = {}, opts = {}) {
|
|
|
680
737
|
o.onConfirm();
|
|
681
738
|
return;
|
|
682
739
|
}
|
|
683
|
-
const message = o.message ?? (o.name ? ui.t("openui.buyFeature.confirm", { name: ui.t(o.name), price: o.price ?? "" }) : "openui.buyFeature.message");
|
|
684
|
-
ui
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
]
|
|
693
|
-
);
|
|
740
|
+
const message = o.message ?? (o.name ? ui.t("openui.buyFeature.confirm", { name: ui.t(o.name), price: o.price ?? "" }) : ui.t("openui.buyFeature.message"));
|
|
741
|
+
void showConfirm(ui, {
|
|
742
|
+
title: o.title ?? "openui.buyFeature.title",
|
|
743
|
+
message,
|
|
744
|
+
confirmLabel: o.confirmLabel,
|
|
745
|
+
cancelLabel: o.cancelLabel
|
|
746
|
+
}).then((ok) => {
|
|
747
|
+
if (ok) o.onConfirm();
|
|
748
|
+
});
|
|
694
749
|
},
|
|
695
750
|
showControls: () => pixi.setControlsVisible(true),
|
|
696
751
|
hideControls: () => pixi.setControlsVisible(false),
|
|
@@ -704,10 +759,199 @@ function mountHud(app, spec = {}, opts = {}) {
|
|
|
704
759
|
dispose: teardown
|
|
705
760
|
};
|
|
706
761
|
}
|
|
762
|
+
function money(amount, cur) {
|
|
763
|
+
return formatAmount(amount, cur);
|
|
764
|
+
}
|
|
765
|
+
var esc3 = (s) => s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
766
|
+
function mountBuyFeatureModal(_app, hud, features, opts = {}) {
|
|
767
|
+
const ui = hud.ui;
|
|
768
|
+
const tr = (k) => ui.t(k);
|
|
769
|
+
const list = features.slice(0, 4);
|
|
770
|
+
const boosts = /* @__PURE__ */ new Set();
|
|
771
|
+
const activation = opts.activation ?? "multi";
|
|
772
|
+
const blocksBuy = opts.activationBlocksBuy ?? false;
|
|
773
|
+
const disposers = [];
|
|
774
|
+
const host2 = document.createElement("div");
|
|
775
|
+
host2.className = "bfm-root";
|
|
776
|
+
const vars = {
|
|
777
|
+
"--accent": "#d99000",
|
|
778
|
+
"--accent-text": "#1a1200",
|
|
779
|
+
"--surface": "#ffffff",
|
|
780
|
+
"--surface-alt": "#eef1f6",
|
|
781
|
+
"--text": "#181b20",
|
|
782
|
+
"--text-dim": "#5b6472",
|
|
783
|
+
"--font": ui.theme.type.family
|
|
784
|
+
};
|
|
785
|
+
for (const [k, v] of Object.entries(vars)) host2.style.setProperty(k, v);
|
|
786
|
+
host2.innerHTML = `
|
|
787
|
+
<div class="bfm-backdrop" data-close></div>
|
|
788
|
+
<button class="bfm-x" data-close aria-label="Close">\u2715</button>
|
|
789
|
+
<div class="bfm-panel" role="dialog" aria-modal="true">
|
|
790
|
+
<div class="bfm-fit" id="bfm-fit">
|
|
791
|
+
<h2 class="bfm-title" data-t="Buy Feature">${esc3(tr("Buy Feature"))}</h2>
|
|
792
|
+
<div class="bfm-bet">
|
|
793
|
+
<button class="bfm-step" id="bfm-minus" aria-label="Decrease">\u2212</button>
|
|
794
|
+
<div class="bfm-betbox"><span class="bfm-betlabel" data-t="Bet">${esc3(tr("Bet"))}</span><b id="bfm-betval">\u2014</b></div>
|
|
795
|
+
<button class="bfm-step" id="bfm-plus" aria-label="Increase">+</button>
|
|
796
|
+
</div>
|
|
797
|
+
<div class="bfm-cards" id="bfm-cards"></div>
|
|
798
|
+
</div>
|
|
799
|
+
</div>`;
|
|
800
|
+
const style = document.createElement("style");
|
|
801
|
+
style.textContent = BFM_CSS;
|
|
802
|
+
host2.appendChild(style);
|
|
803
|
+
document.body.appendChild(host2);
|
|
804
|
+
const $ = (sel) => host2.querySelector(sel);
|
|
805
|
+
const panel = $(".bfm-panel");
|
|
806
|
+
const fitEl = $("#bfm-fit");
|
|
807
|
+
const cardsEl = $("#bfm-cards");
|
|
808
|
+
const betValEl = $("#bfm-betval");
|
|
809
|
+
const askConfirm = (name, price, onYes) => {
|
|
810
|
+
const message = ui.t("openui.buyFeature.confirm", { name: ui.t(name), price });
|
|
811
|
+
void showConfirm(ui, { title: "Buy Feature", message }).then((ok) => {
|
|
812
|
+
if (ok) onYes();
|
|
813
|
+
});
|
|
814
|
+
};
|
|
815
|
+
const layout = () => {
|
|
816
|
+
const vw = window.innerWidth;
|
|
817
|
+
const vh = window.innerHeight;
|
|
818
|
+
const cols = vh <= 540 || vw >= 900 ? Math.min(4, list.length) : Math.min(2, list.length);
|
|
819
|
+
cardsEl.style.gridTemplateColumns = `repeat(${cols}, 1fr)`;
|
|
820
|
+
const fitW = Math.min(vw * 0.96, cols >= 4 ? 1180 : cols === 1 ? 380 : 760);
|
|
821
|
+
fitEl.style.width = `${fitW}px`;
|
|
822
|
+
fitEl.style.transform = "none";
|
|
823
|
+
const natH = fitEl.offsetHeight;
|
|
824
|
+
const s = Math.min(1, vh * 0.95 / natH);
|
|
825
|
+
fitEl.style.transform = `translateX(-50%) scale(${s})`;
|
|
826
|
+
panel.style.width = `${Math.ceil(fitW * s)}px`;
|
|
827
|
+
panel.style.height = `${Math.ceil(natH * s)}px`;
|
|
828
|
+
};
|
|
829
|
+
const renderCards = () => {
|
|
830
|
+
const bet = opts.getBet ? opts.getBet() : ui.bet.get();
|
|
831
|
+
const cur = ui.bet.currency.get();
|
|
832
|
+
cardsEl.innerHTML = list.map((f) => {
|
|
833
|
+
const active = boosts.has(f.id);
|
|
834
|
+
const buyBlocked = f.variant === "buy" && blocksBuy && boosts.size > 0;
|
|
835
|
+
const price = f.variant === "buy" ? money(f.cost * bet, cur) : `+${money(f.cost * bet, cur)}`;
|
|
836
|
+
const label = f.variant === "buy" ? tr("Buy") : active ? tr("Activated") : tr("Activate");
|
|
837
|
+
const cls = `bfm-action bfm-action--${f.variant}${active ? " is-active" : ""}${buyBlocked ? " is-blocked" : ""}`;
|
|
838
|
+
const img = f.image ? ` style="background-image:url('${f.image}')"` : "";
|
|
839
|
+
return `
|
|
840
|
+
<div class="bfm-cell">
|
|
841
|
+
<div class="bfm-card">
|
|
842
|
+
<div class="bfm-cardimg"${img}></div>
|
|
843
|
+
<div class="bfm-strip"></div>
|
|
844
|
+
<div class="bfm-cardbody">
|
|
845
|
+
<span class="bfm-name">${esc3(tr(f.name))}</span>
|
|
846
|
+
<b class="bfm-price">${esc3(price)}</b>
|
|
847
|
+
</div>
|
|
848
|
+
</div>
|
|
849
|
+
<button class="${cls}" data-id="${f.id}" data-variant="${f.variant}"${buyBlocked ? " disabled" : ""}>${esc3(label)}</button>
|
|
850
|
+
</div>`;
|
|
851
|
+
}).join("");
|
|
852
|
+
betValEl.textContent = money(bet, cur);
|
|
853
|
+
cardsEl.querySelectorAll(".bfm-action").forEach((b) => {
|
|
854
|
+
b.addEventListener("click", () => onAction(b.dataset.id, b.dataset.variant));
|
|
855
|
+
});
|
|
856
|
+
layout();
|
|
857
|
+
};
|
|
858
|
+
const onAction = (id, variant) => {
|
|
859
|
+
const f = list.find((x) => x.id === id);
|
|
860
|
+
const bet = opts.getBet ? opts.getBet() : ui.bet.get();
|
|
861
|
+
const cur = ui.bet.currency.get();
|
|
862
|
+
if (variant === "boost") {
|
|
863
|
+
const wasActive = boosts.has(id);
|
|
864
|
+
const commit = () => {
|
|
865
|
+
if (activation === "single") boosts.clear();
|
|
866
|
+
if (wasActive) boosts.delete(id);
|
|
867
|
+
else boosts.add(id);
|
|
868
|
+
ui.bus.emit("cardActivated", { id });
|
|
869
|
+
renderCards();
|
|
870
|
+
opts.onActivate?.([...boosts], id, boosts.has(id));
|
|
871
|
+
};
|
|
872
|
+
if (wasActive) {
|
|
873
|
+
commit();
|
|
874
|
+
return;
|
|
875
|
+
}
|
|
876
|
+
const total = 1 + (f?.cost ?? 0);
|
|
877
|
+
askConfirm(f?.name ?? id, money(total * bet, cur), commit);
|
|
878
|
+
} else {
|
|
879
|
+
if (blocksBuy && boosts.size > 0) return;
|
|
880
|
+
const cost = (f?.cost ?? 0) * bet;
|
|
881
|
+
askConfirm(f?.name ?? id, money(cost, cur), () => {
|
|
882
|
+
ui.bus.emit("cardActivated", { id });
|
|
883
|
+
close();
|
|
884
|
+
opts.onBuy?.(id, cost);
|
|
885
|
+
});
|
|
886
|
+
}
|
|
887
|
+
};
|
|
888
|
+
$("#bfm-minus").addEventListener("click", () => ui.betStepper.dec());
|
|
889
|
+
$("#bfm-plus").addEventListener("click", () => ui.betStepper.inc());
|
|
890
|
+
disposers.push(ui.bet.value.subscribe(() => renderCards()));
|
|
891
|
+
const open = () => {
|
|
892
|
+
renderCards();
|
|
893
|
+
host2.classList.add("open");
|
|
894
|
+
};
|
|
895
|
+
const close = () => host2.classList.remove("open");
|
|
896
|
+
host2.querySelectorAll("[data-close]").forEach((b) => b.addEventListener("click", close));
|
|
897
|
+
disposers.push(ui.on("buttonActivated", ({ id }) => {
|
|
898
|
+
if (id === "bonus") open();
|
|
899
|
+
}));
|
|
900
|
+
const onResize = () => {
|
|
901
|
+
if (host2.classList.contains("open")) layout();
|
|
902
|
+
};
|
|
903
|
+
window.addEventListener("resize", onResize);
|
|
904
|
+
disposers.push(
|
|
905
|
+
ui.locale.subscribe(() => {
|
|
906
|
+
host2.querySelectorAll("[data-t]").forEach((n) => n.textContent = tr(n.dataset.t));
|
|
907
|
+
renderCards();
|
|
908
|
+
})
|
|
909
|
+
);
|
|
910
|
+
renderCards();
|
|
911
|
+
return () => {
|
|
912
|
+
window.removeEventListener("resize", onResize);
|
|
913
|
+
for (const d of disposers.splice(0)) d();
|
|
914
|
+
host2.remove();
|
|
915
|
+
};
|
|
916
|
+
}
|
|
917
|
+
var BFM_CSS = `
|
|
918
|
+
.bfm-root { position: fixed; inset: 0; z-index: 11000; display: grid; place-items: center; font-family: var(--font); opacity: 0; pointer-events: none; transition: opacity .18s ease; }
|
|
919
|
+
.bfm-root.open { opacity: 1; pointer-events: auto; }
|
|
920
|
+
.bfm-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; }
|
|
921
|
+
.bfm-root.open .bfm-backdrop { background: rgba(8,6,4,.5); backdrop-filter: blur(10px) saturate(1.1); -webkit-backdrop-filter: blur(10px) saturate(1.1); }
|
|
922
|
+
.bfm-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; }
|
|
923
|
+
.bfm-x:hover { transform: scale(1.08); background: rgba(18,14,10,.95); }
|
|
924
|
+
.bfm-root *, .bfm-root *::before, .bfm-root *::after { box-sizing: border-box; }
|
|
925
|
+
.bfm-panel { position: relative; transform: translateY(8px) scale(.985); transition: transform .18s ease; }
|
|
926
|
+
.bfm-root.open .bfm-panel { transform: none; }
|
|
927
|
+
.bfm-fit { position: absolute; top: 0; left: 50%; transform: translateX(-50%); transform-origin: top center; }
|
|
928
|
+
.bfm-title { margin: 0 0 14px; text-align: center; color: #fff; font-size: 30px; font-weight: 800; letter-spacing: 1px; text-shadow: 0 2px 12px rgba(0,0,0,.6); }
|
|
929
|
+
.bfm-bet { display: flex; align-items: center; justify-content: center; gap: 16px; margin: 0 0 24px; }
|
|
930
|
+
.bfm-betbox { min-width: 200px; padding: 10px 22px; border-radius: 12px; background: var(--surface); border: 3px solid #000; display: flex; flex-direction: column; align-items: center; line-height: 1.1; }
|
|
931
|
+
.bfm-betlabel { font-size: 12px; font-weight: 700; color: var(--text-dim); text-transform: uppercase; letter-spacing: 1px; }
|
|
932
|
+
.bfm-betbox b { font-size: 24px; color: var(--text); }
|
|
933
|
+
.bfm-step { flex: none; width: 54px; height: 54px; border-radius: 999px; border: 3px solid #000; background: var(--surface); color: var(--text); font-size: 28px; font-weight: 800; cursor: pointer; display: grid; place-items: center; line-height: 1; transition: transform .1s, background .12s; box-shadow: 0 4px 12px rgba(0,0,0,.3); }
|
|
934
|
+
.bfm-step:hover { background: var(--surface-alt); }
|
|
935
|
+
.bfm-step:active { transform: scale(.92); }
|
|
936
|
+
.bfm-cards { display: grid; grid-template-columns: repeat(4, 1fr); gap: 18px; align-items: start; }
|
|
937
|
+
.bfm-cell { display: flex; flex-direction: column; min-width: 0; }
|
|
938
|
+
.bfm-card { background: var(--surface); border: 4px solid #000; border-radius: 14px; overflow: hidden; box-shadow: 0 14px 34px rgba(0,0,0,.45); }
|
|
939
|
+
.bfm-cardimg { width: 100%; aspect-ratio: 16 / 10; background-size: cover; background-position: center; background-color: var(--surface-alt); background-image: linear-gradient(135deg, #e7ebf2, #cfd6e2); }
|
|
940
|
+
.bfm-strip { height: 8px; background: linear-gradient(90deg, #f0a500, #ffd166, #f0a500); }
|
|
941
|
+
.bfm-cardbody { padding: 12px 14px 16px; text-align: center; }
|
|
942
|
+
.bfm-name { display: block; font-size: 15px; font-weight: 600; color: var(--text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
943
|
+
.bfm-price { display: block; margin-top: 2px; font-size: 22px; font-weight: 800; color: var(--text); }
|
|
944
|
+
.bfm-action { display: block; width: 100%; margin-top: 12px; padding: 14px 10px; border-radius: 12px; border: 4px solid #000; background: var(--surface); color: var(--text); font-size: 15px; font-weight: 800; letter-spacing: .5px; text-transform: uppercase; cursor: pointer; transition: transform .1s, background .12s, color .12s; box-shadow: 0 5px 14px rgba(0,0,0,.35); white-space: nowrap; }
|
|
945
|
+
.bfm-action:hover { background: var(--surface-alt); }
|
|
946
|
+
.bfm-action:active { transform: scale(.96); }
|
|
947
|
+
.bfm-action.is-active { background: var(--accent); color: var(--accent-text); border-color: #000; }
|
|
948
|
+
.bfm-action.is-blocked, .bfm-action:disabled { opacity: .38; cursor: not-allowed; box-shadow: none; }
|
|
949
|
+
.bfm-action.is-blocked:hover, .bfm-action:disabled:hover { background: var(--surface); }
|
|
950
|
+
`;
|
|
707
951
|
|
|
708
952
|
// src/bootError.ts
|
|
709
953
|
var host = null;
|
|
710
|
-
var
|
|
954
|
+
var esc4 = (s) => s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
711
955
|
function showBootError(opts = {}) {
|
|
712
956
|
const title = opts.title ?? "Connection lost";
|
|
713
957
|
const message = opts.message ?? "The game could not reach the game server. Please reload to reconnect and continue.";
|
|
@@ -721,10 +965,10 @@ function showBootError(opts = {}) {
|
|
|
721
965
|
<div class="openui-be-icon" aria-hidden="true">
|
|
722
966
|
<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>
|
|
723
967
|
</div>
|
|
724
|
-
<h1 class="openui-be-title">${
|
|
725
|
-
<p class="openui-be-msg">${
|
|
726
|
-
${opts.detail ? `<p class="openui-be-detail">${
|
|
727
|
-
${showReload ? `<button class="openui-be-reload" type="button">${
|
|
968
|
+
<h1 class="openui-be-title">${esc4(title)}</h1>
|
|
969
|
+
<p class="openui-be-msg">${esc4(message)}</p>
|
|
970
|
+
${opts.detail ? `<p class="openui-be-detail">${esc4(opts.detail)}</p>` : ""}
|
|
971
|
+
${showReload ? `<button class="openui-be-reload" type="button">${esc4(opts.reloadLabel ?? "Reload")}</button>` : ""}
|
|
728
972
|
</div>
|
|
729
973
|
<style>
|
|
730
974
|
.openui-boot-error { position: fixed; inset: 0; z-index: 2147483000; display: grid; place-items: center;
|
|
@@ -752,6 +996,6 @@ function hideBootError() {
|
|
|
752
996
|
host = null;
|
|
753
997
|
}
|
|
754
998
|
|
|
755
|
-
export { PanelBodyView, PanelView, PopoverView, hideBootError, mountHud, mountInfoMenu, showBootError };
|
|
999
|
+
export { PanelBodyView, PanelView, PopoverView, hideBootError, mountBuyFeatureModal, mountHud, mountInfoMenu, showBootError, showConfirm };
|
|
756
1000
|
//# sourceMappingURL=index.js.map
|
|
757
1001
|
//# sourceMappingURL=index.js.map
|