@puzzmo/sdk 1.0.15 → 1.0.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +33 -0
- package/dist/{createSimulator-IMuPxYe-.js → createSimulator-BwucCTnM.js} +174 -25
- package/dist/createSimulator-BwucCTnM.js.map +1 -0
- package/dist/{createSimulator-CGTMmToi.cjs → createSimulator-HQPoM1pd.cjs} +85 -4
- package/dist/createSimulator-HQPoM1pd.cjs.map +1 -0
- package/dist/fonts.cjs +3 -0
- package/dist/fonts.cjs.map +1 -0
- package/dist/fonts.d.ts +26 -0
- package/dist/fonts.d.ts.map +1 -0
- package/dist/fonts.js +29 -0
- package/dist/fonts.js.map +1 -0
- package/dist/simulator/createSimulator.d.ts +11 -14
- package/dist/simulator/createSimulator.d.ts.map +1 -1
- package/dist/simulator/index.cjs +1 -1
- package/dist/simulator/index.js +1 -1
- package/dist/simulator/standalone.cjs +1 -1
- package/dist/simulator/standalone.js +1 -1
- package/dist/simulator/styles.d.ts +1 -1
- package/dist/simulator/styles.d.ts.map +1 -1
- package/dist/simulator/views/AuthView.d.ts.map +1 -1
- package/dist/simulator/views/KeyboardView.d.ts +3 -0
- package/dist/simulator/views/KeyboardView.d.ts.map +1 -0
- package/dist/simulator/views/index.d.ts +1 -0
- package/dist/simulator/views/index.d.ts.map +1 -1
- package/dist/svgJSX.cjs +2 -0
- package/dist/svgJSX.cjs.map +1 -0
- package/dist/svgJSX.d.ts +16 -0
- package/dist/svgJSX.d.ts.map +1 -0
- package/dist/svgJSX.js +60 -0
- package/dist/svgJSX.js.map +1 -0
- package/dist/vite.cjs +3 -3
- package/dist/vite.cjs.map +1 -1
- package/dist/vite.d.ts +18 -4
- package/dist/vite.d.ts.map +1 -1
- package/dist/vite.js +91 -47
- package/dist/vite.js.map +1 -1
- package/package.json +12 -1
- package/dist/createSimulator-CGTMmToi.cjs.map +0 -1
- package/dist/createSimulator-IMuPxYe-.js.map +0 -1
package/README.md
CHANGED
|
@@ -189,6 +189,39 @@ This and the editor bundle are separate JavaScript files from your main game.
|
|
|
189
189
|
|
|
190
190
|
There are Vite plugins to make this easy, but otherwise, they should be files in your upload named `app-bundle.js` and `editor-bundle.js` with ESM exports which match the shapes of the TypeScript types.
|
|
191
191
|
|
|
192
|
+
### Thumbnail JSX
|
|
193
|
+
|
|
194
|
+
We have found over time that using JSX for thumbnails makes it a lot easier to ensure correct SVG output, but React/Preact are big runtimes, so we have a smaller JSX runtime built just for non-interactive SVGs based on [understated](https://github.com/callmecavs/understated).
|
|
195
|
+
|
|
196
|
+
To use it, configure your file's JSX pragma to use `h` and `render` from `@puzzmo/sdk/svgJSX`:
|
|
197
|
+
|
|
198
|
+
```tsx
|
|
199
|
+
/** @jsxRuntime classic @jsx h */
|
|
200
|
+
import { h, render } from "@puzzmo/sdk/svgJSX"
|
|
201
|
+
|
|
202
|
+
// Needed for fragments
|
|
203
|
+
const React = { Fragment: "g" }
|
|
204
|
+
|
|
205
|
+
export function renderThumbnail(puzzleStr: string, inputStr?: string, config?: ThumbnailConfig): string {
|
|
206
|
+
const puzzle = JSON.parse(puzzleStr)
|
|
207
|
+
const size = 200
|
|
208
|
+
|
|
209
|
+
const svg = (
|
|
210
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox={`0 0 ${size} ${size}`}>
|
|
211
|
+
<rect width={size} height={size} fill={config?.theme?.g_bg ?? "#1a1a2e"} />
|
|
212
|
+
<text x={size / 2} y={size / 2} textAnchor="middle" fill={config?.theme?.fg ?? "#fff"} fontSize="24">
|
|
213
|
+
{puzzle.title}
|
|
214
|
+
</text>
|
|
215
|
+
</svg>
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
const el = render(svg)
|
|
219
|
+
return el instanceof Element ? el.outerHTML : ""
|
|
220
|
+
}
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
The `h` function is a JSX factory that creates virtual DOM nodes, and `render` converts them into real DOM elements. Since thumbnails can run server-side or in a DOM-shimmed environment, the result is serialized to an SVG string via `outerHTML`.
|
|
224
|
+
|
|
192
225
|
## Editor Integration
|
|
193
226
|
|
|
194
227
|
For games that support puzzle editing in Puzzmo Workshop, you will need an Editor Bundle:
|
|
@@ -993,10 +993,7 @@ var F = "puzzmo_sim_api_mode", I = "http://localhost:8911", L = "https://api.puz
|
|
|
993
993
|
return crypto.getRandomValues(e), Array.from(e, (e) => e.toString(16).padStart(2, "0")).join("");
|
|
994
994
|
}, W = "puzzmo_sim_oauth_token", G = "puzzmo_sim_oauth_refresh_token", K = (e) => localStorage.setItem(W, e), ue = (e) => localStorage.setItem(G, e), q = () => {
|
|
995
995
|
let e = localStorage.getItem(W);
|
|
996
|
-
return
|
|
997
|
-
TOKEN_KEY: W,
|
|
998
|
-
token: e ? `${e.substring(0, 20)}...` : null
|
|
999
|
-
}), e;
|
|
996
|
+
return e && `${e.substring(0, 20)}`, e;
|
|
1000
997
|
}, J = () => localStorage.getItem(G), Y = () => {
|
|
1001
998
|
localStorage.removeItem(W), localStorage.removeItem(G);
|
|
1002
999
|
}, de = () => {
|
|
@@ -1016,7 +1013,7 @@ var F = "puzzmo_sim_api_mode", I = "http://localhost:8911", L = "https://api.puz
|
|
|
1016
1013
|
}, pe = function() {
|
|
1017
1014
|
var t = e(function* () {
|
|
1018
1015
|
let e = J();
|
|
1019
|
-
if (!e) return
|
|
1016
|
+
if (!e) return !1;
|
|
1020
1017
|
let t = H();
|
|
1021
1018
|
try {
|
|
1022
1019
|
let n = new URLSearchParams({
|
|
@@ -1028,14 +1025,14 @@ var F = "puzzmo_sim_api_mode", I = "http://localhost:8911", L = "https://api.puz
|
|
|
1028
1025
|
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
1029
1026
|
body: n.toString()
|
|
1030
1027
|
});
|
|
1031
|
-
if (!r.ok) return
|
|
1028
|
+
if (!r.ok) return r.statusText, !1;
|
|
1032
1029
|
let i = yield r.json(), a = i.access_token || i.accessToken;
|
|
1033
|
-
if (!a) return
|
|
1030
|
+
if (!a) return !1;
|
|
1034
1031
|
K(a);
|
|
1035
1032
|
let o = i.refresh_token || i.refreshToken;
|
|
1036
|
-
return o && ue(o),
|
|
1033
|
+
return o && ue(o), !0;
|
|
1037
1034
|
} catch (e) {
|
|
1038
|
-
return
|
|
1035
|
+
return !1;
|
|
1039
1036
|
}
|
|
1040
1037
|
});
|
|
1041
1038
|
return function() {
|
|
@@ -1044,7 +1041,7 @@ var F = "puzzmo_sim_api_mode", I = "http://localhost:8911", L = "https://api.puz
|
|
|
1044
1041
|
}(), me = function() {
|
|
1045
1042
|
var t = e(function* (e, t) {
|
|
1046
1043
|
let n = H(), r = sessionStorage.getItem("oauth_state");
|
|
1047
|
-
if (!r || r !== t) return
|
|
1044
|
+
if (!r || r !== t) return null;
|
|
1048
1045
|
sessionStorage.removeItem("oauth_state");
|
|
1049
1046
|
try {
|
|
1050
1047
|
let t = new URLSearchParams({
|
|
@@ -1057,9 +1054,9 @@ var F = "puzzmo_sim_api_mode", I = "http://localhost:8911", L = "https://api.puz
|
|
|
1057
1054
|
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
1058
1055
|
body: t.toString()
|
|
1059
1056
|
});
|
|
1060
|
-
return r.ok ? yield r.json() : (
|
|
1057
|
+
return r.ok ? yield r.json() : (r.statusText, null);
|
|
1061
1058
|
} catch (e) {
|
|
1062
|
-
return
|
|
1059
|
+
return null;
|
|
1063
1060
|
}
|
|
1064
1061
|
});
|
|
1065
1062
|
return function(e, n) {
|
|
@@ -1069,7 +1066,7 @@ var F = "puzzmo_sim_api_mode", I = "http://localhost:8911", L = "https://api.puz
|
|
|
1069
1066
|
var t = e(function* (e, t = {}) {
|
|
1070
1067
|
let n = H(), r = q();
|
|
1071
1068
|
if (!r) throw Error("Not authenticated");
|
|
1072
|
-
if (fe(r)) if (
|
|
1069
|
+
if (fe(r)) if (yield pe()) {
|
|
1073
1070
|
if (r = q(), !r) throw Error("Token refresh succeeded but no token available");
|
|
1074
1071
|
} else throw Y(), Error("Session expired. Please log in again.");
|
|
1075
1072
|
let i = yield fetch(`${n.apiURL}/graphql`, {
|
|
@@ -1092,13 +1089,10 @@ var F = "puzzmo_sim_api_mode", I = "http://localhost:8911", L = "https://api.puz
|
|
|
1092
1089
|
};
|
|
1093
1090
|
}(), ge = (e) => {
|
|
1094
1091
|
try {
|
|
1095
|
-
console.log("[AuthView] decodeJWT input:", e);
|
|
1096
1092
|
let t = e.split(".");
|
|
1097
|
-
|
|
1098
|
-
let n = JSON.parse(atob(t[1]));
|
|
1099
|
-
return console.log("[AuthView] JWT payload:", n), n;
|
|
1093
|
+
return t.length, t.length === 3 ? JSON.parse(atob(t[1])) : null;
|
|
1100
1094
|
} catch (e) {
|
|
1101
|
-
return
|
|
1095
|
+
return null;
|
|
1102
1096
|
}
|
|
1103
1097
|
};
|
|
1104
1098
|
function _e() {
|
|
@@ -1110,7 +1104,7 @@ function _e() {
|
|
|
1110
1104
|
let e = q(), t = !!e, n = R(), r = n === "dev" ? I : L, i = `<button class="simulator-btn tiny" id="auth-dev-toggle" style="display: none;">${n === "dev" ? "Using Dev" : "Dev"}</button>`;
|
|
1111
1105
|
if (t) {
|
|
1112
1106
|
let t = ge(e), n = t != null && t.exp ? (/* @__PURE__ */ new Date(t.exp * 1e3)).toLocaleString() : "Unknown", a = !!J(), o = a ? ge(J()) : null, s = o != null && o.exp ? (/* @__PURE__ */ new Date(o.exp * 1e3)).toLocaleString() : null;
|
|
1113
|
-
return
|
|
1107
|
+
return `
|
|
1114
1108
|
<div class="simulator-section">
|
|
1115
1109
|
<div class="simulator-section-title auth-title-row">
|
|
1116
1110
|
<span>Puzzmo Authentication</span>
|
|
@@ -1408,12 +1402,91 @@ function Ne() {
|
|
|
1408
1402
|
}
|
|
1409
1403
|
};
|
|
1410
1404
|
}
|
|
1405
|
+
/** Renders a single keyboard key as an HTML button string */
|
|
1406
|
+
var Pe = (e, t) => {
|
|
1407
|
+
var n, r;
|
|
1408
|
+
let i = (n = t.symbols[e]) == null ? e : n, a = t.disabled.includes(e), o = t.highlight.includes(e), s = t.xl.includes(e), c = t.l.includes(e), l = (r = t.flexGrowSymbols) == null ? void 0 : r.includes(e);
|
|
1409
|
+
return `<button class="${[
|
|
1410
|
+
"sim-kb-key",
|
|
1411
|
+
a ? "disabled" : "",
|
|
1412
|
+
o ? "highlight" : "",
|
|
1413
|
+
s ? "xl" : "",
|
|
1414
|
+
c ? "l" : "",
|
|
1415
|
+
l ? "grow" : ""
|
|
1416
|
+
].filter(Boolean).join(" ")}" data-key="${e}" ${a ? "disabled" : ""}>${i}</button>`;
|
|
1417
|
+
}, Fe = (e) => `<div class="sim-kb">${e.layout.filter((e) => e != null).map((t) => `<div class="sim-kb-row">${[...t].map((t) => Pe(t, e)).join("")}</div>`).join("")}</div>`;
|
|
1418
|
+
function Ie() {
|
|
1419
|
+
let e = null, t = () => e ? Fe(e) : "<div class=\"sim-kb-empty\">No keyboard config received from game yet.<br>The game calls <code>sdk.keyboard.show(config)</code> to display a keyboard.</div>";
|
|
1420
|
+
return {
|
|
1421
|
+
id: "kbd",
|
|
1422
|
+
label: "Kbd",
|
|
1423
|
+
render() {
|
|
1424
|
+
return `
|
|
1425
|
+
<div class="keyboard-view-container">
|
|
1426
|
+
<div id="sim-kb-content">
|
|
1427
|
+
${t()}
|
|
1428
|
+
</div>
|
|
1429
|
+
</div>
|
|
1430
|
+
`;
|
|
1431
|
+
},
|
|
1432
|
+
bind(e) {
|
|
1433
|
+
Le(e);
|
|
1434
|
+
},
|
|
1435
|
+
onMessage(n, r, i) {
|
|
1436
|
+
var a;
|
|
1437
|
+
if (n !== "KEYBOARD_UPDATE_CONFIG") return;
|
|
1438
|
+
e = !(!(r == null || (a = r.layout) == null) && a.length) || r.layout.every((e) => !e) ? null : r;
|
|
1439
|
+
let o = i.getElement("#sim-kb-content");
|
|
1440
|
+
o && (o.innerHTML = t(), Le(i)), i.updateBadge("kbd", void 0);
|
|
1441
|
+
}
|
|
1442
|
+
};
|
|
1443
|
+
}
|
|
1444
|
+
/** Attach click handlers to all rendered keys */
|
|
1445
|
+
function Le(e) {
|
|
1446
|
+
var t;
|
|
1447
|
+
let n = (t = e.getElement("#sim-kb-content")) == null ? void 0 : t.querySelectorAll(".sim-kb-key");
|
|
1448
|
+
n == null || n.forEach((t) => {
|
|
1449
|
+
t.addEventListener("click", () => {
|
|
1450
|
+
let n = t.getAttribute("data-key");
|
|
1451
|
+
n && e.sendToGame("KEYBOARD_KEY_PRESS", { key: n });
|
|
1452
|
+
});
|
|
1453
|
+
});
|
|
1454
|
+
}
|
|
1411
1455
|
var $ = null;
|
|
1412
1456
|
/**
|
|
1413
|
-
*
|
|
1414
|
-
*
|
|
1457
|
+
* Simulator - A development UI for testing games with the Puzzmo Proto SDK.
|
|
1458
|
+
*
|
|
1459
|
+
* This script simulates the Puzzmo host environment by:
|
|
1460
|
+
* - Listening for READY messages from the game
|
|
1461
|
+
* - Sending READY_DATA with puzzle data
|
|
1462
|
+
* - Providing UI controls for START_GAME, PAUSE_GAME, RESUME_GAME, RETRY_PUZZLE
|
|
1463
|
+
*
|
|
1464
|
+
* Usage with Vite plugin (recommended):
|
|
1465
|
+
*
|
|
1466
|
+
* ```ts
|
|
1467
|
+
* // vite.config.ts
|
|
1468
|
+
* import { puzzmoSimulator } from "@puzzmo/sdk/vite"
|
|
1469
|
+
* export default defineConfig({
|
|
1470
|
+
* plugins: [puzzmoSimulator({})]
|
|
1471
|
+
* })
|
|
1472
|
+
* ```
|
|
1473
|
+
*
|
|
1474
|
+
* The plugin automatically reads the game slug from the nearest puzzmo.json.
|
|
1475
|
+
*
|
|
1476
|
+
* The plugin handles making sure it is removed on vite builds.
|
|
1477
|
+
*
|
|
1478
|
+
* Usage with manual imports:
|
|
1479
|
+
* ```html
|
|
1480
|
+
* <script type="module">
|
|
1481
|
+
* import { createSimulator } from "@puzzmo/sdk/simulator"
|
|
1482
|
+
* const fixtures = import.meta.glob("./fixtures/puzzles/**\/*.json", { eager: true })
|
|
1483
|
+
* createSimulator({ fixtures })
|
|
1484
|
+
* <\/script>
|
|
1485
|
+
* ```
|
|
1486
|
+
* The fixtures folder structure should be: fixtures/puzzles/{category}/{puzzle}.json
|
|
1487
|
+
* This will show dropdowns in the Ctrl tab to select category and puzzle.
|
|
1415
1488
|
*/
|
|
1416
|
-
function
|
|
1489
|
+
function Re(t = {}) {
|
|
1417
1490
|
var n, r;
|
|
1418
1491
|
if (console.log("[Simulator] createSimulator called with config:", {
|
|
1419
1492
|
slug: t.slug,
|
|
@@ -1428,7 +1501,8 @@ function Pe(t = {}) {
|
|
|
1428
1501
|
c,
|
|
1429
1502
|
le(),
|
|
1430
1503
|
_e(),
|
|
1431
|
-
Ne()
|
|
1504
|
+
Ne(),
|
|
1505
|
+
Ie()
|
|
1432
1506
|
], p = f.map((e) => e.id), m = l(t, o, p), h = {
|
|
1433
1507
|
pause: "<svg width=\"10\" height=\"10\" viewBox=\"0 0 10 10\" fill=\"currentColor\"><rect x=\"1\" y=\"1\" width=\"3\" height=\"8\"/><rect x=\"6\" y=\"1\" width=\"3\" height=\"8\"/></svg>",
|
|
1434
1508
|
play: "<svg width=\"10\" height=\"10\" viewBox=\"0 0 10 10\" fill=\"currentColor\"><polygon points=\"2,1 9,5 2,9\"/></svg>",
|
|
@@ -1468,6 +1542,7 @@ function Pe(t = {}) {
|
|
|
1468
1542
|
border-radius: 4px;
|
|
1469
1543
|
color: var(--sim-text);
|
|
1470
1544
|
width: 420px;
|
|
1545
|
+
max-width: calc(100vw - 8px);
|
|
1471
1546
|
box-shadow: 4px 4px 0 rgba(0,0,0,0.5);
|
|
1472
1547
|
}
|
|
1473
1548
|
#simulator-panel.collapsed {
|
|
@@ -2511,6 +2586,80 @@ function Pe(t = {}) {
|
|
|
2511
2586
|
color: var(--sim-text-dim);
|
|
2512
2587
|
font-size: 9px;
|
|
2513
2588
|
}
|
|
2589
|
+
/* Keyboard view styles */
|
|
2590
|
+
.keyboard-view-container {
|
|
2591
|
+
padding: 4px;
|
|
2592
|
+
}
|
|
2593
|
+
.sim-kb-empty {
|
|
2594
|
+
color: var(--sim-text-dim);
|
|
2595
|
+
text-align: center;
|
|
2596
|
+
padding: 16px 8px;
|
|
2597
|
+
font-size: 10px;
|
|
2598
|
+
line-height: 1.6;
|
|
2599
|
+
}
|
|
2600
|
+
.sim-kb-empty code {
|
|
2601
|
+
background: var(--sim-bg);
|
|
2602
|
+
padding: 1px 4px;
|
|
2603
|
+
border-radius: 2px;
|
|
2604
|
+
font-size: 10px;
|
|
2605
|
+
}
|
|
2606
|
+
.sim-kb {
|
|
2607
|
+
display: flex;
|
|
2608
|
+
flex-direction: column;
|
|
2609
|
+
gap: 4px;
|
|
2610
|
+
}
|
|
2611
|
+
.sim-kb-row {
|
|
2612
|
+
display: flex;
|
|
2613
|
+
justify-content: center;
|
|
2614
|
+
gap: 3px;
|
|
2615
|
+
}
|
|
2616
|
+
.sim-kb-key {
|
|
2617
|
+
min-width: 28px;
|
|
2618
|
+
height: 30px;
|
|
2619
|
+
padding: 0 4px;
|
|
2620
|
+
border: 1px solid var(--sim-border);
|
|
2621
|
+
border-radius: 3px;
|
|
2622
|
+
background: var(--sim-bg);
|
|
2623
|
+
color: var(--sim-text);
|
|
2624
|
+
font: inherit;
|
|
2625
|
+
font-size: 11px;
|
|
2626
|
+
text-transform: uppercase;
|
|
2627
|
+
cursor: pointer;
|
|
2628
|
+
display: flex;
|
|
2629
|
+
align-items: center;
|
|
2630
|
+
justify-content: center;
|
|
2631
|
+
}
|
|
2632
|
+
.sim-kb-key:hover {
|
|
2633
|
+
background: var(--sim-bg-alt);
|
|
2634
|
+
border-color: var(--sim-border-light);
|
|
2635
|
+
}
|
|
2636
|
+
.sim-kb-key:active {
|
|
2637
|
+
background: var(--sim-accent);
|
|
2638
|
+
color: var(--sim-panel);
|
|
2639
|
+
}
|
|
2640
|
+
.sim-kb-key.highlight {
|
|
2641
|
+
background: var(--sim-bg-alt);
|
|
2642
|
+
border-color: var(--sim-accent);
|
|
2643
|
+
color: var(--sim-accent);
|
|
2644
|
+
font-size: 9px;
|
|
2645
|
+
}
|
|
2646
|
+
.sim-kb-key.highlight:active {
|
|
2647
|
+
background: var(--sim-accent);
|
|
2648
|
+
color: var(--sim-panel);
|
|
2649
|
+
}
|
|
2650
|
+
.sim-kb-key.l {
|
|
2651
|
+
min-width: 40px;
|
|
2652
|
+
}
|
|
2653
|
+
.sim-kb-key.xl {
|
|
2654
|
+
min-width: 52px;
|
|
2655
|
+
}
|
|
2656
|
+
.sim-kb-key.grow {
|
|
2657
|
+
flex: 1;
|
|
2658
|
+
}
|
|
2659
|
+
.sim-kb-key.disabled {
|
|
2660
|
+
opacity: 0.3;
|
|
2661
|
+
cursor: default;
|
|
2662
|
+
}
|
|
2514
2663
|
</style>
|
|
2515
2664
|
<div id="simulator-panel" class="${m.isCollapsed ? "collapsed" : ""}">
|
|
2516
2665
|
<div id="simulator-header">
|
|
@@ -2743,6 +2892,6 @@ function Pe(t = {}) {
|
|
|
2743
2892
|
loadPuzzle: I
|
|
2744
2893
|
}, $;
|
|
2745
2894
|
}
|
|
2746
|
-
export {
|
|
2895
|
+
export { Re as t };
|
|
2747
2896
|
|
|
2748
|
-
//# sourceMappingURL=createSimulator-
|
|
2897
|
+
//# sourceMappingURL=createSimulator-BwucCTnM.js.map
|