Skip to content

Commit ac2fe14

Browse files
Sync packages from monorepo (d03ced6)
1 parent ad5b198 commit ac2fe14

5 files changed

Lines changed: 59 additions & 43 deletions

File tree

packages/sdk/src/simulator/createSimulator.ts

Lines changed: 37 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,16 @@
66
* - Sending READY_DATA with puzzle data
77
* - Providing UI controls for START_GAME, PAUSE_GAME, RESUME_GAME, RETRY_PUZZLE
88
*
9-
* Usage in index.html:
10-
* ```html
11-
* <script type="module">
12-
* import { createSimulator } from "@puzzmo/sdk/simulator"
13-
* createSimulator({ puzzlePath: "./sample-puzzle.json", autoStart: true })
14-
* </script>
9+
* Usage with Vite plugin (recommended):
10+
* ```ts
11+
* // vite.config.ts
12+
* import { puzzmoSimulator } from "@puzzmo/sdk/vite"
13+
* export default defineConfig({
14+
* plugins: [puzzmoSimulator({ slug: "my-game", fixturesGlob: "./fixtures/puzzles/**\/*.json" })]
15+
* })
1516
* ```
1617
*
17-
* With fixtures (recommended for multiple puzzles):
18+
* Usage with manual imports:
1819
* ```html
1920
* <script type="module">
2021
* import { createSimulator } from "@puzzmo/sdk/simulator"
@@ -47,9 +48,6 @@ import {
4748
// Re-export types for consumers
4849
export type { SimulatorConfig, FixtureImports }
4950

50-
// Default puzzle path
51-
const DEFAULT_PUZZLE_PATH = "./sample-puzzle.json"
52-
5351
// Singleton instance state
5452
interface SimulatorInstance {
5553
updateFixtures: (fixtures: FixtureImports) => void
@@ -74,7 +72,6 @@ function createSimulator(config: SimulatorConfig = {}): SimulatorInstance {
7472
return simulatorInstance
7573
}
7674

77-
const puzzlePath = config.puzzlePath ?? DEFAULT_PUZZLE_PATH
7875
const autoStart = config.autoStart ?? true
7976

8077
// Parse fixtures if provided
@@ -139,7 +136,10 @@ function createSimulator(config: SimulatorConfig = {}): SimulatorInstance {
139136
<div id="simulator-tabs">
140137
${views
141138
.filter((v) => v.id !== "auth")
142-
.map((v) => `<button class="simulator-tab" data-tab="${v.id}">${v.label}<span class="simulator-tab-badge" data-badge="${v.id}"></span></button>`)
139+
.map(
140+
(v) =>
141+
`<button class="simulator-tab" data-tab="${v.id}">${v.label}<span class="simulator-tab-badge" data-badge="${v.id}"></span></button>`,
142+
)
143143
.join("")}
144144
</div>
145145
<div id="simulator-content" class="hidden">
@@ -238,32 +238,37 @@ function createSimulator(config: SimulatorConfig = {}): SimulatorInstance {
238238
thumbView.updatePreview(context)
239239
}
240240

241-
// Load puzzle data
241+
// Load puzzle data from fixtures
242242
const loadPuzzle = async (): Promise<any> => {
243243
if (state.puzzleData) return state.puzzleData
244244

245-
try {
246-
const response = await fetch(puzzlePath)
247-
if (!response.ok) {
248-
throw new Error(`Failed to load puzzle from ${puzzlePath}`)
249-
}
250-
state.puzzleData = await response.json()
251-
console.log("Simulator: Puzzle loaded", state.puzzleData)
252-
state.originalPuzzle = JSON.stringify(state.puzzleData, null, 2)
245+
if (!fixtures || fixtures.size === 0) {
246+
throw new Error("No fixtures configured. Add puzzle JSON files to a fixtures directory and pass fixturesGlob to the simulator.")
247+
}
253248

254-
// Update puzzle textarea if exists
255-
const puzzleTextarea = getElement<HTMLTextAreaElement>("#simulator-puzzle")
256-
if (puzzleTextarea) puzzleTextarea.value = state.originalPuzzle
249+
// Pick the selected fixture, or the first one available
250+
const category = state.selectedCategory ?? fixtureCategories[0]
251+
const categoryMap = category ? fixtures.get(category) : undefined
252+
if (!categoryMap || categoryMap.size === 0) {
253+
throw new Error(`No puzzles found in fixture category "${category}"`)
254+
}
257255

258-
// Refresh thumbnail if thumb tab is active
259-
if (state.activeTab === "thumb") {
260-
updateThumbnail()
261-
}
262-
return state.puzzleData
263-
} catch (error) {
264-
console.error("Simulator: Failed to load puzzle", error)
265-
throw error
256+
const puzzleName = state.selectedPuzzle ?? categoryMap.keys().next().value
257+
const puzzleData = puzzleName ? categoryMap.get(puzzleName) : undefined
258+
if (!puzzleData) {
259+
throw new Error(`Puzzle "${puzzleName}" not found in category "${category}"`)
266260
}
261+
262+
state.puzzleData = puzzleData
263+
console.log("Simulator: Puzzle loaded from fixtures", { category, puzzle: puzzleName })
264+
state.originalPuzzle = JSON.stringify(state.puzzleData, null, 2)
265+
266+
const puzzleTextarea = getElement<HTMLTextAreaElement>("#simulator-puzzle")
267+
if (puzzleTextarea) puzzleTextarea.value = state.originalPuzzle
268+
269+
if (state.activeTab === "thumb") updateThumbnail()
270+
271+
return state.puzzleData
267272
}
268273

269274
// Wrapped sendToGame with logger

packages/sdk/src/simulator/standalone.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
/**
22
* Standalone entry point for the simulator.
33
*
4-
* Use as an IIFE script tag for non-Vite setups:
4+
* Use as a script tag for non-Vite setups:
55
* ```html
66
* <script>
7-
* window.SIMULATOR_CONFIG = { puzzlePath: "./sample-puzzle.json" }
7+
* window.SIMULATOR_CONFIG = { slug: "my-game" }
88
* </script>
9-
* <script src="path/to/simulator/standalone.iife.js"></script>
9+
* <script src="https://cdn.jsdelivr.net/npm/@puzzmo/sdk/dist/simulator/standalone.js"></script>
1010
* ```
1111
*/
1212
import { createSimulator } from "./createSimulator"

packages/sdk/src/simulator/types.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@ import type { MessagesReceived, Theme, ThumbnailConfig } from "../types"
44
export type FixtureImports = Record<string, { default?: any }>
55

66
export interface SimulatorConfig {
7-
/** Path to the puzzle JSON file (default: "./sample-puzzle.json") */
8-
puzzlePath?: string
97
/** Whether to auto-start the game after READY (default: true) */
108
autoStart?: boolean
119
/** Initial collapsed state (default: true) */

packages/sdk/src/vite.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
import type { Plugin } from "vite"
22

33
export type PuzzmoSimulatorPluginOptions = {
4-
/** Path to the puzzle JSON file (default: "./sample-puzzle.json") */
5-
puzzlePath?: string
64
/** Whether to auto-start the game after READY (default: true) */
75
autoStart?: boolean
86
/** Initial collapsed state (default: true) */

skills/introduce-puzzmo-sdk/SKILL.md

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,21 @@ Add the `@puzzmo/sdk` package and wire up the game lifecycle.
6262

6363
5. Wire up state saving - call `sdk.updateGameState(stateString)` whenever the game state changes so the host can save progress.
6464

65-
6. Create a `sample-puzzle.json` file with test puzzle data that matches what the game expects.
65+
6. Create puzzle fixture files for local testing. Make a `fixtures/puzzles/` directory and add a few different puzzle JSON files that exercise different aspects of the game:
66+
67+
```
68+
fixtures/puzzles/
69+
easy/
70+
small-grid.json
71+
basic.json
72+
hard/
73+
large-grid.json
74+
tricky.json
75+
```
76+
77+
Each JSON file should contain puzzle data in the format the game expects. Try to create at least 2-3 puzzles with different characteristics (e.g. varying difficulty, size, or edge cases) so the game can be tested against realistic variety.
78+
79+
If the original game already has a few puzzles hardcoded, use those as a starting point for creating the fixture files. Then delete the buttons which may have been used to load them, since the simulator will handle loading puzzles from the fixtures.
6680

6781
7. Set up the dev simulator for local testing. Add the Vite plugin to `vite.config.ts`:
6882

@@ -74,21 +88,22 @@ Add the `@puzzmo/sdk` package and wire up the game lifecycle.
7488
plugins: [
7589
puzzmoSimulator({
7690
slug: "my-game",
77-
puzzlePath: "./sample-puzzle.json",
91+
fixturesGlob: "./fixtures/puzzles/**/*.json",
7892
}),
7993
],
8094
})
8195
```
8296

8397
The plugin automatically:
8498
- Injects the simulator UI in dev mode only (tree-shaken from production builds)
99+
- Loads fixtures from the glob pattern, organized by folder as categories
85100
- Handles OAuth callback routing for authenticated API features
86101

87102
For non-Vite setups, load the SDK and simulator from jsDelivr:
88103

89104
```html
90105
<script>
91-
window.SIMULATOR_CONFIG = { puzzlePath: "./sample-puzzle.json", slug: "my-game" }
106+
window.SIMULATOR_CONFIG = { slug: "my-game" }
92107
</script>
93108
<script src="https://cdn.jsdelivr.net/npm/@puzzmo/sdk/dist/simulator/standalone.js"></script>
94109
```
@@ -107,5 +122,5 @@ Add the `@puzzmo/sdk` package and wire up the game lifecycle.
107122
- Game initializes through SDK lifecycle (gameReady -> gameLoaded -> on start)
108123
- Game state is saved via `updateGameState` on each user action
109124
- Pause/resume/retry events are handled
110-
- A sample-puzzle.json exists for local testing
125+
- Fixture puzzles exist in `fixtures/puzzles/` with at least 2-3 varied puzzles
111126
- Simulator appears in dev mode but is not included in production builds

0 commit comments

Comments
 (0)