@littlepartytime/dev-kit 1.1.0 → 1.2.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.
Files changed (39) hide show
  1. package/dist/cli.js +0 -0
  2. package/dist/commands/dev.d.ts +7 -1
  3. package/dist/commands/dev.d.ts.map +1 -1
  4. package/dist/commands/dev.js +30 -13
  5. package/dist/commands/dev.js.map +1 -1
  6. package/dist/index.d.ts +2 -0
  7. package/dist/index.d.ts.map +1 -1
  8. package/dist/index.js +3 -1
  9. package/dist/index.js.map +1 -1
  10. package/dist/testing/game-preview.d.ts +53 -0
  11. package/dist/testing/game-preview.d.ts.map +1 -0
  12. package/dist/testing/game-preview.js +158 -0
  13. package/dist/testing/game-preview.js.map +1 -0
  14. package/dist/testing/index.d.ts +3 -0
  15. package/dist/testing/index.d.ts.map +1 -0
  16. package/dist/testing/index.js +6 -0
  17. package/dist/testing/index.js.map +1 -0
  18. package/dist/webapp/pages/Preview.tsx +195 -42
  19. package/package.json +20 -2
  20. package/dist/webapp/App.d.ts +0 -3
  21. package/dist/webapp/App.d.ts.map +0 -1
  22. package/dist/webapp/App.js +0 -70
  23. package/dist/webapp/App.js.map +0 -1
  24. package/dist/webapp/main.d.ts +0 -2
  25. package/dist/webapp/main.d.ts.map +0 -1
  26. package/dist/webapp/main.js +0 -12
  27. package/dist/webapp/main.js.map +0 -1
  28. package/dist/webapp/pages/Debug.d.ts +0 -3
  29. package/dist/webapp/pages/Debug.d.ts.map +0 -1
  30. package/dist/webapp/pages/Debug.js +0 -69
  31. package/dist/webapp/pages/Debug.js.map +0 -1
  32. package/dist/webapp/pages/Play.d.ts +0 -3
  33. package/dist/webapp/pages/Play.d.ts.map +0 -1
  34. package/dist/webapp/pages/Play.js +0 -129
  35. package/dist/webapp/pages/Play.js.map +0 -1
  36. package/dist/webapp/pages/Preview.d.ts +0 -3
  37. package/dist/webapp/pages/Preview.d.ts.map +0 -1
  38. package/dist/webapp/pages/Preview.js +0 -118
  39. package/dist/webapp/pages/Preview.js.map +0 -1
package/dist/cli.js CHANGED
File without changes
@@ -1,6 +1,12 @@
1
1
  export interface DevOptions {
2
2
  port?: number;
3
3
  socketPort?: number;
4
+ silent?: boolean;
4
5
  }
5
- export declare function devCommand(projectDir: string, options?: DevOptions): Promise<void>;
6
+ export interface DevServerHandle {
7
+ stop(): Promise<void>;
8
+ port: number;
9
+ socketPort: number;
10
+ }
11
+ export declare function devCommand(projectDir: string, options?: DevOptions): Promise<DevServerHandle>;
6
12
  //# sourceMappingURL=dev.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"dev.d.ts","sourceRoot":"","sources":["../../src/commands/dev.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,UAAU;IACzB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,wBAAsB,UAAU,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,GAAE,UAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAsD5F"}
