@energy8platform/shell 0.3.0 → 0.4.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/html.cjs.js +55 -7
- package/dist/html.cjs.js.map +1 -1
- package/dist/html.d.ts +2 -2
- package/dist/html.esm.js +55 -7
- package/dist/html.esm.js.map +1 -1
- package/dist/index.cjs.js +26 -3
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.esm.js +26 -3
- package/dist/index.esm.js.map +1 -1
- package/dist/pixi.cjs.js +138 -30
- package/dist/pixi.cjs.js.map +1 -1
- package/dist/pixi.d.ts +2 -2
- package/dist/pixi.esm.js +138 -30
- package/dist/pixi.esm.js.map +1 -1
- package/package.json +1 -1
- package/src/core/locales.ts +26 -2
- package/src/core/version.ts +1 -1
- package/src/ui/html/components/pickers.ts +13 -1
- package/src/ui/html/shell.css.ts +19 -3
- package/src/ui/pixi/components/GameInfo.ts +52 -13
- package/src/ui/pixi/components/pickers.ts +66 -15
package/dist/pixi.d.ts
CHANGED
|
@@ -473,7 +473,7 @@ declare class ShellController extends EventEmitter<ShellEvents> implements Shell
|
|
|
473
473
|
tokens: ShellTokens;
|
|
474
474
|
layout: ShellLayoutMode;
|
|
475
475
|
soundOn: boolean;
|
|
476
|
-
readonly engineVersion = "0.
|
|
476
|
+
readonly engineVersion = "0.4.1";
|
|
477
477
|
readonly actions: ShellActions;
|
|
478
478
|
private renderer;
|
|
479
479
|
private i18n;
|
|
@@ -539,7 +539,7 @@ interface I18n {
|
|
|
539
539
|
declare function createI18n(opts: I18nOptions): I18n;
|
|
540
540
|
|
|
541
541
|
/** The @energy8platform/shell package version, stamped into the game-info footer. */
|
|
542
|
-
declare const PACKAGE_VERSION = "0.
|
|
542
|
+
declare const PACKAGE_VERSION = "0.4.1";
|
|
543
543
|
|
|
544
544
|
/** A shell: the renderer-agnostic controller plus the surface facade (safeArea/barHeight/setVisible)
|
|
545
545
|
* an embedding host reads. `createShell`, `createGameShell` and `createPixiShell` all return this. */
|
package/dist/pixi.esm.js
CHANGED
|
@@ -1166,8 +1166,31 @@ const DISCLAIMER_LOCALES = {
|
|
|
1166
1166
|
[D.L5]: '奖金根据从 Remote Game Server 收到的金额结算,而非依据网页浏览器内的事件。',
|
|
1167
1167
|
},
|
|
1168
1168
|
};
|
|
1169
|
-
|
|
1170
|
-
|
|
1169
|
+
// ── Replay summary labels ──────────────────────────────────────────────────────────────────────
|
|
1170
|
+
// The replay modal (html + pixi) renders these row labels via host.t(); grouped here (merged into
|
|
1171
|
+
// LOCALES below) so the whole replay screen localizes. Terminology matches the base Bet/Win keys.
|
|
1172
|
+
const REPLAY_LOCALES = {
|
|
1173
|
+
da: { Mode: 'Tilstand', 'Base bet': 'Grundindsats', 'Cost multiplier': 'Omkostningsmultiplikator', 'Total cost': 'Samlede omkostninger', 'Win multiplier': 'Gevinstmultiplikator' },
|
|
1174
|
+
de: { Mode: 'Modus', 'Base bet': 'Grundeinsatz', 'Cost multiplier': 'Kostenmultiplikator', 'Total cost': 'Gesamtkosten', 'Win multiplier': 'Gewinnmultiplikator' },
|
|
1175
|
+
es: { Mode: 'Modo', 'Base bet': 'Apuesta base', 'Cost multiplier': 'Multiplicador de coste', 'Total cost': 'Coste total', 'Win multiplier': 'Multiplicador de premio' },
|
|
1176
|
+
fi: { Mode: 'Tila', 'Base bet': 'Peruspanos', 'Cost multiplier': 'Kustannuskerroin', 'Total cost': 'Kokonaiskustannus', 'Win multiplier': 'Voittokerroin' },
|
|
1177
|
+
fr: { Mode: 'Mode', 'Base bet': 'Mise de base', 'Cost multiplier': 'Multiplicateur de coût', 'Total cost': 'Coût total', 'Win multiplier': 'Multiplicateur de gain' },
|
|
1178
|
+
hi: { Mode: 'मोड', 'Base bet': 'आधार दांव', 'Cost multiplier': 'लागत गुणक', 'Total cost': 'कुल लागत', 'Win multiplier': 'जीत गुणक' },
|
|
1179
|
+
id: { Mode: 'Mode', 'Base bet': 'Taruhan dasar', 'Cost multiplier': 'Pengganda biaya', 'Total cost': 'Total biaya', 'Win multiplier': 'Pengganda kemenangan' },
|
|
1180
|
+
ja: { Mode: 'モード', 'Base bet': '基本ベット', 'Cost multiplier': 'コスト倍率', 'Total cost': '合計コスト', 'Win multiplier': '当選倍率' },
|
|
1181
|
+
ko: { Mode: '모드', 'Base bet': '기본 베팅', 'Cost multiplier': '비용 배수', 'Total cost': '총 비용', 'Win multiplier': '당첨 배수' },
|
|
1182
|
+
pl: { Mode: 'Tryb', 'Base bet': 'Zakład podstawowy', 'Cost multiplier': 'Mnożnik kosztu', 'Total cost': 'Całkowity koszt', 'Win multiplier': 'Mnożnik wygranej' },
|
|
1183
|
+
pt: { Mode: 'Modo', 'Base bet': 'Aposta base', 'Cost multiplier': 'Multiplicador de custo', 'Total cost': 'Custo total', 'Win multiplier': 'Multiplicador de ganho' },
|
|
1184
|
+
ru: { Mode: 'Режим', 'Base bet': 'Базовая ставка', 'Cost multiplier': 'Множитель стоимости', 'Total cost': 'Общая стоимость', 'Win multiplier': 'Множитель выигрыша' },
|
|
1185
|
+
tr: { Mode: 'Mod', 'Base bet': 'Temel bahis', 'Cost multiplier': 'Maliyet çarpanı', 'Total cost': 'Toplam maliyet', 'Win multiplier': 'Kazanç çarpanı' },
|
|
1186
|
+
vi: { Mode: 'Chế độ', 'Base bet': 'Cược cơ bản', 'Cost multiplier': 'Hệ số chi phí', 'Total cost': 'Tổng chi phí', 'Win multiplier': 'Hệ số thắng' },
|
|
1187
|
+
zh: { Mode: '模式', 'Base bet': '基础投注', 'Cost multiplier': '成本倍数', 'Total cost': '总成本', 'Win multiplier': '奖金倍数' },
|
|
1188
|
+
};
|
|
1189
|
+
/** Merge the base UI catalog with the separately-authored disclaimer + replay lines, per language. */
|
|
1190
|
+
const LOCALES = Object.fromEntries(Object.keys(BASE_LOCALES).map((l) => [
|
|
1191
|
+
l,
|
|
1192
|
+
{ ...BASE_LOCALES[l], ...DISCLAIMER_LOCALES[l], ...REPLAY_LOCALES[l] },
|
|
1193
|
+
]));
|
|
1171
1194
|
|
|
1172
1195
|
// Social-casino language. English is the source (and, for now, the only) language; `socialize`
|
|
1173
1196
|
// rewrites the restricted gambling vocabulary into social-safe phrasing while preserving case.
|
|
@@ -1414,7 +1437,7 @@ class KeyboardController {
|
|
|
1414
1437
|
|
|
1415
1438
|
// AUTO-GENERATED by scripts/gen-version.mjs — do not edit. Mirrors package.json "version".
|
|
1416
1439
|
/** The @energy8platform/shell package version, stamped into the game-info footer. */
|
|
1417
|
-
const PACKAGE_VERSION = '0.
|
|
1440
|
+
const PACKAGE_VERSION = '0.4.1';
|
|
1418
1441
|
|
|
1419
1442
|
/** Apply defaults to the raw config (the mount target lives on the renderer, not here). */
|
|
1420
1443
|
function resolveConfig(config) {
|
|
@@ -4056,28 +4079,69 @@ function sectionHotkeys(host, title, width) {
|
|
|
4056
4079
|
visible.forEach((r, i) => {
|
|
4057
4080
|
if (i > 0)
|
|
4058
4081
|
sec.add(hairline$1(host, inner));
|
|
4059
|
-
sec.add(hkRow(host, r));
|
|
4082
|
+
sec.add(hkRow(host, r, inner));
|
|
4060
4083
|
});
|
|
4061
4084
|
return sec;
|
|
4062
4085
|
}
|
|
4063
|
-
/** Render a single hotkey row: keycap chip(s) on the left, action name on the right.
|
|
4086
|
+
/** Render a single hotkey row: keycap chip(s) on the left, action name on the right. The row is
|
|
4087
|
+
* constrained to `inner` (chips left, label right). On narrow widths the chip combos stack onto
|
|
4088
|
+
* their own lines and the label wraps within the leftover width, so nothing clips past the plaque
|
|
4089
|
+
* — the Pixi analogue of the HTML `.ge-gi-hk-chips { flex:0 1 auto; flex-wrap }` + label wrap. */
|
|
4064
4090
|
function hkRow(host, r, inner) {
|
|
4065
|
-
const
|
|
4066
|
-
//
|
|
4067
|
-
const chipsCol = new FlexBox({ direction: 'row', align: 'center', gap: 4 });
|
|
4091
|
+
const GAP = 14;
|
|
4092
|
+
const LABEL_MIN = 92; // reserve at least this much for the action label before wrapping the chips
|
|
4068
4093
|
const chipKeys = buildChipKeys(r);
|
|
4094
|
+
// Lay the chips on one line and measure. If that leaves too little room for the label, stack the
|
|
4095
|
+
// combos (split at the '/') onto their own lines so the chips give up horizontal width.
|
|
4096
|
+
let chips = chipLine(host, chipKeys);
|
|
4097
|
+
let chipsW = chips.measureSize().w;
|
|
4098
|
+
if (chipsW > inner - GAP - LABEL_MIN) {
|
|
4099
|
+
chips = chipCombos(host, chipKeys);
|
|
4100
|
+
chipsW = chips.measureSize().w;
|
|
4101
|
+
}
|
|
4102
|
+
// The label takes the leftover width and WRAPS within it (never clips); right-aligned to sit
|
|
4103
|
+
// against the plaque edge like the DOM `.ge-gi-hk-tx`.
|
|
4104
|
+
const labelW = Math.max(LABEL_MIN, inner - chipsW - GAP);
|
|
4105
|
+
const nameText = makeText(host.t(r.name), { size: 15, weight: '700', color: '#ffffff', wrapWidth: labelW, align: 'right' });
|
|
4106
|
+
const row = new FlexBox({ direction: 'row', align: 'center', justify: 'space-between', gap: GAP, padding: { top: 8, bottom: 8 }, width: inner });
|
|
4107
|
+
row.add(chips);
|
|
4108
|
+
row.add(nameText);
|
|
4109
|
+
return row;
|
|
4110
|
+
}
|
|
4111
|
+
/** Chips on a single row, with '/' separators between the combos (as flattened by buildChipKeys). */
|
|
4112
|
+
function chipLine(host, chipKeys) {
|
|
4113
|
+
const line = new FlexBox({ direction: 'row', align: 'center', gap: 4 });
|
|
4114
|
+
for (const key of chipKeys) {
|
|
4115
|
+
if (key === '/')
|
|
4116
|
+
line.add(makeText('/', { size: 12, weight: '500', color: host.tokens.plaqueLabel }));
|
|
4117
|
+
else
|
|
4118
|
+
line.add(keycap(host, key));
|
|
4119
|
+
}
|
|
4120
|
+
return line;
|
|
4121
|
+
}
|
|
4122
|
+
/** Chips stacked as combos — split at the '/' separators, one combo per line (drops the inline
|
|
4123
|
+
* '/', which reads as a line break once the combos are stacked). */
|
|
4124
|
+
function chipCombos(host, chipKeys) {
|
|
4125
|
+
const col = new FlexBox({ direction: 'column', align: 'start', gap: 4 });
|
|
4126
|
+
let line = new FlexBox({ direction: 'row', align: 'center', gap: 4 });
|
|
4127
|
+
let filled = false;
|
|
4128
|
+
const flush = () => {
|
|
4129
|
+
if (!filled)
|
|
4130
|
+
return;
|
|
4131
|
+
col.add(line);
|
|
4132
|
+
line = new FlexBox({ direction: 'row', align: 'center', gap: 4 });
|
|
4133
|
+
filled = false;
|
|
4134
|
+
};
|
|
4069
4135
|
for (const key of chipKeys) {
|
|
4070
4136
|
if (key === '/') {
|
|
4071
|
-
|
|
4072
|
-
|
|
4073
|
-
else {
|
|
4074
|
-
chipsCol.add(keycap(host, key));
|
|
4137
|
+
flush();
|
|
4138
|
+
continue;
|
|
4075
4139
|
}
|
|
4140
|
+
line.add(keycap(host, key));
|
|
4141
|
+
filled = true;
|
|
4076
4142
|
}
|
|
4077
|
-
|
|
4078
|
-
|
|
4079
|
-
row.add(nameText);
|
|
4080
|
-
return row;
|
|
4143
|
+
flush();
|
|
4144
|
+
return col;
|
|
4081
4145
|
}
|
|
4082
4146
|
/** Flatten a row's chip list into display tokens — for bet rows, produce: Shift ↑ / Shift = style. */
|
|
4083
4147
|
function buildChipKeys(r) {
|
|
@@ -5243,8 +5307,27 @@ class PickerModal extends CardModal {
|
|
|
5243
5307
|
}
|
|
5244
5308
|
}
|
|
5245
5309
|
/** A centred picker (chips grid + accent Confirm) on the shared card modal. */
|
|
5310
|
+
/** A ScrollBox that reports a fixed box to the FlexBox layout — so a capped, scrolling grid slots
|
|
5311
|
+
* into the card body like any other sized child (measured/positioned by its viewport, not its
|
|
5312
|
+
* full content bounds). */
|
|
5313
|
+
class SizedScrollBox extends ScrollBox {
|
|
5314
|
+
boxW;
|
|
5315
|
+
boxH;
|
|
5316
|
+
constructor(boxW, boxH, canvas) {
|
|
5317
|
+
super(canvas);
|
|
5318
|
+
this.boxW = boxW;
|
|
5319
|
+
this.boxH = boxH;
|
|
5320
|
+
this.setViewport(boxW, boxH);
|
|
5321
|
+
}
|
|
5322
|
+
measureSize() {
|
|
5323
|
+
return { w: this.boxW, h: this.boxH };
|
|
5324
|
+
}
|
|
5325
|
+
setLayoutSize() {
|
|
5326
|
+
/* fixed viewport — ignore layout-imposed sizing */
|
|
5327
|
+
}
|
|
5328
|
+
}
|
|
5246
5329
|
function buildSheet(host, opts) {
|
|
5247
|
-
const
|
|
5330
|
+
const maxColumns = typeof opts.columns === 'number'
|
|
5248
5331
|
? opts.columns
|
|
5249
5332
|
: host.layout === 'mobile' ? opts.columns.mobile : opts.columns.wide;
|
|
5250
5333
|
let selected = opts.selected;
|
|
@@ -5296,27 +5379,51 @@ function buildSheet(host, opts) {
|
|
|
5296
5379
|
const em = modal.emSize;
|
|
5297
5380
|
const gap = 0.65 * em;
|
|
5298
5381
|
const innerW = modal.cardWidth - 2.4 * em;
|
|
5382
|
+
// Build the chips first (at natural width) so autoFit can measure the widest label.
|
|
5383
|
+
for (let i = 0; i < opts.choices.length; i++) {
|
|
5384
|
+
const c = opts.choices[i];
|
|
5385
|
+
const chipIndex = i;
|
|
5386
|
+
chips.push(new Chip(host, c.id, c.label, chipIndex === focusIndex, em, (id) => {
|
|
5387
|
+
const idx = opts.choices.findIndex((ch) => ch.id === id);
|
|
5388
|
+
if (idx >= 0)
|
|
5389
|
+
setHighlight(idx);
|
|
5390
|
+
}));
|
|
5391
|
+
}
|
|
5392
|
+
// Column count: with autoFit, drop below the max until the widest label fits. Short labels
|
|
5393
|
+
// (a normal currency) exceed the max and clamp back to it — so the compact 6-wide layout is
|
|
5394
|
+
// preserved and only wide currencies reflow to fewer columns.
|
|
5395
|
+
let columns = maxColumns;
|
|
5396
|
+
if (opts.autoFit) {
|
|
5397
|
+
const widest = chips.reduce((m, ch) => Math.max(m, ch.measureSize().w), 0);
|
|
5398
|
+
const fitCols = Math.floor((innerW + gap) / (widest + gap));
|
|
5399
|
+
columns = Math.max(1, Math.min(maxColumns, fitCols));
|
|
5400
|
+
}
|
|
5299
5401
|
const colW = (innerW - gap * (columns - 1)) / columns;
|
|
5300
5402
|
const grid = new FlexBox({ direction: 'column', align: 'start', gap });
|
|
5301
|
-
for (let i = 0; i <
|
|
5302
|
-
const rowChoices = opts.choices.slice(i, i + columns);
|
|
5403
|
+
for (let i = 0; i < chips.length; i += columns) {
|
|
5303
5404
|
const row = new FlexBox({ direction: 'row', align: 'center', gap });
|
|
5304
|
-
for (let j = 0; j <
|
|
5305
|
-
const
|
|
5306
|
-
const chipIndex = i + j;
|
|
5307
|
-
const chip = new Chip(host, c.id, c.label, chipIndex === focusIndex, em, (id) => {
|
|
5308
|
-
const idx = opts.choices.findIndex((ch) => ch.id === id);
|
|
5309
|
-
if (idx >= 0)
|
|
5310
|
-
setHighlight(idx);
|
|
5311
|
-
});
|
|
5405
|
+
for (let j = 0; j < columns && i + j < chips.length; j++) {
|
|
5406
|
+
const chip = chips[i + j];
|
|
5312
5407
|
chip.setLayoutSize(colW, undefined);
|
|
5313
|
-
chips.push(chip);
|
|
5314
5408
|
row.add(chip);
|
|
5315
5409
|
}
|
|
5316
|
-
row.layout();
|
|
5317
5410
|
grid.add(row);
|
|
5318
5411
|
}
|
|
5319
|
-
|
|
5412
|
+
// Cap the grid height and scroll when the ladder overflows — the Pixi analogue of the HTML
|
|
5413
|
+
// shell's `max-height:min(50vh,28em); overflow-y:auto`. (Mask hit-testing clips to the viewport
|
|
5414
|
+
// but still lets clicks through to chips inside it — verified against pixi.js 8.16's
|
|
5415
|
+
// EventBoundary.) When it fits, drop the scroll box entirely so nothing is masked needlessly.
|
|
5416
|
+
const gridH = grid.measureSize().h;
|
|
5417
|
+
const maxGridH = Math.min(host.screenH * 0.5, 28 * em);
|
|
5418
|
+
if (opts.autoFit && gridH > maxGridH) {
|
|
5419
|
+
const scroll = new SizedScrollBox(innerW, maxGridH, host.canvas);
|
|
5420
|
+
scroll.content.addChild(grid);
|
|
5421
|
+
scroll.refresh();
|
|
5422
|
+
modal.body.add(scroll);
|
|
5423
|
+
}
|
|
5424
|
+
else {
|
|
5425
|
+
modal.body.add(grid);
|
|
5426
|
+
}
|
|
5320
5427
|
modal.setActions([
|
|
5321
5428
|
{
|
|
5322
5429
|
label: opts.confirmLabel,
|
|
@@ -5333,6 +5440,7 @@ function openBetPicker(host) {
|
|
|
5333
5440
|
tag: 'bet-modal',
|
|
5334
5441
|
title: host.t('Bet'),
|
|
5335
5442
|
columns: { wide: 6, mobile: 3 },
|
|
5443
|
+
autoFit: true, // reflow columns + scroll when a wide currency makes the labels grow
|
|
5336
5444
|
maxEm: 44, // wider card to fit 6 chips/row
|
|
5337
5445
|
confirmLabel: host.t('Confirm'),
|
|
5338
5446
|
choices: host.state.availableBets.map((b) => ({ id: String(b), label: host.fmt(b) })),
|