@energy8platform/shell 0.2.1 → 0.2.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@energy8platform/shell",
3
- "version": "0.2.1",
3
+ "version": "0.2.3",
4
4
  "description": "Energy8 branded game shell — one logic core, pluggable html/pixi renderers behind a stable contract.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs.js",
@@ -1,3 +1,3 @@
1
1
  // AUTO-GENERATED by scripts/gen-version.mjs — do not edit. Mirrors package.json "version".
2
2
  /** The @energy8platform/shell package version, stamped into the game-info footer. */
3
- export const PACKAGE_VERSION = '0.2.1';
3
+ export const PACKAGE_VERSION = '0.2.3';
@@ -397,6 +397,17 @@ export const SHELL_CSS = SHELL_FONT_CSS + SHELL_DIGIT_FONT_CSS + `
397
397
  #${SHELL_ROOT_ID} [data-ge="buybonus-overlay"] .ge-bb-betval { min-width:clamp(50px,14cqh,66px); }
398
398
  #${SHELL_ROOT_ID} [data-ge="buybonus-overlay"] .ge-bb-betval b { font-size:clamp(9px,2.5cqh,11px); }
399
399
  #${SHELL_ROOT_ID} [data-ge="buybonus-overlay"] .ge-bb-betval span { font-size:clamp(5px,1.25cqh,6px); }
400
+ /* Popout S (very short landscape, e.g. 400×225): the height-fit shrank the cards' descriptions to an
401
+ unreadable ~4px floor with nothing to scroll. Below a ~340px frame height, drop the shrink-to-fit and
402
+ switch the (non-mobile) buy-bonus to a READABLE vertical stack that scrolls vertically — like mobile.
403
+ Query the overlay's ge-bb-frame size container so it tracks the popout frame, not the browser window. */
404
+ @container ge-bb-frame (max-height: 340px) {
405
+ #${SHELL_ROOT_ID}:not(.ge-mobile) [data-ge="buybonus-overlay"] .ge-ov-scroll { overflow-y:auto; }
406
+ #${SHELL_ROOT_ID}:not(.ge-mobile) [data-ge="buybonus-overlay"] .ge-ov-body { justify-content:flex-start; }
407
+ #${SHELL_ROOT_ID}:not(.ge-mobile) .ge-bb-grid { flex-direction:column; align-items:center; overflow:visible; }
408
+ #${SHELL_ROOT_ID}:not(.ge-mobile) .ge-bb-grid .ge-bonus-card {
409
+ flex:0 0 auto; width:18em; font-size:min(12px, calc(84cqw / 18)); }
410
+ }
400
411
 
401
412
  /* ═══ desktop control bar — one continuous dark panel; white-disc buttons; BUY BONUS outside-left ═══ */
402
413
  /* the dark surface fills the row width (BUY BONUS sits outside, to its left) */
@@ -10,6 +10,11 @@ import { clamp } from '../primitives/overlay';
10
10
  import { attachHover } from '../primitives/widgets';
11
11
  import { roundedPath } from '../primitives/flex';
12
12
 
13
+ /** Below this frame height (px), a wide/landscape popout (e.g. Popout S 400×225) stacks its cards
14
+ * vertically and scrolls — like mobile — so descriptions stay readable instead of shrinking to a
15
+ * ~4px floor. Wide + taller frames (Popout L and up) keep the centred horizontal row. */
16
+ const SHORT_STACK_H = 340;
17
+
13
18
  /** Buy-bonus overlay — art-forward cards (one per option), a live bet footer, and a confirm modal.
14
19
  * Returns null when there are no bonus options. */
15
20
  export function openBuyBonus(host: PixiComponentContext): ShellLayer | null {
@@ -46,6 +51,8 @@ class BuyBonusOverlay extends Container implements ShellLayer {
46
51
  private cardEntries: CardEntry[] = [];
47
52
  /** Keyboard focus index into the affordable subset of cardEntries. -1 = none. */
48
53
  private focusIndex = -1;
54
+ /** true when cards are stacked vertically (mobile, or a short landscape popout). */
55
+ private stack = false;
49
56
  // Drag-scroll state. The drag is handled on the (unmasked) overlay, not the masked strip — a mask
50
57
  // prunes pointer events outside its band, stalling globalpointermove mid-drag so later cards never
51
58
  // scroll into reach. The overlay sees the whole screen, so the scroll runs the full range.
@@ -155,7 +162,10 @@ class BuyBonusOverlay extends Container implements ShellLayer {
155
162
 
156
163
  private buildCards(): void {
157
164
  this.strip.removeChildren().forEach((c) => c.destroy({ children: true }));
158
- const mobile = this.host.layout === 'mobile';
165
+ // Stack (readable vertical list + scroll) on mobile OR on a short landscape popout; a shrink-to-fit
166
+ // horizontal row on wide + tall frames. See SHORT_STACK_H.
167
+ const stack = this.host.layout === 'mobile' || this.h <= SHORT_STACK_H;
168
+ this.stack = stack;
159
169
  const top = this.headerH + 6;
160
170
  const areaH = this.h - top - this.footerH - 6;
161
171
  const gap = 14;
@@ -166,13 +176,13 @@ class BuyBonusOverlay extends Container implements ShellLayer {
166
176
  // N cards (+ gaps + 24px side margins) fit the frame width. min() of the two = the binding fit.
167
177
  // Floor 4 is a last-resort so a 400×225 popout still shows the CTA (then X-drag scrolls the slack).
168
178
  const emH = 3.4 * (areaH / 100);
169
- const emW = mobile
179
+ const emW = stack
170
180
  ? (this.w - 48) / 18 // vertical stack: a single card spans the width
171
181
  : (this.w - 48 - (n - 1) * gap) / (18 * n); // row: N cards + gaps fit the frame width
172
- const em = mobile ? Math.min(12, emW) : clamp(4, Math.min(emH, emW), 12);
182
+ const em = stack ? Math.min(12, emW) : clamp(4, Math.min(emH, emW), 12);
173
183
  const cardW = Math.min(18 * em, this.w - 48);
174
184
 
175
- const cards = this.bonuses.map((b) => this.buildCard(b, cardW, em, mobile, areaH));
185
+ const cards = this.bonuses.map((b) => this.buildCard(b, cardW, em, stack, areaH));
176
186
  const cardH = Math.max(...cards.map((c) => c.height));
177
187
  for (const c of cards) c.setHeight(cardH);
178
188
 
@@ -193,7 +203,7 @@ class BuyBonusOverlay extends Container implements ShellLayer {
193
203
  }
194
204
 
195
205
  this.stripMask.clear();
196
- if (mobile) {
206
+ if (stack) {
197
207
  // vertical stack — scroll vertically if it overflows (simple drag)
198
208
  let y = 0;
199
209
  const colX = (this.w - cardW) / 2;
@@ -220,7 +230,7 @@ class BuyBonusOverlay extends Container implements ShellLayer {
220
230
  }
221
231
 
222
232
  // ── one card ──────────────────────────────────────────────────────────────
223
- private buildCard(bonus: BonusOption, cardW: number, em: number, mobile: boolean, areaH: number): CardView {
233
+ private buildCard(bonus: BonusOption, cardW: number, em: number, stack: boolean, areaH: number): CardView {
224
234
  const accent = effectiveAccent(bonus);
225
235
  const ink = contrastText(accent);
226
236
  const price = bonus.priceMultiplier * this.host.state.bet;
@@ -255,7 +265,7 @@ class BuyBonusOverlay extends Container implements ShellLayer {
255
265
  ctaLabel: this.host.t(bonus.type === 'feature' ? 'Activate' : 'Buy'),
256
266
  onSelect: select,
257
267
  });
258
- void mobile;
268
+ void stack;
259
269
  void areaH;
260
270
  return card;
261
271
  }
@@ -435,7 +445,8 @@ class BuyBonusOverlay extends Container implements ShellLayer {
435
445
  * Browse phase: arrows move focus; +/- step bet; Enter/Space opens confirm; Escape closes.
436
446
  * Confirm phase: Enter/Space buys/activates; Escape returns to browse. */
437
447
  onKey(e: KeyboardEvent): boolean {
438
- const mobile = this.host.layout === 'mobile';
448
+ // Vertical stack (mobile / short popout) → Up/Down navigate; horizontal row → Left/Right.
449
+ const stack = this.stack;
439
450
 
440
451
  if (this.confirm && this.confirmBonus) {
441
452
  // ── Confirm phase ──
@@ -467,8 +478,8 @@ class BuyBonusOverlay extends Container implements ShellLayer {
467
478
  if (bet !== null) { this.stepBetBy(bet); return true; }
468
479
 
469
480
  // Determine navigation direction from key code + layout
470
- const fwdKey = e.code === 'ArrowRight' || (mobile && e.code === 'ArrowDown');
471
- const bwdKey = e.code === 'ArrowLeft' || (mobile && e.code === 'ArrowUp');
481
+ const fwdKey = e.code === 'ArrowRight' || (stack && e.code === 'ArrowDown');
482
+ const bwdKey = e.code === 'ArrowLeft' || (stack && e.code === 'ArrowUp');
472
483
 
473
484
  if (fwdKey) {
474
485
  if (last < 0) return true;