1
+ {"version":3,"file":"dev.d.ts","sourceRoot":"","sources":["../../src/commands/dev.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,UAAU;IACzB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,wBAAsB,UAAU,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,GAAE,UAAe,GAAG,OAAO,CAAC,eAAe,CAAC,CAwEvG"}
@@ -12,12 +12,14 @@ const chokidar_1 = __importDefault(require("chokidar"));
12
12
  async function devCommand(projectDir, options = {}) {
13
13
  const port = options.port || 4000;
14
14
  const socketPort = options.socketPort || 4001;
15
- console.log('');
16
- console.log(' Little Party Time Dev Kit');
17
- console.log(' =========================');
18
- console.log('');
15
+ if (!options.silent) {
16
+ console.log('');
17
+ console.log(' Little Party Time Dev Kit');
18
+ console.log(' =========================');
19
+ console.log('');
20
+ }
19
21
  // Start Socket.IO server
20
- const { reloadEngine } = (0, socket_server_1.createSocketServer)({
22
+ const { server, io, reloadEngine } = (0, socket_server_1.createSocketServer)({
21
23
  port: socketPort,
22
24
  projectDir,
23
25
  });
@@ -25,7 +27,9 @@ async function devCommand(projectDir, options = {}) {
25
27
  const enginePath = path_1.default.join(projectDir, 'dist', 'engine.cjs');
26
28
  const watcher = chokidar_1.default.watch(enginePath, { ignoreInitial: true });
27
29
  watcher.on('change', () => {
28
- console.log('[Dev] Engine changed, reloading...');
30
+ if (!options.silent) {
31
+ console.log('[Dev] Engine changed, reloading...');
32
+ }
29
33
  reloadEngine();
30
34
  });
31
35
  // Start Vite dev server
@@ -48,12 +52,25 @@ async function devCommand(projectDir, options = {}) {
48
52
  },
49
53
  });
50
54
  await vite.listen();
51
- console.log(` Preview: http://localhost:${port}/preview`);
52
- console.log(` Multiplayer: http://localhost:${port}/play`);
53
- console.log(` Debug Panel: http://localhost:${port}/debug`);
54
- console.log(` Socket.IO: ws://localhost:${socketPort}`);
55
- console.log('');
56
- console.log(' Press Ctrl+C to stop');
57
- console.log('');
55
+ if (!options.silent) {
56
+ console.log(` Preview: http://localhost:${port}/preview`);
57
+ console.log(` Multiplayer: http://localhost:${port}/play`);
58
+ console.log(` Debug Panel: http://localhost:${port}/debug`);
59
+ console.log(` Socket.IO: ws://localhost:${socketPort}`);
60
+ console.log('');
61
+ console.log(' Press Ctrl+C to stop');
62
+ console.log('');
63
+ }
64
+ return {
65
+ port,
66
+ socketPort,
67
+ async stop() {
68
+ await watcher.close();
69
+ await vite.close();
70
+ io.disconnectSockets(true);
71
+ io.close();
72
+ await new Promise((resolve) => server.close(() => resolve()));
73
+ },
74
+ };
58
75
  }
59
76
  //# sourceMappingURL=dev.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"dev.js","sourceRoot":"","sources":["../../src/commands/dev.ts"],"names":[],"mappings":";;;;;AAWA,gCAsDC;AAjED,uCAAuC;AACvC,gDAAwB;AACxB,+BAAmD;AACnD,2DAA6D;AAC7D,wDAAgC;AAOzB,KAAK,UAAU,UAAU,CAAC,UAAkB,EAAE,UAAsB,EAAE;IAC3E,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,IAAI,IAAI,CAAC;IAClC,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,IAAI,IAAI,CAAC;IAE9C,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAChB,OAAO,CAAC,GAAG,CAAC,6BAA6B,CAAC,CAAC;IAC3C,OAAO,CAAC,GAAG,CAAC,6BAA6B,CAAC,CAAC;IAC3C,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAEhB,yBAAyB;IACzB,MAAM,EAAE,YAAY,EAAE,GAAG,IAAA,kCAAkB,EAAC;QAC1C,IAAI,EAAE,UAAU;QAChB,UAAU;KACX,CAAC,CAAC;IAEH,2BAA2B;IAC3B,MAAM,UAAU,GAAG,cAAI,CAAC,IAAI,CAAC,UAAU,EAAE,MAAM,EAAE,YAAY,CAAC,CAAC;IAC/D,MAAM,OAAO,GAAG,kBAAQ,CAAC,KAAK,CAAC,UAAU,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;IACpE,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,GAAG,EAAE;QACxB,OAAO,CAAC,GAAG,CAAC,oCAAoC,CAAC,CAAC;QAClD,YAAY,EAAE,CAAC;IACjB,CAAC,CAAC,CAAC;IAEH,wBAAwB;IACxB,MAAM,SAAS,GAAG,cAAI,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAC;IAEvD,8DAA8D;IAC9D,MAAM,KAAK,GAAG,OAAO,CAAC,sBAAsB,CAAC,CAAC,OAAO,CAAC;IAEtD,MAAM,IAAI,GAAkB,MAAM,IAAA,mBAAY,EAAC;QAC7C,IAAI,EAAE,SAAS;QACf,OAAO,EAAE,CAAC,KAAK,EAAE,CAAC;QAClB,MAAM,EAAE;YACN,IAAI;YACJ,EAAE,EAAE;gBACF,KAAK,EAAE,CAAC,SAAS,EAAE,UAAU,CAAC;aAC/B;SACF;QACD,OAAO,EAAE;YACP,KAAK,EAAE;gBACL,MAAM,EAAE,cAAI,CAAC,IAAI,CAAC,UAAU,EAAE,KAAK,CAAC;aACrC;SACF;KACF,CAAC,CAAC;IAEH,MAAM,IAAI,CAAC,MAAM,EAAE,CAAC;IAEpB,OAAO,CAAC,GAAG,CAAC,oCAAoC,IAAI,UAAU,CAAC,CAAC;IAChE,OAAO,CAAC,GAAG,CAAC,oCAAoC,IAAI,OAAO,CAAC,CAAC;IAC7D,OAAO,CAAC,GAAG,CAAC,oCAAoC,IAAI,QAAQ,CAAC,CAAC;IAC9D,OAAO,CAAC,GAAG,CAAC,kCAAkC,UAAU,EAAE,CAAC,CAAC;IAC5D,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAChB,OAAO,CAAC,GAAG,CAAC,wBAAwB,CAAC,CAAC;IACtC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;AAClB,CAAC"}
1
+ {"version":3,"file":"dev.js","sourceRoot":"","sources":["../../src/commands/dev.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -1,3 +1,5 @@
1
1
  export { validateBundle } from './utils/validate-bundle';
2
2
  export type { ValidationResult } from './utils/validate-bundle';
3
+ export { devCommand } from './commands/dev';
4
+ export type { DevOptions, DevServerHandle } from './commands/dev';
3
5
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AACzD,YAAY,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AACzD,YAAY,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAChE,OAAO,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAC5C,YAAY,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC"}
package/dist/index.js CHANGED
@@ -1,6 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.validateBundle = void 0;
3
+ exports.devCommand = exports.validateBundle = void 0;
4
4
  var validate_bundle_1 = require("./utils/validate-bundle");
5
5
  Object.defineProperty(exports, "validateBundle", { enumerable: true, get: function () { return validate_bundle_1.validateBundle; } });
6
+ var dev_1 = require("./commands/dev");
7
+ Object.defineProperty(exports, "devCommand", { enumerable: true, get: function () { return dev_1.devCommand; } });
6
8
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,2DAAyD;AAAhD,iHAAA,cAAc,OAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,2DAAyD;AAAhD,iHAAA,cAAc,OAAA;AAEvB,sCAA4C;AAAnC,iGAAA,UAAU,OAAA"}
@@ -0,0 +1,53 @@
1
+ export interface GamePreviewOptions {
2
+ /** Absolute path to the game project directory */
3
+ projectDir: string;
4
+ /** Number of players to simulate */
5
+ playerCount: number;
6
+ /** Vite dev server port (default: 4100) */
7
+ port?: number;
8
+ /** Socket.IO server port (default: 4101) */
9
+ socketPort?: number;
10
+ /** Run browser in headless mode (default: true) */
11
+ headless?: boolean;
12
+ /** Browser type to use (default: 'chromium') */
13
+ browserType?: 'chromium' | 'firefox' | 'webkit';
14
+ }
15
+ export declare class GamePreview {
16
+ private options;
17
+ private serverHandle;
18
+ private browser;
19
+ private context;
20
+ private playerPages;
21
+ constructor(options: GamePreviewOptions);
22
+ /**
23
+ * Start the dev server and launch browser pages for each player.
24
+ * Each player automatically joins the game lobby.
25
+ */
26
+ start(): Promise<void>;
27
+ /**
28
+ * Get the Playwright Page object for a specific player by index.
29
+ * Player 0 is the host.
30
+ */
31
+ getPlayerPage(playerIndex: number): any;
32
+ /**
33
+ * Get all Playwright Page objects.
34
+ */
35
+ getPlayerPages(): any[];
36
+ /**
37
+ * Get the number of players.
38
+ */
39
+ get playerCount(): number;
40
+ /**
41
+ * Click "Ready" for all players.
42
+ */
43
+ readyAll(): Promise<void>;
44
+ /**
45
+ * Have the host start the game. Requires all players to be ready.
46
+ */
47
+ startGame(): Promise<void>;
48
+ /**
49
+ * Stop the browser and dev server, cleaning up all resources.
50
+ */
51
+ stop(): Promise<void>;
52
+ }
53
+ //# sourceMappingURL=game-preview.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"game-preview.d.ts","sourceRoot":"","sources":["../../src/testing/game-preview.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,kBAAkB;IACjC,kDAAkD;IAClD,UAAU,EAAE,MAAM,CAAC;IACnB,oCAAoC;IACpC,WAAW,EAAE,MAAM,CAAC;IACpB,2CAA2C;IAC3C,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,4CAA4C;IAC5C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,mDAAmD;IACnD,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,gDAAgD;IAChD,WAAW,CAAC,EAAE,UAAU,GAAG,SAAS,GAAG,QAAQ,CAAC;CACjD;AAID,qBAAa,WAAW;IACtB,OAAO,CAAC,OAAO,CAA+B;IAC9C,OAAO,CAAC,YAAY,CAAgC;IACpD,OAAO,CAAC,OAAO,CAAa;IAC5B,OAAO,CAAC,OAAO,CAAa;IAC5B,OAAO,CAAC,WAAW,CAAyC;gBAEhD,OAAO,EAAE,kBAAkB;IAUvC;;;OAGG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAiD5B;;;OAGG;IACH,aAAa,CAAC,WAAW,EAAE,MAAM,GAAG,GAAG;IASvC;;OAEG;IACH,cAAc,IAAI,GAAG,EAAE;IAIvB;;OAEG;IACH,IAAI,WAAW,IAAI,MAAM,CAExB;IAED;;OAEG;IACG,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAS/B;;OAEG;IACG,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC;IAYhC;;OAEG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;CAe5B"}
@@ -0,0 +1,158 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.GamePreview = void 0;
37
+ const PLAYER_NAMES = ['Alice', 'Bob', 'Carol', 'Dave', 'Eve', 'Frank', 'Grace', 'Heidi'];
38
+ class GamePreview {
39
+ options;
40
+ serverHandle = null;
41
+ browser = null;
42
+ context = null;
43
+ playerPages = [];
44
+ constructor(options) {
45
+ this.options = {
46
+ port: 4100,
47
+ socketPort: 4101,
48
+ headless: true,
49
+ browserType: 'chromium',
50
+ ...options,
51
+ };
52
+ }
53
+ /**
54
+ * Start the dev server and launch browser pages for each player.
55
+ * Each player automatically joins the game lobby.
56
+ */
57
+ async start() {
58
+ // 1. Start dev server programmatically
59
+ const { devCommand } = await Promise.resolve().then(() => __importStar(require('../commands/dev')));
60
+ this.serverHandle = await devCommand(this.options.projectDir, {
61
+ port: this.options.port,
62
+ socketPort: this.options.socketPort,
63
+ silent: true,
64
+ });
65
+ // 2. Launch browser (dynamic import to handle optional dependency)
66
+ let playwright;
67
+ try {
68
+ // Use variable to prevent TypeScript from resolving the module at compile time
69
+ const moduleName = 'playwright';
70
+ playwright = await Promise.resolve(`${moduleName}`).then(s => __importStar(require(s)));
71
+ }
72
+ catch {
73
+ throw new Error('GamePreview requires playwright. Install it with: npm install -D playwright');
74
+ }
75
+ const browserType = playwright[this.options.browserType];
76
+ if (!browserType) {
77
+ throw new Error(`Unknown browser type: ${this.options.browserType}`);
78
+ }
79
+ this.browser = await browserType.launch({
80
+ headless: this.options.headless,
81
+ });
82
+ this.context = await this.browser.newContext();
83
+ // 3. Open pages and join lobby for each player
84
+ for (let i = 0; i < this.options.playerCount; i++) {
85
+ const nickname = PLAYER_NAMES[i] || `Player ${i + 1}`;
86
+ const page = await this.context.newPage();
87
+ await page.goto(`http://localhost:${this.options.port}/play`);
88
+ // Fill nickname and join
89
+ await page.fill('input[placeholder="Your nickname"]', nickname);
90
+ await page.click('button:has-text("Join")');
91
+ // Wait for lobby to appear
92
+ await page.waitForSelector('text=Lobby', { timeout: 10000 });
93
+ this.playerPages.push({ page, nickname });
94
+ }
95
+ }
96
+ /**
97
+ * Get the Playwright Page object for a specific player by index.
98
+ * Player 0 is the host.
99
+ */
100
+ getPlayerPage(playerIndex) {
101
+ if (playerIndex < 0 || playerIndex >= this.playerPages.length) {
102
+ throw new Error(`Invalid playerIndex: ${playerIndex}. Valid range: 0-${this.playerPages.length - 1}`);
103
+ }
104
+ return this.playerPages[playerIndex].page;
105
+ }
106
+ /**
107
+ * Get all Playwright Page objects.
108
+ */
109
+ getPlayerPages() {
110
+ return this.playerPages.map(pp => pp.page);
111
+ }
112
+ /**
113
+ * Get the number of players.
114
+ */
115
+ get playerCount() {
116
+ return this.playerPages.length;
117
+ }
118
+ /**
119
+ * Click "Ready" for all players.
120
+ */
121
+ async readyAll() {
122
+ for (const pp of this.playerPages) {
123
+ const readyButton = pp.page.locator('button:has-text("Ready")');
124
+ await readyButton.click();
125
+ // Small delay to let Socket.IO propagate
126
+ await pp.page.waitForTimeout(100);
127
+ }
128
+ }
129
+ /**
130
+ * Have the host start the game. Requires all players to be ready.
131
+ */
132
+ async startGame() {
133
+ const hostPage = this.playerPages[0].page;
134
+ await hostPage.click('button:has-text("Start Game")');
135
+ // Wait for game state to load on all pages
136
+ await Promise.all(this.playerPages.map(pp => pp.page.waitForTimeout(500)));
137
+ }
138
+ /**
139
+ * Stop the browser and dev server, cleaning up all resources.
140
+ */
141
+ async stop() {
142
+ if (this.context) {
143
+ await this.context.close().catch(() => { });
144
+ this.context = null;
145
+ }
146
+ if (this.browser) {
147
+ await this.browser.close().catch(() => { });
148
+ this.browser = null;
149
+ }
150
+ if (this.serverHandle) {
151
+ await this.serverHandle.stop();
152
+ this.serverHandle = null;
153
+ }
154
+ this.playerPages = [];
155
+ }
156
+ }
157
+ exports.GamePreview = GamePreview;
158
+ //# sourceMappingURL=game-preview.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"game-preview.js","sourceRoot":"","sources":["../../src/testing/game-preview.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ export { GamePreview } from './game-preview';
2
+ export type { GamePreviewOptions } from './game-preview';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/testing/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC7C,YAAY,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC"}
@@ -0,0 +1,6 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.GamePreview = void 0;
4
+ var game_preview_1 = require("./game-preview");
5
+ Object.defineProperty(exports, "GamePreview", { enumerable: true, get: function () { return game_preview_1.GamePreview; } });
6
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/testing/index.ts"],"names":[],"mappings":";;;AAAA,+CAA6C;AAApC,2GAAA,WAAW,OAAA"}
@@ -1,90 +1,243 @@
1
- import React, { useState, useEffect, useCallback } from 'react';
1
+ import React, { useState, useEffect, useCallback, useMemo, useRef } from 'react';
2
+
3
+ const PLAYER_NAMES = ['Alice', 'Bob', 'Carol', 'Dave', 'Eve', 'Frank', 'Grace', 'Heidi'];
2
4
 
3
5
  export default function Preview() {
4
- const [state, setState] = useState<any>({ phase: 'playing', players: [], data: {} });
5
- const [stateJson, setStateJson] = useState('');
6
+ const [playerCount, setPlayerCount] = useState(3);
6
7
  const [playerIndex, setPlayerIndex] = useState(0);
7
8
  const [actions, setActions] = useState<any[]>([]);
8
9
  const [GameRenderer, setGameRenderer] = useState<React.ComponentType<any> | null>(null);
10
+ const [engine, setEngine] = useState<any>(null);
11
+ const [fullState, setFullState] = useState<any>(null);
12
+ const [viewState, setViewState] = useState<any>(null);
13
+ const [gameOver, setGameOver] = useState(false);
14
+ const [gameResult, setGameResult] = useState<any>(null);
15
+ const [stateJson, setStateJson] = useState('');
16
+
17
+ // Refs to avoid recreating platform on every state change
18
+ const fullStateRef = useRef(fullState);
19
+ fullStateRef.current = fullState;
20
+ const playerIndexRef = useRef(playerIndex);
21
+ playerIndexRef.current = playerIndex;
22
+ const stateUpdateListeners = useRef<Set<(...args: unknown[]) => void>>(new Set());
9
23
 
10
- // Load renderer dynamically
24
+ // Generate mock players
25
+ const mockPlayers = useMemo(() => {
26
+ return Array.from({ length: playerCount }, (_, i) => ({
27
+ id: `player-${i + 1}`,
28
+ nickname: PLAYER_NAMES[i] || `Player ${i + 1}`,
29
+ avatarUrl: null,
30
+ isHost: i === 0,
31
+ }));
32
+ }, [playerCount]);
33
+
34
+ const mockPlayersRef = useRef(mockPlayers);
35
+ mockPlayersRef.current = mockPlayers;
36
+
37
+ // Load renderer and engine dynamically
11
38
  useEffect(() => {
12
- import('/src/renderer.tsx').then((mod) => {
13
- setGameRenderer(() => mod.default || mod.Renderer);
39
+ import('/src/index.ts').then((mod) => {
40
+ setGameRenderer(() => mod.Renderer || mod.default);
41
+ if (mod.engine) {
42
+ setEngine(mod.engine);
43
+ }
14
44
  }).catch((err) => {
15
- console.error('Failed to load renderer:', err);
45
+ console.error('Failed to load game module:', err);
46
+ // Fallback: try loading renderer directly
47
+ import('/src/renderer.tsx').then((mod) => {
48
+ setGameRenderer(() => mod.default || mod.Renderer);
49
+ }).catch((err2) => {
50
+ console.error('Failed to load renderer:', err2);
51
+ });
16
52
  });
17
53
  }, []);
18
54
 
19
- // Mock players
20
- const mockPlayers = [
21
- { id: 'player-1', nickname: 'Alice', avatarUrl: null, isHost: true },
22
- { id: 'player-2', nickname: 'Bob', avatarUrl: null, isHost: false },
23
- { id: 'player-3', nickname: 'Carol', avatarUrl: null, isHost: false },
24
- ];
25
-
26
- // Mock platform
27
- const platform = {
28
- getPlayers: () => mockPlayers,
29
- getLocalPlayer: () => mockPlayers[playerIndex],
30
- send: (action: any) => {
31
- setActions((prev) => [...prev, { time: new Date().toISOString(), player: mockPlayers[playerIndex].nickname, action }]);
32
- },
33
- on: () => {},
34
- off: () => {},
35
- reportResult: () => {},
36
- };
37
-
38
- const updateState = useCallback(() => {
55
+ // Initialize game when engine loads or player count changes
56
+ useEffect(() => {
57
+ if (!engine) return;
58
+ const initialState = engine.init(mockPlayers);
59
+ setFullState(initialState);
60
+ setGameOver(false);
61
+ setGameResult(null);
62
+ setActions([]);
63
+ }, [engine, mockPlayers]);
64
+
65
+ // Compute view state whenever fullState or playerIndex changes
66
+ useEffect(() => {
67
+ if (!engine || !fullState) return;
68
+ const view = engine.getPlayerView(fullState, mockPlayers[playerIndex].id);
69
+ setViewState(view);
70
+ // Notify renderer of new view
71
+ stateUpdateListeners.current.forEach(handler => handler(view));
72
+ }, [fullState, playerIndex, engine, mockPlayers]);
73
+
74
+ // Sync state JSON editor
75
+ useEffect(() => {
76
+ if (fullState) {
77
+ setStateJson(JSON.stringify(fullState, null, 2));
78
+ }
79
+ }, [fullState]);
80
+
81
+ // Platform object — stable reference via useMemo on engine + mockPlayers
82
+ const platform = useMemo(() => {
83
+ if (!engine) return null;
84
+ return {
85
+ getPlayers: () => mockPlayersRef.current,
86
+ getLocalPlayer: () => mockPlayersRef.current[playerIndexRef.current],
87
+ send: (action: any) => {
88
+ const currentState = fullStateRef.current;
89
+ if (!currentState) return;
90
+
91
+ const playerId = mockPlayersRef.current[playerIndexRef.current].id;
92
+
93
+ // Log the action
94
+ setActions(prev => [...prev, {
95
+ time: new Date().toISOString(),
96
+ player: mockPlayersRef.current[playerIndexRef.current].nickname,
97
+ action,
98
+ }]);
99
+
100
+ // Run through engine
101
+ const newState = engine.handleAction(currentState, playerId, action);
102
+ setFullState(newState);
103
+
104
+ // Check game over
105
+ if (engine.isGameOver(newState)) {
106
+ setGameOver(true);
107
+ setGameResult(engine.getResult(newState));
108
+ }
109
+ },
110
+ on: (event: string, handler: (...args: unknown[]) => void) => {
111
+ if (event === 'stateUpdate') {
112
+ stateUpdateListeners.current.add(handler);
113
+ }
114
+ },
115
+ off: (event: string, handler: (...args: unknown[]) => void) => {
116
+ if (event === 'stateUpdate') {
117
+ stateUpdateListeners.current.delete(handler);
118
+ }
119
+ },
120
+ reportResult: (result: any) => {
121
+ console.log('Game result reported:', result);
122
+ },
123
+ };
124
+ }, [engine]);
125
+
126
+ // Apply manual state override from JSON editor
127
+ const applyState = useCallback(() => {
39
128
  try {
40
129
  const parsed = JSON.parse(stateJson);
41
- setState(parsed);
130
+ setFullState(parsed);
131
+ if (engine) {
132
+ setGameOver(engine.isGameOver(parsed));
133
+ if (engine.isGameOver(parsed)) {
134
+ setGameResult(engine.getResult(parsed));
135
+ } else {
136
+ setGameResult(null);
137
+ }
138
+ }
42
139
  } catch (e) {
43
- // ignore
140
+ // ignore invalid JSON
44
141
  }
45
- }, [stateJson]);
142
+ }, [stateJson, engine]);
143
+
144
+ // Reset game
145
+ const resetGame = useCallback(() => {
146
+ if (!engine) return;
147
+ const newState = engine.init(mockPlayersRef.current);
148
+ setFullState(newState);
149
+ setGameOver(false);
150
+ setGameResult(null);
151
+ setActions([]);
152
+ }, [engine]);
46
153
 
154
+ // Clamp playerIndex when playerCount decreases
47
155
  useEffect(() => {
48
- setStateJson(JSON.stringify(state, null, 2));
49
- }, []);
156
+ if (playerIndex >= playerCount) {
157
+ setPlayerIndex(0);
158
+ }
159
+ }, [playerCount, playerIndex]);
50
160
 
51
161
  return (
52
162
  <div className="flex gap-4 h-[calc(100vh-80px)]">
53
163
  {/* Renderer */}
54
164
  <div className="flex-1 bg-zinc-900 rounded-lg overflow-auto">
55
- {GameRenderer ? (
56
- <GameRenderer platform={platform} state={state} />
165
+ {GameRenderer && platform && viewState ? (
166
+ <GameRenderer platform={platform} state={viewState} />
57
167
  ) : (
58
- <div className="p-4 text-zinc-500">Loading renderer...</div>
168
+ <div className="p-4 text-zinc-500">
169
+ {!engine ? 'Loading engine...' : 'Initializing game...'}
170
+ </div>
59
171
  )}
60
172
  </div>
61
173
 
62
174
  {/* Control Panel */}
63
- <div className="w-80 flex flex-col gap-4">
175
+ <div className="w-80 flex flex-col gap-4 overflow-auto">
176
+ {/* Player Count */}
177
+ <div className="bg-zinc-900 rounded-lg p-3">
178
+ <h3 className="text-sm font-bold text-zinc-400 mb-2">Player Count</h3>
179
+ <input
180
+ type="number"
181
+ min={2}
182
+ max={8}
183
+ value={playerCount}
184
+ onChange={(e) => setPlayerCount(Math.max(2, Math.min(8, Number(e.target.value))))}
185
+ className="w-full bg-zinc-800 border border-zinc-700 rounded px-2 py-1 text-sm"
186
+ />
187
+ </div>
188
+
64
189
  {/* Player Switcher */}
65
190
  <div className="bg-zinc-900 rounded-lg p-3">
66
191
  <h3 className="text-sm font-bold text-zinc-400 mb-2">Current Player</h3>
67
192
  <select
68
193
  value={playerIndex}
69
194
  onChange={(e) => setPlayerIndex(Number(e.target.value))}
70
- className="w-full bg-zinc-800 border border-zinc-700 rounded px-2 py-1"
195
+ className="w-full bg-zinc-800 border border-zinc-700 rounded px-2 py-1 text-sm"
71
196
  >
72
197
  {mockPlayers.map((p, i) => (
73
- <option key={p.id} value={i}>{p.nickname}</option>
198
+ <option key={p.id} value={i}>{p.nickname}{p.isHost ? ' (Host)' : ''}</option>
74
199
  ))}
75
200
  </select>
76
201
  </div>
77
202
 
203
+ {/* Game Result */}
204
+ {gameOver && gameResult && (
205
+ <div className="bg-zinc-900 rounded-lg p-3">
206
+ <h3 className="text-sm font-bold text-green-400 mb-2">Game Over</h3>
207
+ <pre className="text-xs font-mono bg-zinc-800 rounded p-2 overflow-auto max-h-32 whitespace-pre-wrap">
208
+ {JSON.stringify(gameResult, null, 2)}
209
+ </pre>
210
+ <button
211
+ onClick={resetGame}
212
+ className="mt-2 w-full bg-amber-600 hover:bg-amber-500 text-white px-3 py-1 rounded text-sm"
213
+ >
214
+ Reset Game
215
+ </button>
216
+ </div>
217
+ )}
218
+
219
+ {/* Reset button (when game is not over) */}
220
+ {!gameOver && engine && (
221
+ <div className="bg-zinc-900 rounded-lg p-3">
222
+ <button
223
+ onClick={resetGame}
224
+ className="w-full bg-zinc-700 hover:bg-zinc-600 text-white px-3 py-1 rounded text-sm"
225
+ >
226
+ Reset Game
227
+ </button>
228
+ </div>
229
+ )}
230
+
78
231
  {/* State Editor */}
79
- <div className="bg-zinc-900 rounded-lg p-3 flex-1 flex flex-col">
80
- <h3 className="text-sm font-bold text-zinc-400 mb-2">Game State</h3>
232
+ <div className="bg-zinc-900 rounded-lg p-3 flex-1 flex flex-col min-h-0">
233
+ <h3 className="text-sm font-bold text-zinc-400 mb-2">Game State (Full)</h3>
81
234
  <textarea
82
235
  value={stateJson}
83
236
  onChange={(e) => setStateJson(e.target.value)}
84
- className="flex-1 bg-zinc-800 border border-zinc-700 rounded p-2 font-mono text-xs resize-none"
237
+ className="flex-1 bg-zinc-800 border border-zinc-700 rounded p-2 font-mono text-xs resize-none min-h-[120px]"
85
238
  />
86
239
  <button
87
- onClick={updateState}
240
+ onClick={applyState}
88
241
  className="mt-2 bg-amber-600 hover:bg-amber-500 text-white px-3 py-1 rounded text-sm"
89
242
  >
90
243
  Apply State
package/package.json CHANGED
@@ -1,14 +1,24 @@
1
1
  {
2
2
  "name": "@littlepartytime/dev-kit",
3
- "version": "1.1.0",
3
+ "version": "1.2.1",
4
4
  "description": "Development toolkit CLI for Little Party Time game developers",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
7
+ "exports": {
8
+ ".": {
9
+ "types": "./dist/index.d.ts",
10
+ "default": "./dist/index.js"
11
+ },
12
+ "./testing": {
13
+ "types": "./dist/testing/index.d.ts",
14
+ "default": "./dist/testing/index.js"
15
+ }
16
+ },
7
17
  "bin": {
8
18
  "lpt-dev-kit": "./dist/cli.js"
9
19
  },
10
20
  "scripts": {
11
- "build": "tsc && cp -r src/webapp dist/",
21
+ "build": "rm -rf dist && tsc && cp -r src/webapp dist/",
12
22
  "dev": "tsc --watch",
13
23
  "test": "vitest run",
14
24
  "test:watch": "vitest"
@@ -21,6 +31,14 @@
21
31
  "url": "https://github.com/chesterli710/littlepartytime-sdk.git",
22
32
  "directory": "packages/dev-kit"
23
33
  },
34
+ "peerDependencies": {
35
+ "playwright": ">=1.40.0"
36
+ },
37
+ "peerDependenciesMeta": {
38
+ "playwright": {
39
+ "optional": true
40
+ }
41
+ },
24
42
  "dependencies": {
25
43
  "@vitejs/plugin-react": "^5",
26
44
  "archiver": "^7",
@@ -1,3 +0,0 @@
1
- import React from 'react';
2
- export default function App(): React.JSX.Element;
3
- //# sourceMappingURL=App.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"App.d.ts","sourceRoot":"","sources":["../../src/webapp/App.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAmB,MAAM,OAAO,CAAC;AAOxC,MAAM,CAAC,OAAO,UAAU,GAAG,sBAgC1B"}
@@ -1,70 +0,0 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
35
- var __importDefault = (this && this.__importDefault) || function (mod) {
36
- return (mod && mod.__esModule) ? mod : { "default": mod };
37
- };
38
- Object.defineProperty(exports, "__esModule", { value: true });
39
- exports.default = App;
40
- const react_1 = __importStar(require("react"));
41
- const Preview_1 = __importDefault(require("./pages/Preview"));
42
- const Play_1 = __importDefault(require("./pages/Play"));
43
- const Debug_1 = __importDefault(require("./pages/Debug"));
44
- function App() {
45
- const [page, setPage] = (0, react_1.useState)(() => {
46
- const path = window.location.pathname;
47
- if (path.includes('play'))
48
- return 'play';
49
- if (path.includes('debug'))
50
- return 'debug';
51
- return 'preview';
52
- });
53
- return (<div className="min-h-screen flex flex-col">
54
- {/* Nav */}
55
- <nav className="bg-zinc-900 border-b border-zinc-800 px-4 py-2 flex gap-4">
56
- <span className="text-amber-500 font-bold mr-4">LPT Dev Kit</span>
57
- {['preview', 'play', 'debug'].map((p) => (<button key={p} onClick={() => { setPage(p); history.pushState(null, '', `/${p}`); }} className={`px-3 py-1 rounded ${page === p ? 'bg-amber-600 text-white' : 'text-zinc-400 hover:text-white'}`}>
58
- {p.charAt(0).toUpperCase() + p.slice(1)}
59
- </button>))}
60
- </nav>
61
-
62
- {/* Content */}
63
- <main className="flex-1 p-4">
64
- {page === 'preview' && <Preview_1.default />}
65
- {page === 'play' && <Play_1.default />}
66
- {page === 'debug' && <Debug_1.default />}
67
- </main>
68
- </div>);
69
- }
70
- //# sourceMappingURL=App.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"App.js","sourceRoot":"","sources":["../../src/webapp/App.tsx"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAOA,sBAgCC;AAvCD,+CAAwC;AACxC,8DAAsC;AACtC,wDAAgC;AAChC,0DAAkC;AAIlC,SAAwB,GAAG;IACzB,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,IAAA,gBAAQ,EAAO,GAAG,EAAE;QAC1C,MAAM,IAAI,GAAG,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC;QACtC,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC;YAAE,OAAO,MAAM,CAAC;QACzC,IAAI,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC;YAAE,OAAO,OAAO,CAAC;QAC3C,OAAO,SAAS,CAAC;IACnB,CAAC,CAAC,CAAC;IAEH,OAAO,CACL,CAAC,GAAG,CAAC,SAAS,CAAC,4BAA4B,CACzC;MAAA,CAAC,SAAS,CACV;MAAA,CAAC,GAAG,CAAC,SAAS,CAAC,2DAA2D,CACxE;QAAA,CAAC,IAAI,CAAC,SAAS,CAAC,+BAA+B,CAAC,WAAW,EAAE,IAAI,CACjE;QAAA,CAAE,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,CAAY,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CACnD,CAAC,MAAM,CACL,GAAG,CAAC,CAAC,CAAC,CAAC,CACP,OAAO,CAAC,CAAC,GAAG,EAAE,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC,IAAI,EAAE,EAAE,EAAE,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CACrE,SAAS,CAAC,CAAC,qBAAqB,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,yBAAyB,CAAC,CAAC,CAAC,gCAAgC,EAAE,CAAC,CAE5G;YAAA,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CACzC;UAAA,EAAE,MAAM,CAAC,CACV,CAAC,CACJ;MAAA,EAAE,GAAG,CAEL;;MAAA,CAAC,aAAa,CACd;MAAA,CAAC,IAAI,CAAC,SAAS,CAAC,YAAY,CAC1B;QAAA,CAAC,IAAI,KAAK,SAAS,IAAI,CAAC,iBAAO,CAAC,AAAD,EAAG,CAClC;QAAA,CAAC,IAAI,KAAK,MAAM,IAAI,CAAC,cAAI,CAAC,AAAD,EAAG,CAC5B;QAAA,CAAC,IAAI,KAAK,OAAO,IAAI,CAAC,eAAK,CAAC,AAAD,EAAG,CAChC;MAAA,EAAE,IAAI,CACR;IAAA,EAAE,GAAG,CAAC,CACP,CAAC;AACJ,CAAC"}
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=main.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../../src/webapp/main.tsx"],"names":[],"mappings":""}
@@ -1,12 +0,0 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- const react_1 = __importDefault(require("react"));
7
- const client_1 = __importDefault(require("react-dom/client"));
8
- const App_1 = __importDefault(require("./App"));
9
- client_1.default.createRoot(document.getElementById('root')).render(<react_1.default.StrictMode>
10
- <App_1.default />
11
- </react_1.default.StrictMode>);
12
- //# sourceMappingURL=main.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"main.js","sourceRoot":"","sources":["../../src/webapp/main.tsx"],"names":[],"mappings":";;;;;AAAA,kDAA0B;AAC1B,8DAAwC;AACxC,gDAAwB;AAExB,gBAAQ,CAAC,UAAU,CAAC,QAAQ,CAAC,cAAc,CAAC,MAAM,CAAE,CAAC,CAAC,MAAM,CAC1D,CAAC,eAAK,CAAC,UAAU,CACf;IAAA,CAAC,aAAG,CAAC,AAAD,EACN;EAAA,EAAE,eAAK,CAAC,UAAU,CAAC,CACpB,CAAC"}
@@ -1,3 +0,0 @@
1
- import React from 'react';
2
- export default function Debug(): React.JSX.Element;
3
- //# sourceMappingURL=Debug.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"Debug.d.ts","sourceRoot":"","sources":["../../../src/webapp/pages/Debug.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA8B,MAAM,OAAO,CAAC;AAGnD,MAAM,CAAC,OAAO,UAAU,KAAK,sBAoC5B"}
@@ -1,69 +0,0 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
35
- Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.default = Debug;
37
- const react_1 = __importStar(require("react"));
38
- const socket_io_client_1 = require("socket.io-client");
39
- function Debug() {
40
- const [socket, setSocket] = (0, react_1.useState)(null);
41
- const [room, setRoom] = (0, react_1.useState)({ players: [], phase: 'lobby' });
42
- const [fullState, setFullState] = (0, react_1.useState)(null);
43
- (0, react_1.useEffect)(() => {
44
- const sock = (0, socket_io_client_1.io)('http://localhost:4001', { query: { nickname: '__debug__' } });
45
- sock.on('room:update', setRoom);
46
- sock.on('game:state', setFullState);
47
- sock.on('debug:state', setFullState);
48
- setSocket(sock);
49
- return () => { sock.disconnect(); };
50
- }, []);
51
- return (<div className="grid grid-cols-2 gap-4 h-[calc(100vh-80px)]">
52
- {/* Room State */}
53
- <div className="bg-zinc-900 rounded-lg p-4 overflow-auto">
54
- <h2 className="text-lg font-bold mb-4 text-amber-400">Room State</h2>
55
- <pre className="text-xs font-mono whitespace-pre-wrap">
56
- {JSON.stringify(room, null, 2)}
57
- </pre>
58
- </div>
59
-
60
- {/* Game State */}
61
- <div className="bg-zinc-900 rounded-lg p-4 overflow-auto">
62
- <h2 className="text-lg font-bold mb-4 text-amber-400">Full Game State</h2>
63
- <pre className="text-xs font-mono whitespace-pre-wrap">
64
- {fullState ? JSON.stringify(fullState, null, 2) : 'No game in progress'}
65
- </pre>
66
- </div>
67
- </div>);
68
- }
69
- //# sourceMappingURL=Debug.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"Debug.js","sourceRoot":"","sources":["../../../src/webapp/pages/Debug.tsx"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAGA,wBAoCC;AAvCD,+CAAmD;AACnD,uDAA8C;AAE9C,SAAwB,KAAK;IAC3B,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,GAAG,IAAA,gBAAQ,EAAgB,IAAI,CAAC,CAAC;IAC1D,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,IAAA,gBAAQ,EAAM,EAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC;IACvE,MAAM,CAAC,SAAS,EAAE,YAAY,CAAC,GAAG,IAAA,gBAAQ,EAAM,IAAI,CAAC,CAAC;IAEtD,IAAA,iBAAS,EAAC,GAAG,EAAE;QACb,MAAM,IAAI,GAAG,IAAA,qBAAE,EAAC,uBAAuB,EAAE,EAAE,KAAK,EAAE,EAAE,QAAQ,EAAE,WAAW,EAAE,EAAE,CAAC,CAAC;QAE/E,IAAI,CAAC,EAAE,CAAC,aAAa,EAAE,OAAO,CAAC,CAAC;QAChC,IAAI,CAAC,EAAE,CAAC,YAAY,EAAE,YAAY,CAAC,CAAC;QACpC,IAAI,CAAC,EAAE,CAAC,aAAa,EAAE,YAAY,CAAC,CAAC;QAErC,SAAS,CAAC,IAAI,CAAC,CAAC;QAEhB,OAAO,GAAG,EAAE,GAAG,IAAI,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,CAAC;IACtC,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,OAAO,CACL,CAAC,GAAG,CAAC,SAAS,CAAC,6CAA6C,CAC1D;MAAA,CAAC,gBAAgB,CACjB;MAAA,CAAC,GAAG,CAAC,SAAS,CAAC,0CAA0C,CACvD;QAAA,CAAC,EAAE,CAAC,SAAS,CAAC,uCAAuC,CAAC,UAAU,EAAE,EAAE,CACpE;QAAA,CAAC,GAAG,CAAC,SAAS,CAAC,uCAAuC,CACpD;UAAA,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAChC;QAAA,EAAE,GAAG,CACP;MAAA,EAAE,GAAG,CAEL;;MAAA,CAAC,gBAAgB,CACjB;MAAA,CAAC,GAAG,CAAC,SAAS,CAAC,0CAA0C,CACvD;QAAA,CAAC,EAAE,CAAC,SAAS,CAAC,uCAAuC,CAAC,eAAe,EAAE,EAAE,CACzE;QAAA,CAAC,GAAG,CAAC,SAAS,CAAC,uCAAuC,CACpD;UAAA,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,qBAAqB,CACzE;QAAA,EAAE,GAAG,CACP;MAAA,EAAE,GAAG,CACP;IAAA,EAAE,GAAG,CAAC,CACP,CAAC;AACJ,CAAC"}
@@ -1,3 +0,0 @@
1
- import React from 'react';
2
- export default function Play(): React.JSX.Element;
3
- //# sourceMappingURL=Play.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"Play.d.ts","sourceRoot":"","sources":["../../../src/webapp/pages/Play.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA2C,MAAM,OAAO,CAAC;AAGhE,MAAM,CAAC,OAAO,UAAU,IAAI,sBAqI3B"}
@@ -1,129 +0,0 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
35
- Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.default = Play;
37
- const react_1 = __importStar(require("react"));
38
- const socket_io_client_1 = require("socket.io-client");
39
- function Play() {
40
- const [socket, setSocket] = (0, react_1.useState)(null);
41
- const [nickname, setNickname] = (0, react_1.useState)('');
42
- const [joined, setJoined] = (0, react_1.useState)(false);
43
- const [room, setRoom] = (0, react_1.useState)({ players: [], phase: 'lobby' });
44
- const [gameState, setGameState] = (0, react_1.useState)(null);
45
- const [myId, setMyId] = (0, react_1.useState)(null);
46
- const [GameRenderer, setGameRenderer] = (0, react_1.useState)(null);
47
- // Load renderer
48
- (0, react_1.useEffect)(() => {
49
- Promise.resolve().then(() => __importStar(require('/src/renderer.tsx'))).then((mod) => {
50
- setGameRenderer(() => mod.default || mod.Renderer);
51
- }).catch(console.error);
52
- }, []);
53
- const join = (0, react_1.useCallback)(() => {
54
- if (!nickname.trim())
55
- return;
56
- const sock = (0, socket_io_client_1.io)('http://localhost:4001', { query: { nickname } });
57
- sock.on('connect', () => {
58
- setMyId(sock.id);
59
- setJoined(true);
60
- });
61
- sock.on('room:update', setRoom);
62
- sock.on('game:state', setGameState);
63
- sock.on('game:result', (result) => {
64
- console.log('Game result:', result);
65
- });
66
- setSocket(sock);
67
- }, [nickname]);
68
- const me = room.players.find((p) => socket && p.socketId === socket.id) || room.players.find((p) => p.nickname === nickname);
69
- const isHost = me?.isHost;
70
- const isReady = me?.ready;
71
- const platform = socket ? {
72
- getPlayers: () => room.players.map((p) => ({ id: p.id, nickname: p.nickname, avatarUrl: null, isHost: p.isHost })),
73
- getLocalPlayer: () => me ? { id: me.id, nickname: me.nickname, avatarUrl: null, isHost: me.isHost } : { id: '', nickname: '', avatarUrl: null, isHost: false },
74
- send: (action) => socket.emit('game:action', action),
75
- on: (event, handler) => {
76
- if (event === 'stateUpdate')
77
- socket.on('game:state', handler);
78
- },
79
- off: (event, handler) => {
80
- if (event === 'stateUpdate')
81
- socket.off('game:state', handler);
82
- },
83
- reportResult: () => { },
84
- } : null;
85
- if (!joined) {
86
- return (<div className="flex items-center justify-center h-[60vh]">
87
- <div className="bg-zinc-900 rounded-lg p-6 w-80">
88
- <h2 className="text-xl font-bold mb-4">Join Game</h2>
89
- <input type="text" placeholder="Your nickname" value={nickname} onChange={(e) => setNickname(e.target.value)} onKeyDown={(e) => e.key === 'Enter' && join()} className="w-full bg-zinc-800 border border-zinc-700 rounded px-3 py-2 mb-4"/>
90
- <button onClick={join} className="w-full bg-amber-600 hover:bg-amber-500 text-white py-2 rounded font-semibold">
91
- Join
92
- </button>
93
- </div>
94
- </div>);
95
- }
96
- if (room.phase === 'lobby' || room.phase === 'ready') {
97
- return (<div className="max-w-md mx-auto mt-8">
98
- <div className="bg-zinc-900 rounded-lg p-6">
99
- <h2 className="text-xl font-bold mb-4">Lobby</h2>
100
- <div className="space-y-2 mb-6">
101
- {room.players.map((p) => (<div key={p.id} className="flex items-center justify-between bg-zinc-800 rounded px-3 py-2">
102
- <span>{p.nickname} {p.isHost && '(Host)'}</span>
103
- <span className={p.ready ? 'text-green-400' : 'text-zinc-500'}>
104
- {p.ready ? 'Ready' : 'Not Ready'}
105
- </span>
106
- </div>))}
107
- </div>
108
- <div className="flex gap-2">
109
- <button onClick={() => socket?.emit('player:ready', !isReady)} className={`flex-1 py-2 rounded font-semibold ${isReady ? 'bg-zinc-700 text-zinc-300' : 'bg-green-600 text-white'}`}>
110
- {isReady ? 'Cancel Ready' : 'Ready'}
111
- </button>
112
- {isHost && (<button onClick={() => socket?.emit('game:start')} disabled={!room.players.every((p) => p.ready) || room.players.length < 2} className="flex-1 bg-amber-600 hover:bg-amber-500 disabled:opacity-50 text-white py-2 rounded font-semibold">
113
- Start Game
114
- </button>)}
115
- </div>
116
- </div>
117
- </div>);
118
- }
119
- // Playing or ended
120
- return (<div className="h-[calc(100vh-80px)]">
121
- {GameRenderer && platform && gameState ? (<GameRenderer platform={platform} state={gameState}/>) : (<div className="p-4 text-zinc-500">Loading game...</div>)}
122
- {room.phase === 'ended' && isHost && (<div className="fixed bottom-4 left-1/2 -translate-x-1/2">
123
- <button onClick={() => socket?.emit('game:playAgain')} className="bg-amber-600 hover:bg-amber-500 text-white px-6 py-2 rounded-full font-semibold">
124
- Play Again
125
- </button>
126
- </div>)}
127
- </div>);
128
- }
129
- //# sourceMappingURL=Play.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"Play.js","sourceRoot":"","sources":["../../../src/webapp/pages/Play.tsx"],"names":[],"mappings":"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"}
@@ -1,3 +0,0 @@
1
- import React from 'react';
2
- export default function Preview(): React.JSX.Element;
3
- //# sourceMappingURL=Preview.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"Preview.d.ts","sourceRoot":"","sources":["../../../src/webapp/pages/Preview.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA2C,MAAM,OAAO,CAAC;AAEhE,MAAM,CAAC,OAAO,UAAU,OAAO,sBA6G9B"}
@@ -1,118 +0,0 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
35
- Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.default = Preview;
37
- const react_1 = __importStar(require("react"));
38
- function Preview() {
39
- const [state, setState] = (0, react_1.useState)({ phase: 'playing', players: [], data: {} });
40
- const [stateJson, setStateJson] = (0, react_1.useState)('');
41
- const [playerIndex, setPlayerIndex] = (0, react_1.useState)(0);
42
- const [actions, setActions] = (0, react_1.useState)([]);
43
- const [GameRenderer, setGameRenderer] = (0, react_1.useState)(null);
44
- // Load renderer dynamically
45
- (0, react_1.useEffect)(() => {
46
- Promise.resolve().then(() => __importStar(require('/src/renderer.tsx'))).then((mod) => {
47
- setGameRenderer(() => mod.default || mod.Renderer);
48
- }).catch((err) => {
49
- console.error('Failed to load renderer:', err);
50
- });
51
- }, []);
52
- // Mock players
53
- const mockPlayers = [
54
- { id: 'player-1', nickname: 'Alice', avatarUrl: null, isHost: true },
55
- { id: 'player-2', nickname: 'Bob', avatarUrl: null, isHost: false },
56
- { id: 'player-3', nickname: 'Carol', avatarUrl: null, isHost: false },
57
- ];
58
- // Mock platform
59
- const platform = {
60
- getPlayers: () => mockPlayers,
61
- getLocalPlayer: () => mockPlayers[playerIndex],
62
- send: (action) => {
63
- setActions((prev) => [...prev, { time: new Date().toISOString(), player: mockPlayers[playerIndex].nickname, action }]);
64
- },
65
- on: () => { },
66
- off: () => { },
67
- reportResult: () => { },
68
- };
69
- const updateState = (0, react_1.useCallback)(() => {
70
- try {
71
- const parsed = JSON.parse(stateJson);
72
- setState(parsed);
73
- }
74
- catch (e) {
75
- // ignore
76
- }
77
- }, [stateJson]);
78
- (0, react_1.useEffect)(() => {
79
- setStateJson(JSON.stringify(state, null, 2));
80
- }, []);
81
- return (<div className="flex gap-4 h-[calc(100vh-80px)]">
82
- {/* Renderer */}
83
- <div className="flex-1 bg-zinc-900 rounded-lg overflow-auto">
84
- {GameRenderer ? (<GameRenderer platform={platform} state={state}/>) : (<div className="p-4 text-zinc-500">Loading renderer...</div>)}
85
- </div>
86
-
87
- {/* Control Panel */}
88
- <div className="w-80 flex flex-col gap-4">
89
- {/* Player Switcher */}
90
- <div className="bg-zinc-900 rounded-lg p-3">
91
- <h3 className="text-sm font-bold text-zinc-400 mb-2">Current Player</h3>
92
- <select value={playerIndex} onChange={(e) => setPlayerIndex(Number(e.target.value))} className="w-full bg-zinc-800 border border-zinc-700 rounded px-2 py-1">
93
- {mockPlayers.map((p, i) => (<option key={p.id} value={i}>{p.nickname}</option>))}
94
- </select>
95
- </div>
96
-
97
- {/* State Editor */}
98
- <div className="bg-zinc-900 rounded-lg p-3 flex-1 flex flex-col">
99
- <h3 className="text-sm font-bold text-zinc-400 mb-2">Game State</h3>
100
- <textarea value={stateJson} onChange={(e) => setStateJson(e.target.value)} className="flex-1 bg-zinc-800 border border-zinc-700 rounded p-2 font-mono text-xs resize-none"/>
101
- <button onClick={updateState} className="mt-2 bg-amber-600 hover:bg-amber-500 text-white px-3 py-1 rounded text-sm">
102
- Apply State
103
- </button>
104
- </div>
105
-
106
- {/* Action Log */}
107
- <div className="bg-zinc-900 rounded-lg p-3 h-48 overflow-auto">
108
- <h3 className="text-sm font-bold text-zinc-400 mb-2">Action Log</h3>
109
- {actions.length === 0 ? (<p className="text-zinc-500 text-xs">No actions yet</p>) : (<div className="space-y-1">
110
- {actions.map((a, i) => (<div key={i} className="text-xs font-mono bg-zinc-800 rounded p-1">
111
- <span className="text-amber-400">{a.player}</span>: {JSON.stringify(a.action)}
112
- </div>))}
113
- </div>)}
114
- </div>
115
- </div>
116
- </div>);
117
- }
118
- //# sourceMappingURL=Preview.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"Preview.js","sourceRoot":"","sources":["../../../src/webapp/pages/Preview.tsx"],"names":[],"mappings":"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"}