From 0febd2fca1844929ba97242ac1150ccb9b9f14f9 Mon Sep 17 00:00:00 2001 From: rgardler-msft Date: Sun, 22 Feb 2026 01:40:47 -0800 Subject: [PATCH 01/13] CG-0MLVZN6XF0KS4XR4: add Sushi Go icon STYLE, sample SVG, preload and simple render integration --- example-games/sushi-go/scenes/SushiGoScene.ts | 58 ++++++++++++++++--- public/assets/CREDITS.md | 8 +++ public/assets/sushi-go/STYLE.md | 54 +++++++++++++++++ public/assets/sushi-go/icon-nigiri-salmon.svg | 10 ++++ 4 files changed, 122 insertions(+), 8 deletions(-) create mode 100644 public/assets/sushi-go/STYLE.md create mode 100644 public/assets/sushi-go/icon-nigiri-salmon.svg diff --git a/example-games/sushi-go/scenes/SushiGoScene.ts b/example-games/sushi-go/scenes/SushiGoScene.ts index 2358f1ec..964a9cfb 100644 --- a/example-games/sushi-go/scenes/SushiGoScene.ts +++ b/example-games/sushi-go/scenes/SushiGoScene.ts @@ -169,6 +169,10 @@ export class SushiGoScene extends Phaser.Scene { this.load.audio(SFX_KEYS.ROUND_END, 'assets/audio/round-end.wav'); this.load.audio(SFX_KEYS.SCORE_REVEAL, 'assets/audio/score-reveal.wav'); this.load.audio(SFX_KEYS.UI_CLICK, 'assets/audio/ui-click.wav'); + + // Sushi Go icon assets + // Load only the reference icon we added; full set to be added later. + this.load.svg('icon-nigiri-salmon', 'assets/sushi-go/icon-nigiri-salmon.svg'); } // ── Create ────────────────────────────────────────────── @@ -360,14 +364,33 @@ export class SushiGoScene extends Phaser.Scene { const labelText = isHand ? this.getHandCardLabel(card) : this.getTableauCardLabel(card); const fontSize = isHand ? '16px' : '12px'; - const label = this.add.text(0, 0, labelText, { - fontSize, - color: style.text, - fontFamily: FONT_FAMILY, - align: 'center', - wordWrap: { width: w - 6 }, - }).setOrigin(0.5); - container.add(label); + // Try to render an icon if available for this card type. + const iconKey = this.getIconKeyForCard(card); + if (iconKey && this.textures.exists(iconKey)) { + const img = this.add.image(0, -h * 0.06, iconKey); + img.setOrigin(0.5, 0); + img.setDisplaySize(Math.min(w * 0.85, h * 0.6), Math.min(h * 0.6, w * 0.85)); + container.add(img); + + // Add a small type label below the icon for clarity at small sizes + const label = this.add.text(0, h * 0.36, labelText, { + fontSize, + color: style.text, + fontFamily: FONT_FAMILY, + align: 'center', + wordWrap: { width: w - 6 }, + }).setOrigin(0.5, 0); + container.add(label); + } else { + const label = this.add.text(0, 0, labelText, { + fontSize, + color: style.text, + fontFamily: FONT_FAMILY, + align: 'center', + wordWrap: { width: w - 6 }, + }).setOrigin(0.5); + container.add(label); + } // Make the card interactive for tooltip and/or clicking bg.setInteractive({ useHandCursor: interactive }); @@ -409,6 +432,25 @@ export class SushiGoScene extends Phaser.Scene { } } + /** Return the icon texture key for a card if available. */ + private getIconKeyForCard(card: SushiGoCard): string | null { + switch (card.type) { + case 'nigiri': + if (card.variant === 'salmon') return 'icon-nigiri-salmon'; + return null; + case 'maki': + case 'tempura': + case 'sashimi': + case 'dumpling': + case 'wasabi': + case 'pudding': + case 'chopsticks': + return null; + default: + return null; + } + } + // ── Refresh display ───────────────────────────────────── private refreshAll(): void { diff --git a/public/assets/CREDITS.md b/public/assets/CREDITS.md index f535d58c..d8dfdf46 100644 --- a/public/assets/CREDITS.md +++ b/public/assets/CREDITS.md @@ -72,6 +72,14 @@ Files (in `cards/lost-cities/`): - `lc-back.svg` — card back with expedition theme - Colors: yellow (compass), blue (ship), white (mountain), green (torch), red (crystal) +## Sushi Go! Icons (in-house) + +- **Source**: In-house authored SVG icons for Sushi Go! example game +- **Files**: `public/assets/sushi-go/icon-*.svg` +- **License**: MIT (see project LICENSE) + +Notes: Icons are vector SVGs authored for the project. See `public/assets/sushi-go/STYLE.md` for style and regeneration notes. + ## Audio Sound Effects — Lost Cities 12 expedition-themed WAV sound effects generated for the Lost Cities game: diff --git a/public/assets/sushi-go/STYLE.md b/public/assets/sushi-go/STYLE.md new file mode 100644 index 00000000..346af217 --- /dev/null +++ b/public/assets/sushi-go/STYLE.md @@ -0,0 +1,54 @@ +Icon Set: Sushi Go! +------------------- + +This document describes the visual language, naming, export targets and regeneration notes for the in-repo Sushi Go! icon set. + +Files +- Location: `public/assets/sushi-go/` +- Naming: `icon-[-variant].svg` (examples: `icon-nigiri-salmon.svg`, `icon-maki-1.svg`, `icon-maki-3.svg`) + +Visual / Style rules +- Palette: keep icons limited and flat for legibility at small sizes. + - Rice / neutral: #F6F6F4 + - Salmon / accent: #FF7B6B + - Seaweed / outline: #2E3B2E + - Wasabi / green: #86C166 + - Tempura / yellow: #FFD37A + - Sashimi / coral: #FF9E8A + - Pudding / brown: #C07A4A + +- Stroke: avoid thin strokes (<1px at 128px) — prefer solid shapes for pixel-tight rendering. +- Padding: provide an invisible 8px padding at 128x128 export (safe margin ~6%). +- Text: do not rely on text or external fonts inside SVGs; icons must be pure vector shapes. + +Export targets +- Baseline (source): SVG (editable vectors) at any viewBox; recommended artboard 128x128 px. +- Raster fallbacks (for CI/older devices): PNG at + - 128x128 (baseline) + - 110x145 (hand-card reference) + - 72x48 (tableau-card reference) + +Naming and variants +- Use kebab-case with `icon-` prefix: `icon-[-variant]`. +- Examples: + - `icon-nigiri-salmon.svg` + - `icon-maki-1.svg`, `icon-maki-2.svg`, `icon-maki-3.svg` + - `icon-tempura.svg`, `icon-sashimi.svg`, `icon-dumpling.svg` + +CI / Regeneration notes +- We recommend a small Node script to rasterize SVG -> PNG using `sharp`. +- Suggested script path: `tools/export-sushi-icons.js` (not yet implemented). +- Example command to produce fallbacks: + + npx tsx tools/export-sushi-icons.js --src public/assets/sushi-go --out public/assets/sushi-go/png --sizes 128x128,110x145,72x48 + +- Keep SVGs as the source of truth. If you edit an SVG, re-run the raster export to refresh PNG fallbacks. + +Accessibility +- Ensure icon color contrast is sufficient when used over card backgrounds. Prefer a subtle outline (`#2E3B2E`) to maintain separation on light/dark backgrounds. + +Example +- Reference SVG: `public/assets/sushi-go/icon-nigiri-salmon.svg` + +License +- Icons authored in-house for this project. License: MIT (see project LICENSE file). diff --git a/public/assets/sushi-go/icon-nigiri-salmon.svg b/public/assets/sushi-go/icon-nigiri-salmon.svg new file mode 100644 index 00000000..b8515f42 --- /dev/null +++ b/public/assets/sushi-go/icon-nigiri-salmon.svg @@ -0,0 +1,10 @@ + + + + + + + + + + From 0c1d663fcb6732b1b24e6516060db2490713299b Mon Sep 17 00:00:00 2001 From: rgardler-msft Date: Sun, 22 Feb 2026 01:51:18 -0800 Subject: [PATCH 02/13] CG-0MLVZN6XF0KS4XR4: add in-house SVG icon set for Sushi Go! (12 icons) and STYLE.md --- public/assets/sushi-go/icon-chopsticks.svg | 8 ++++++++ public/assets/sushi-go/icon-dumpling.svg | 12 ++++++++++++ public/assets/sushi-go/icon-maki-1.svg | 8 ++++++++ public/assets/sushi-go/icon-maki-2.svg | 13 +++++++++++++ public/assets/sushi-go/icon-maki-3.svg | 13 +++++++++++++ public/assets/sushi-go/icon-nigiri-egg.svg | 6 ++++++ public/assets/sushi-go/icon-nigiri-squid.svg | 6 ++++++ public/assets/sushi-go/icon-pudding.svg | 8 ++++++++ public/assets/sushi-go/icon-sashimi.svg | 10 ++++++++++ public/assets/sushi-go/icon-tempura.svg | 10 ++++++++++ public/assets/sushi-go/icon-wasabi.svg | 7 +++++++ 11 files changed, 101 insertions(+) create mode 100644 public/assets/sushi-go/icon-chopsticks.svg create mode 100644 public/assets/sushi-go/icon-dumpling.svg create mode 100644 public/assets/sushi-go/icon-maki-1.svg create mode 100644 public/assets/sushi-go/icon-maki-2.svg create mode 100644 public/assets/sushi-go/icon-maki-3.svg create mode 100644 public/assets/sushi-go/icon-nigiri-egg.svg create mode 100644 public/assets/sushi-go/icon-nigiri-squid.svg create mode 100644 public/assets/sushi-go/icon-pudding.svg create mode 100644 public/assets/sushi-go/icon-sashimi.svg create mode 100644 public/assets/sushi-go/icon-tempura.svg create mode 100644 public/assets/sushi-go/icon-wasabi.svg diff --git a/public/assets/sushi-go/icon-chopsticks.svg b/public/assets/sushi-go/icon-chopsticks.svg new file mode 100644 index 00000000..5f9ab0b0 --- /dev/null +++ b/public/assets/sushi-go/icon-chopsticks.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/public/assets/sushi-go/icon-dumpling.svg b/public/assets/sushi-go/icon-dumpling.svg new file mode 100644 index 00000000..68b6cc21 --- /dev/null +++ b/public/assets/sushi-go/icon-dumpling.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/public/assets/sushi-go/icon-maki-1.svg b/public/assets/sushi-go/icon-maki-1.svg new file mode 100644 index 00000000..5abc5fb1 --- /dev/null +++ b/public/assets/sushi-go/icon-maki-1.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/public/assets/sushi-go/icon-maki-2.svg b/public/assets/sushi-go/icon-maki-2.svg new file mode 100644 index 00000000..b9cf5116 --- /dev/null +++ b/public/assets/sushi-go/icon-maki-2.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/public/assets/sushi-go/icon-maki-3.svg b/public/assets/sushi-go/icon-maki-3.svg new file mode 100644 index 00000000..aef37d7d --- /dev/null +++ b/public/assets/sushi-go/icon-maki-3.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/public/assets/sushi-go/icon-nigiri-egg.svg b/public/assets/sushi-go/icon-nigiri-egg.svg new file mode 100644 index 00000000..e4090ee9 --- /dev/null +++ b/public/assets/sushi-go/icon-nigiri-egg.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/public/assets/sushi-go/icon-nigiri-squid.svg b/public/assets/sushi-go/icon-nigiri-squid.svg new file mode 100644 index 00000000..89417925 --- /dev/null +++ b/public/assets/sushi-go/icon-nigiri-squid.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/public/assets/sushi-go/icon-pudding.svg b/public/assets/sushi-go/icon-pudding.svg new file mode 100644 index 00000000..792d9709 --- /dev/null +++ b/public/assets/sushi-go/icon-pudding.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/public/assets/sushi-go/icon-sashimi.svg b/public/assets/sushi-go/icon-sashimi.svg new file mode 100644 index 00000000..2506ce9b --- /dev/null +++ b/public/assets/sushi-go/icon-sashimi.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/public/assets/sushi-go/icon-tempura.svg b/public/assets/sushi-go/icon-tempura.svg new file mode 100644 index 00000000..93319f8f --- /dev/null +++ b/public/assets/sushi-go/icon-tempura.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/public/assets/sushi-go/icon-wasabi.svg b/public/assets/sushi-go/icon-wasabi.svg new file mode 100644 index 00000000..5583fb47 --- /dev/null +++ b/public/assets/sushi-go/icon-wasabi.svg @@ -0,0 +1,7 @@ + + + + + + + From e645bd04167ca9eba844c14983d77e5dbde086d6 Mon Sep 17 00:00:00 2001 From: rgardler-msft Date: Sun, 22 Feb 2026 01:53:31 -0800 Subject: [PATCH 03/13] CG-0MLVZN6XF0KS4XR4: add IconMap and preload all sushi-go icons; integrate icon mapping into scene --- example-games/sushi-go/IconMap.ts | 28 ++++++++++++++ example-games/sushi-go/scenes/SushiGoScene.ts | 37 ++++++++----------- 2 files changed, 44 insertions(+), 21 deletions(-) create mode 100644 example-games/sushi-go/IconMap.ts diff --git a/example-games/sushi-go/IconMap.ts b/example-games/sushi-go/IconMap.ts new file mode 100644 index 00000000..be8084b5 --- /dev/null +++ b/example-games/sushi-go/IconMap.ts @@ -0,0 +1,28 @@ +import type { SushiGoCard } from './SushiGoCards'; + +/** + * Return the canonical icon key and filename (relative to `assets/sushi-go/`) for a card. + * Keys match the filenames without extension (e.g. `icon-nigiri-salmon`). + */ +export function getIconKeyForCard(card: SushiGoCard): { key: string; filename: string } | null { + switch (card.type) { + case 'nigiri': + return { key: `icon-nigiri-${card.variant}`, filename: `icon-nigiri-${card.variant}.svg` }; + case 'maki': + return { key: `icon-maki-${card.icons}`, filename: `icon-maki-${card.icons}.svg` }; + case 'tempura': + return { key: 'icon-tempura', filename: 'icon-tempura.svg' }; + case 'sashimi': + return { key: 'icon-sashimi', filename: 'icon-sashimi.svg' }; + case 'dumpling': + return { key: 'icon-dumpling', filename: 'icon-dumpling.svg' }; + case 'wasabi': + return { key: 'icon-wasabi', filename: 'icon-wasabi.svg' }; + case 'pudding': + return { key: 'icon-pudding', filename: 'icon-pudding.svg' }; + case 'chopsticks': + return { key: 'icon-chopsticks', filename: 'icon-chopsticks.svg' }; + default: + return null; + } +} diff --git a/example-games/sushi-go/scenes/SushiGoScene.ts b/example-games/sushi-go/scenes/SushiGoScene.ts index 964a9cfb..6fec9316 100644 --- a/example-games/sushi-go/scenes/SushiGoScene.ts +++ b/example-games/sushi-go/scenes/SushiGoScene.ts @@ -14,6 +14,7 @@ import Phaser from 'phaser'; import type { SushiGoCard, SushiGoCardType } from '../SushiGoCards'; import { cardLabel } from '../SushiGoCards'; +import { getIconKeyForCard } from '../IconMap'; import type { SushiGoSession, RoundResult, PickAction } from '../SushiGoGame'; import { setupSushiGoGame, @@ -170,9 +171,18 @@ export class SushiGoScene extends Phaser.Scene { this.load.audio(SFX_KEYS.SCORE_REVEAL, 'assets/audio/score-reveal.wav'); this.load.audio(SFX_KEYS.UI_CLICK, 'assets/audio/ui-click.wav'); - // Sushi Go icon assets - // Load only the reference icon we added; full set to be added later. - this.load.svg('icon-nigiri-salmon', 'assets/sushi-go/icon-nigiri-salmon.svg'); + // Sushi Go icon assets: preload all icon files present in public/assets/sushi-go + // We list the known filenames here to avoid dynamic FS lookups at runtime. + const icons = [ + 'icon-nigiri-salmon.svg', 'icon-nigiri-egg.svg', 'icon-nigiri-squid.svg', + 'icon-maki-1.svg', 'icon-maki-2.svg', 'icon-maki-3.svg', + 'icon-tempura.svg', 'icon-sashimi.svg', 'icon-dumpling.svg', + 'icon-wasabi.svg', 'icon-pudding.svg', 'icon-chopsticks.svg', + ]; + for (const fn of icons) { + const key = fn.replace(/\.svg$/, ''); + this.load.svg(key, `assets/sushi-go/${fn}`); + } } // ── Create ────────────────────────────────────────────── @@ -365,7 +375,8 @@ export class SushiGoScene extends Phaser.Scene { const fontSize = isHand ? '16px' : '12px'; // Try to render an icon if available for this card type. - const iconKey = this.getIconKeyForCard(card); + const iconMeta = getIconKeyForCard(card); + const iconKey = iconMeta?.key ?? null; if (iconKey && this.textures.exists(iconKey)) { const img = this.add.image(0, -h * 0.06, iconKey); img.setOrigin(0.5, 0); @@ -433,23 +444,7 @@ export class SushiGoScene extends Phaser.Scene { } /** Return the icon texture key for a card if available. */ - private getIconKeyForCard(card: SushiGoCard): string | null { - switch (card.type) { - case 'nigiri': - if (card.variant === 'salmon') return 'icon-nigiri-salmon'; - return null; - case 'maki': - case 'tempura': - case 'sashimi': - case 'dumpling': - case 'wasabi': - case 'pudding': - case 'chopsticks': - return null; - default: - return null; - } - } + // Deprecated: use example-games/sushi-go/IconMap.ts instead. // ── Refresh display ───────────────────────────────────── From b36ea3b1ec28de74a75681e06c251aa900f12d06 Mon Sep 17 00:00:00 2001 From: rgardler-msft Date: Sun, 22 Feb 2026 01:56:26 -0800 Subject: [PATCH 04/13] CG-0MLVZN6XF0KS4XR4: add tools/export-sushi-icons.js to rasterize SVG icons to PNG fallbacks (sharp) --- tools/export-sushi-icons.js | 61 +++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 tools/export-sushi-icons.js diff --git a/tools/export-sushi-icons.js b/tools/export-sushi-icons.js new file mode 100644 index 00000000..889db6de --- /dev/null +++ b/tools/export-sushi-icons.js @@ -0,0 +1,61 @@ +#!/usr/bin/env node +// Simple SVG -> PNG exporter using sharp +// Usage: node tools/export-sushi-icons.js --src public/assets/sushi-go --out public/assets/sushi-go/png --sizes 128x128,110x145,72x48 + +import fs from 'fs'; +import path from 'path'; +import { fileURLToPath } from 'url'; +import sharp from 'sharp'; + +function usage() { + console.log('Usage: node tools/export-sushi-icons.js --src --out --sizes '); +} + +function parseArgs() { + const args = process.argv.slice(2); + const out: any = { sizes: [] }; + for (let i = 0; i < args.length; i++) { + const a = args[i]; + if (a === '--src') out.src = args[++i]; + else if (a === '--out') out.out = args[++i]; + else if (a === '--sizes') out.sizes = args[++i].split(','); + } + return out; +} + +async function main() { + const opts = parseArgs(); + if (!opts.src || !opts.out || !opts.sizes.length) { + usage(); + process.exit(1); + } + + const svgDir = path.resolve(opts.src); + const outDir = path.resolve(opts.out); + if (!fs.existsSync(svgDir)) { + console.error('SVG source directory not found:', svgDir); + process.exit(1); + } + fs.mkdirSync(outDir, { recursive: true }); + + const files = fs.readdirSync(svgDir).filter((f) => f.endsWith('.svg')); + for (const file of files) { + const srcPath = path.join(svgDir, file); + const base = file.replace(/\.svg$/, ''); + for (const size of opts.sizes) { + const [w, h] = size.split('x').map((s: string) => parseInt(s, 10)); + const outPath = path.join(outDir, `${base}-${w}x${h}.png`); + try { + await sharp(srcPath) + .resize(w, h, { fit: 'contain', background: { r: 0, g: 0, b: 0, alpha: 0 } }) + .png({ quality: 90 }) + .toFile(outPath); + console.log('Wrote', outPath); + } catch (err) { + console.error('Failed to render', srcPath, '->', outPath, err); + } + } + } +} + +main().catch((e) => { console.error(e); process.exit(1); }); From ab8659a4e61d7055a83d7532dde3a98f0e3d0c34 Mon Sep 17 00:00:00 2001 From: rgardler-msft Date: Sun, 22 Feb 2026 01:56:51 -0800 Subject: [PATCH 05/13] CG-0MLVZN6XF0KS4XR4: attach iconKey to SushiGo cards and add export script --- example-games/sushi-go/SushiGoCards.ts | 37 +++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/example-games/sushi-go/SushiGoCards.ts b/example-games/sushi-go/SushiGoCards.ts index 85f7f9e4..bb7e0879 100644 --- a/example-games/sushi-go/SushiGoCards.ts +++ b/example-games/sushi-go/SushiGoCards.ts @@ -52,11 +52,14 @@ export interface MakiCard extends BaseCard { readonly type: 'maki'; /** Number of maki roll icons (1, 2, or 3). */ readonly icons: 1 | 2 | 3; + /** Optional icon key for UI rendering */ + readonly iconKey?: string; } export interface NigiriCard extends BaseCard { readonly type: 'nigiri'; readonly variant: NigiriVariant; + readonly iconKey?: string; } export interface WasabiCard extends BaseCard { @@ -69,6 +72,7 @@ export interface PuddingCard extends BaseCard { export interface ChopsticksCard extends BaseCard { readonly type: 'chopsticks'; + readonly iconKey?: string; } /** Discriminated union of all Sushi Go! card types. */ @@ -154,7 +158,38 @@ export function createSushiGoDeck(): SushiGoCard[] { for (const entry of DECK_COMPOSITION) { for (let i = 0; i < entry.count; i++) { - deck.push(entry.factory(nextId++)); + const card = entry.factory(nextId++); + // Attach iconKey property if the card type maps to an icon filename + // (we keep this logic simple and conservative) + switch (card.type) { + case 'nigiri': + (card as any).iconKey = `icon-nigiri-${(card as any).variant}`; + break; + case 'maki': + (card as any).iconKey = `icon-maki-${(card as any).icons}`; + break; + case 'tempura': + (card as any).iconKey = 'icon-tempura'; + break; + case 'sashimi': + (card as any).iconKey = 'icon-sashimi'; + break; + case 'dumpling': + (card as any).iconKey = 'icon-dumpling'; + break; + case 'wasabi': + (card as any).iconKey = 'icon-wasabi'; + break; + case 'pudding': + (card as any).iconKey = 'icon-pudding'; + break; + case 'chopsticks': + (card as any).iconKey = 'icon-chopsticks'; + break; + default: + break; + } + deck.push(card); } } From 35e15e7571b49e6bf0a86754ff15fcca3a6eb18f Mon Sep 17 00:00:00 2001 From: rgardler-msft Date: Sun, 22 Feb 2026 01:57:00 -0800 Subject: [PATCH 06/13] CG-0MLVZN6XF0KS4XR4: add smoke test to verify sushi-go icon SVG files exist and map correctly --- tests/sushi-go-icons.test.ts | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 tests/sushi-go-icons.test.ts diff --git a/tests/sushi-go-icons.test.ts b/tests/sushi-go-icons.test.ts new file mode 100644 index 00000000..cff64ce2 --- /dev/null +++ b/tests/sushi-go-icons.test.ts @@ -0,0 +1,18 @@ +import { describe, it, expect } from 'vitest'; +import fs from 'fs'; +import path from 'path'; +import { getIconKeyForCard } from '../example-games/sushi-go/IconMap'; +import { createSushiGoDeck } from '../example-games/sushi-go/SushiGoCards'; + +describe('sushi-go icons', () => { + it('all IconMap filenames exist in public assets', () => { + const svgDir = path.resolve('public/assets/sushi-go'); + const files = new Set(fs.readdirSync(svgDir)); + const deck = createSushiGoDeck(); + for (const card of deck) { + const meta = getIconKeyForCard(card as any as any); + if (!meta) continue; + expect(files.has(meta.filename)).toBe(true); + } + }); +}); From b169acec77378c13680b6c80dcca69a7f818e283 Mon Sep 17 00:00:00 2001 From: rgardler-msft Date: Sun, 22 Feb 2026 01:57:16 -0800 Subject: [PATCH 07/13] CG-0MLVZN6XF0KS4XR4: add preview.html and smoke test; begin UI integration --- public/assets/sushi-go/preview.html | 31 +++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 public/assets/sushi-go/preview.html diff --git a/public/assets/sushi-go/preview.html b/public/assets/sushi-go/preview.html new file mode 100644 index 00000000..06698319 --- /dev/null +++ b/public/assets/sushi-go/preview.html @@ -0,0 +1,31 @@ + + + + + Sushi Go Icon Preview + + + +

Sushi Go! Icon Preview

+
+
+ Nigiri Salmon +
128×128
+
+
+ Hand +
Hand 110×145
+
+
+ Tableau +
Tableau 72×48
+
+
+

Open this file through a static server (or place it in the same folder as the SVGs). Example: python -m http.server.

+ + From 975527c7cfbb8e1df91d6eb49fb3f154a3e6926b Mon Sep 17 00:00:00 2001 From: rgardler-msft Date: Sun, 22 Feb 2026 01:58:03 -0800 Subject: [PATCH 08/13] CG-0MLVZN6XF0KS4XR4: add sharp to devDependencies for export script --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 2e290cbe..c76078de 100644 --- a/package.json +++ b/package.json @@ -14,12 +14,13 @@ "dependencies": { "phaser": "^3.90.0" }, - "devDependencies": { + "devDependencies": { "@types/node": "^25.3.0", "@vitest/browser": "^3.2.4", "playwright": "^1.58.2", "tone": "^15.1.22", "tsx": "^4.21.0", + "sharp": "^0.33.0", "typescript": "^5.7.0", "vite": "^6.1.0", "vitest": "^3.0.0" From 71b8d56030276f7e56ef0d900cae8db2438c45cd Mon Sep 17 00:00:00 2001 From: rgardler-msft Date: Sun, 22 Feb 2026 02:06:37 -0800 Subject: [PATCH 09/13] CG-0MLVZN6XF0KS4XR4: center icons on cards and increase icon footprint for better legibility when cards change shape --- example-games/sushi-go/scenes/SushiGoScene.ts | 22 ++++++++----------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/example-games/sushi-go/scenes/SushiGoScene.ts b/example-games/sushi-go/scenes/SushiGoScene.ts index 6fec9316..e920bc8e 100644 --- a/example-games/sushi-go/scenes/SushiGoScene.ts +++ b/example-games/sushi-go/scenes/SushiGoScene.ts @@ -378,20 +378,16 @@ export class SushiGoScene extends Phaser.Scene { const iconMeta = getIconKeyForCard(card); const iconKey = iconMeta?.key ?? null; if (iconKey && this.textures.exists(iconKey)) { - const img = this.add.image(0, -h * 0.06, iconKey); - img.setOrigin(0.5, 0); - img.setDisplaySize(Math.min(w * 0.85, h * 0.6), Math.min(h * 0.6, w * 0.85)); + // Center the icon and scale it to occupy more of the card while preserving + // a small padding so it remains legible when card shapes change. + const img = this.add.image(0, 0, iconKey); + img.setOrigin(0.5, 0.5); + // Use larger footprint for icons in both hand and tableau; hand cards are taller + const iconMaxW = w * (isHand ? 0.9 : 0.85); + const iconMaxH = h * (isHand ? 0.7 : 0.85); + const iconSize = Math.min(iconMaxW, iconMaxH); + img.setDisplaySize(iconSize, iconSize); container.add(img); - - // Add a small type label below the icon for clarity at small sizes - const label = this.add.text(0, h * 0.36, labelText, { - fontSize, - color: style.text, - fontFamily: FONT_FAMILY, - align: 'center', - wordWrap: { width: w - 6 }, - }).setOrigin(0.5, 0); - container.add(label); } else { const label = this.add.text(0, 0, labelText, { fontSize, From 47df5bc1e3c4a91b02009963e7e8af5e91834679 Mon Sep 17 00:00:00 2001 From: rgardler-msft Date: Sun, 22 Feb 2026 02:10:26 -0800 Subject: [PATCH 10/13] CG-0MLVZN6XF0KS4XR4: show text label below centered icon and make tableau card size a scaled version of hand card for consistent aspect ratio --- example-games/sushi-go/scenes/SushiGoScene.ts | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/example-games/sushi-go/scenes/SushiGoScene.ts b/example-games/sushi-go/scenes/SushiGoScene.ts index e920bc8e..cc58cb67 100644 --- a/example-games/sushi-go/scenes/SushiGoScene.ts +++ b/example-games/sushi-go/scenes/SushiGoScene.ts @@ -50,8 +50,12 @@ const HAND_GAP = 8; // gap between hand cards const PLAYER_TABLEAU_Y = 395; // center Y for player tableau const AI_TABLEAU_Y = 200; // center Y for AI tableau -const TABLEAU_CARD_W = 72; // card rect width in tableau -const TABLEAU_CARD_H = 48; // card rect height in tableau +// Tableau cards should keep the same aspect ratio as hand cards so icons +// and labels stay consistent between hand and tableau. Compute them here +// as a scaled version of the hand card size. +const TABLEAU_SCALE = 0.62; // scale factor relative to hand card (tweakable) +const TABLEAU_CARD_W = Math.round(HAND_CARD_W * TABLEAU_SCALE); +const TABLEAU_CARD_H = Math.round(HAND_CARD_H * TABLEAU_SCALE); const TABLEAU_GROUP_GAP = 24; // gap between type groups const TABLEAU_CARD_GAP = 6; // gap between cards in a group @@ -388,6 +392,18 @@ export class SushiGoScene extends Phaser.Scene { const iconSize = Math.min(iconMaxW, iconMaxH); img.setDisplaySize(iconSize, iconSize); container.add(img); + + // Keep the card text label visible for accessibility and clarity. + // Place it below the icon; for small tableau cards use the smaller font. + const labelY = isHand ? h * 0.42 : h * 0.45; + const label = this.add.text(0, labelY, labelText, { + fontSize, + color: style.text, + fontFamily: FONT_FAMILY, + align: 'center', + wordWrap: { width: w - 6 }, + }).setOrigin(0.5, 0); + container.add(label); } else { const label = this.add.text(0, 0, labelText, { fontSize, From bc95b010ae8a044d54dfc23623ab2254f8d93ac5 Mon Sep 17 00:00:00 2001 From: rgardler-msft Date: Sun, 22 Feb 2026 02:12:56 -0800 Subject: [PATCH 11/13] CG-0MLVZN6XF0KS4XR4: fix label positioning so text stays inside card bounds (use bottom padding and origin=1) --- example-games/sushi-go/scenes/SushiGoScene.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/example-games/sushi-go/scenes/SushiGoScene.ts b/example-games/sushi-go/scenes/SushiGoScene.ts index cc58cb67..3b23b699 100644 --- a/example-games/sushi-go/scenes/SushiGoScene.ts +++ b/example-games/sushi-go/scenes/SushiGoScene.ts @@ -395,14 +395,16 @@ export class SushiGoScene extends Phaser.Scene { // Keep the card text label visible for accessibility and clarity. // Place it below the icon; for small tableau cards use the smaller font. - const labelY = isHand ? h * 0.42 : h * 0.45; - const label = this.add.text(0, labelY, labelText, { + // Position label flush above the bottom edge with a small padding so + // it never gets clipped when the card resizes or changes shape. + const bottomPadding = isHand ? 8 : 6; + const label = this.add.text(0, h / 2 - bottomPadding, labelText, { fontSize, color: style.text, fontFamily: FONT_FAMILY, align: 'center', wordWrap: { width: w - 6 }, - }).setOrigin(0.5, 0); + }).setOrigin(0.5, 1); // origin y=1 so text sits above the given y coordinate container.add(label); } else { const label = this.add.text(0, 0, labelText, { From b1143b74373a780aba544090d94dd9c8b86d3409 Mon Sep 17 00:00:00 2001 From: rgardler-msft Date: Sun, 22 Feb 2026 02:21:17 -0800 Subject: [PATCH 12/13] chore: update package-lock after merging PR --- package-lock.json | 517 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 517 insertions(+) diff --git a/package-lock.json b/package-lock.json index 65032eae..ec067438 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,6 +15,7 @@ "@types/node": "^25.3.0", "@vitest/browser": "^3.2.4", "playwright": "^1.58.2", + "sharp": "^0.33.0", "tone": "^15.1.22", "tsx": "^4.21.0", "typescript": "^5.7.0", @@ -64,6 +65,17 @@ "node": ">=6.9.0" } }, + "node_modules/@emnapi/runtime": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.8.1.tgz", + "integrity": "sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/@esbuild/aix-ppc64": { "version": "0.25.12", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", @@ -506,6 +518,386 @@ "node": ">=18" } }, + "node_modules/@img/sharp-darwin-arm64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.5.tgz", + "integrity": "sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-arm64": "1.0.4" + } + }, + "node_modules/@img/sharp-darwin-x64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.33.5.tgz", + "integrity": "sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-x64": "1.0.4" + } + }, + "node_modules/@img/sharp-libvips-darwin-arm64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.0.4.tgz", + "integrity": "sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-darwin-x64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.0.4.tgz", + "integrity": "sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.0.5.tgz", + "integrity": "sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.0.4.tgz", + "integrity": "sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-s390x": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.0.4.tgz", + "integrity": "sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.0.4.tgz", + "integrity": "sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.0.4.tgz", + "integrity": "sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-x64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.0.4.tgz", + "integrity": "sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-linux-arm": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.33.5.tgz", + "integrity": "sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm": "1.0.5" + } + }, + "node_modules/@img/sharp-linux-arm64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.33.5.tgz", + "integrity": "sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm64": "1.0.4" + } + }, + "node_modules/@img/sharp-linux-s390x": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.33.5.tgz", + "integrity": "sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-s390x": "1.0.4" + } + }, + "node_modules/@img/sharp-linux-x64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.33.5.tgz", + "integrity": "sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-x64": "1.0.4" + } + }, + "node_modules/@img/sharp-linuxmusl-arm64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.33.5.tgz", + "integrity": "sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-arm64": "1.0.4" + } + }, + "node_modules/@img/sharp-linuxmusl-x64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.33.5.tgz", + "integrity": "sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-x64": "1.0.4" + } + }, + "node_modules/@img/sharp-wasm32": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.33.5.tgz", + "integrity": "sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "optional": true, + "dependencies": { + "@emnapi/runtime": "^1.2.0" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-ia32": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.33.5.tgz", + "integrity": "sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-x64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.33.5.tgz", + "integrity": "sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, "node_modules/@jridgewell/sourcemap-codec": { "version": "1.5.5", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", @@ -1194,6 +1586,51 @@ "node": ">= 16" } }, + "node_modules/color": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", + "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1", + "color-string": "^1.9.0" + }, + "engines": { + "node": ">=12.5.0" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/color-string": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", + "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, "node_modules/debug": { "version": "4.4.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", @@ -1232,6 +1669,16 @@ "node": ">=6" } }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, "node_modules/dom-accessibility-api": { "version": "0.5.16", "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", @@ -1360,6 +1807,13 @@ "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" } }, + "node_modules/is-arrayish": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.4.tgz", + "integrity": "sha512-m6UrgzFVUYawGBh1dUsWR5M2Clqic9RVXC/9f8ceNlv2IcO9j9J/z8UoCLPqtsPBFNzEpfR3xftohbfqDx8EQA==", + "dev": true, + "license": "MIT" + }, "node_modules/js-tokens": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.1.tgz", @@ -1630,6 +2084,59 @@ "fsevents": "~2.3.2" } }, + "node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/sharp": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.33.5.tgz", + "integrity": "sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw==", + "dev": true, + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "color": "^4.2.3", + "detect-libc": "^2.0.3", + "semver": "^7.6.3" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-darwin-arm64": "0.33.5", + "@img/sharp-darwin-x64": "0.33.5", + "@img/sharp-libvips-darwin-arm64": "1.0.4", + "@img/sharp-libvips-darwin-x64": "1.0.4", + "@img/sharp-libvips-linux-arm": "1.0.5", + "@img/sharp-libvips-linux-arm64": "1.0.4", + "@img/sharp-libvips-linux-s390x": "1.0.4", + "@img/sharp-libvips-linux-x64": "1.0.4", + "@img/sharp-libvips-linuxmusl-arm64": "1.0.4", + "@img/sharp-libvips-linuxmusl-x64": "1.0.4", + "@img/sharp-linux-arm": "0.33.5", + "@img/sharp-linux-arm64": "0.33.5", + "@img/sharp-linux-s390x": "0.33.5", + "@img/sharp-linux-x64": "0.33.5", + "@img/sharp-linuxmusl-arm64": "0.33.5", + "@img/sharp-linuxmusl-x64": "0.33.5", + "@img/sharp-wasm32": "0.33.5", + "@img/sharp-win32-ia32": "0.33.5", + "@img/sharp-win32-x64": "0.33.5" + } + }, "node_modules/siginfo": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", @@ -1637,6 +2144,16 @@ "dev": true, "license": "ISC" }, + "node_modules/simple-swizzle": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.4.tgz", + "integrity": "sha512-nAu1WFPQSMNr2Zn9PGSZK9AGn4t/y97lEm+MXTtUDwfP0ksAIX4nO+6ruD9Jwut4C49SB1Ws+fbXsm/yScWOHw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.3.1" + } + }, "node_modules/sirv": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/sirv/-/sirv-3.0.2.tgz", From 3fc3df037994ec7d1be20b1d6b76717ef4c3e6fe Mon Sep 17 00:00:00 2001 From: rgardler-msft Date: Sun, 22 Feb 2026 02:32:24 -0800 Subject: [PATCH 13/13] tests(lost-cities): increase timeout for full match transcript test to 60s to avoid flakiness --- tests/lost-cities/lost-cities-transcript.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/lost-cities/lost-cities-transcript.test.ts b/tests/lost-cities/lost-cities-transcript.test.ts index f426ba15..91830f13 100644 --- a/tests/lost-cities/lost-cities-transcript.test.ts +++ b/tests/lost-cities/lost-cities-transcript.test.ts @@ -276,7 +276,7 @@ describe('Full match transcript', () => { const transcript = recorder.finalize(session); validateTranscript(transcript); - }, 15_000); + }, 60_000); it('has exactly 3 rounds', () => { const { session, recorder } = runFullAiMatch(99);