@oasiz/sdk 1.8.3 → 1.8.5

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 CHANGED
@@ -138,30 +138,129 @@ Supported `device` presets:
138
138
 
139
139
  Use the Jibble animation constants when wiring a player character atlas so you can reference known animation IDs during local development instead of publishing first and reading logs.
140
140
 
141
+ The atlas animation IDs are stable strings. Directional animations use the pattern
142
+ `<action>_<direction>`, where the direction suffix is one of `n`, `ne`, `e`, `se`,
143
+ `s`, `sw`, `w`, or `nw`.
144
+
145
+ | Player-facing direction | Compass direction | Suffix |
146
+ | --- | --- | --- |
147
+ | `front`, `forward`, `forth` | South | `s` |
148
+ | `front-right`, `forward-right` | SouthEast | `se` |
149
+ | `right` | East | `e` |
150
+ | `back-right`, `backward-right` | NorthEast | `ne` |
151
+ | `back`, `backward` | North | `n` |
152
+ | `back-left`, `backward-left` | NorthWest | `nw` |
153
+ | `left` | West | `w` |
154
+ | `front-left`, `forward-left` | SouthWest | `sw` |
155
+
156
+ You can also pass compass names directly, such as `north`, `south`, `north-east`,
157
+ or `southwest`.
158
+
159
+ #### Animation catalog
160
+
161
+ | Action | Constant | Animation IDs |
162
+ | --- | --- | --- |
163
+ | Idle | `JIBBLE_ANIMATION.Idle` | `idle_n`, `idle_ne`, `idle_e`, `idle_se`, `idle_s`, `idle_sw`, `idle_w`, `idle_nw` |
164
+ | Walk | `JIBBLE_ANIMATION.Walk` | `walk_n`, `walk_ne`, `walk_e`, `walk_se`, `walk_s`, `walk_sw`, `walk_w`, `walk_nw` |
165
+ | Backflip | `JIBBLE_ANIMATION.Backflip` | `backflip` |
166
+
167
+ `backflip` is not directional, so `getJibbleAnimationId("backflip", "left")`
168
+ still returns `backflip`.
169
+
170
+ #### Local browser sample atlas
171
+
172
+ Inside the Oasiz app, `oasiz.getPlayerCharacter()` returns the authenticated
173
+ player's real character atlas. On `localhost` or `file://` without the app
174
+ bridge, it returns a bundled sample Jibble atlas instead, so browser games can
175
+ test sprite loading, atlas slicing, and animation lookup before publishing.
176
+
177
+ The sample atlas includes every catalog ID above, and its `imageUrl` is an
178
+ inline `data:image/svg+xml` URL. It does not make a network request and it is
179
+ only used when the app bridge is missing.
180
+
181
+ ```ts
182
+ const character = await oasiz.getPlayerCharacter();
183
+
184
+ // In Oasiz app: the real signed-in player's character.
185
+ // On localhost without the app bridge: "SDK Sample Jibble".
186
+ // On non-local pages without the app bridge: null.
187
+ ```
188
+
189
+ Use the options when you need explicit behavior:
190
+
141
191
  ```ts
142
- import { JIBBLE_ANIMATION, getJibbleAnimationId, oasiz } from "@oasiz/sdk";
192
+ // Keep the old "missing bridge means null" behavior.
193
+ const characterOrNull = await oasiz.getPlayerCharacter({ localFallback: false });
194
+
195
+ // Force the sample atlas in a browser test harness.
196
+ const sampleCharacter = await oasiz.getPlayerCharacter({ localFallback: true });
197
+
198
+ // Or fetch the sample directly without checking the bridge.
199
+ const directSample = oasiz.getSamplePlayerCharacter();
200
+ ```
201
+
202
+ #### HTML5 / TypeScript
203
+
204
+ ```ts
205
+ import {
206
+ JIBBLE_ANIMATION,
207
+ JIBBLE_ANIMATION_IDS,
208
+ getJibbleAnimationId,
209
+ oasiz,
210
+ } from "@oasiz/sdk";
143
211
 
144
212
  const character = await oasiz.getPlayerCharacter();
145
213
  if (!character) return;
146
214
 
147
215
  const atlas = character.textureAtlas;
216
+
217
+ // Print the full SDK catalog while debugging.
218
+ console.table(JIBBLE_ANIMATION_IDS);
219
+
148
220
  const walkBack = getJibbleAnimationId("walk", "back"); // "walk_n"
149
221
  const walkBackAnimation = atlas.animations.find((anim) => anim.animationId === walkBack);
222
+
150
223
  const idleFrontAnimation = atlas.animations.find(
151
224
  (anim) => anim.animationId === JIBBLE_ANIMATION.Idle.South,
152
225
  );
226
+
227
+ const backflipAnimation = atlas.animations.find(
228
+ (anim) => anim.animationId === JIBBLE_ANIMATION.Backflip,
229
+ );
230
+
231
+ const availableAnimations = new Set(atlas.animations.map((anim) => anim.animationId));
232
+ const animationToPlay = availableAnimations.has(walkBack)
233
+ ? walkBack
234
+ : JIBBLE_ANIMATION.Idle.South;
153
235
  ```
154
236
 
155
- Supported directional IDs use compass codes: `n`, `ne`, `e`, `se`, `s`, `sw`, `w`, `nw`. The helper accepts gameplay-facing aliases too: `front` -> `s`, `back` -> `n`, `left` -> `w`, `right` -> `e`.
237
+ Not every custom atlas is guaranteed to contain every catalog ID. Check
238
+ `atlas.animations` before playing an animation and choose a fallback, usually
239
+ `idle_s` or the closest available direction.
156
240
 
157
- Unity exposes the same IDs:
241
+ #### Unity
158
242
 
159
243
  ```csharp
160
- var walkBack = JibbleAnimations.GetId(
244
+ using Oasiz;
245
+
246
+ var walkBackId = JibbleAnimations.GetId(
247
+ JibbleAnimationAction.Walk,
248
+ JibbleDirection.Back); // "walk_n"
249
+
250
+ var walkFrontRightId = JibbleAnimations.GetId(
161
251
  JibbleAnimationAction.Walk,
162
- JibbleDirection.Back);
252
+ JibbleDirection.FrontRight); // "walk_se"
163
253
 
164
- var idleFront = JibbleAnimations.Idle.South;
254
+ var idleFrontId = JibbleAnimations.GetId(
255
+ JibbleAnimationAction.Idle,
256
+ JibbleDirection.Front); // "idle_s"
257
+
258
+ var backflipId = JibbleAnimations.Backflip; // "backflip"
259
+
260
+ foreach (var animationId in JibbleAnimations.All)
261
+ {
262
+ Debug.Log(animationId);
263
+ }
165
264
  ```
166
265
 
167
266
  ### Score
package/dist/index.cjs CHANGED
@@ -37,6 +37,8 @@ __export(index_exports, {
37
37
  getPlayerName: () => getPlayerName,
38
38
  getRoomCode: () => getRoomCode,
39
39
  getSafeAreaTop: () => getSafeAreaTop,
40
+ getSamplePlayerCharacter: () => getSamplePlayerCharacter,
41
+ getSampleTextureAtlas: () => getSampleTextureAtlas,
40
42
  getViewportInsets: () => getViewportInsets,
41
43
  leaveGame: () => leaveGame,
42
44
  loadGameState: () => loadGameState,
@@ -1444,65 +1446,6 @@ function enableAppSimulator(options = {}) {
1444
1446
  return handle;
1445
1447
  }
1446
1448
 
1447
- // src/character.ts
1448
- function isDevelopment2() {
1449
- const nodeEnv = globalThis.process?.env?.NODE_ENV;
1450
- return nodeEnv !== "production";
1451
- }
1452
- function getBridgeWindow2() {
1453
- if (typeof window === "undefined") {
1454
- return void 0;
1455
- }
1456
- return window;
1457
- }
1458
- function warnMissingBridge2(methodName) {
1459
- if (isDevelopment2()) {
1460
- console.warn(
1461
- "[oasiz/sdk] " + methodName + " bridge is unavailable. This is expected in local development."
1462
- );
1463
- }
1464
- }
1465
- async function getPlayerCharacter() {
1466
- const bridge = getBridgeWindow2();
1467
- if (typeof bridge?.__oasizGetPlayerCharacter !== "function") {
1468
- warnMissingBridge2("getPlayerCharacter");
1469
- return null;
1470
- }
1471
- try {
1472
- const result = await bridge.__oasizGetPlayerCharacter();
1473
- return result ?? null;
1474
- } catch (error) {
1475
- if (isDevelopment2()) {
1476
- console.error("[oasiz/sdk] getPlayerCharacter failed:", error);
1477
- }
1478
- return null;
1479
- }
1480
- }
1481
-
1482
- // src/haptics.ts
1483
- function isDevelopment3() {
1484
- const nodeEnv = globalThis.process?.env?.NODE_ENV;
1485
- return nodeEnv !== "production";
1486
- }
1487
- function getBridgeWindow3() {
1488
- if (typeof window === "undefined") {
1489
- return void 0;
1490
- }
1491
- return window;
1492
- }
1493
- function triggerHaptic(type) {
1494
- const bridge = getBridgeWindow3();
1495
- if (typeof bridge?.triggerHaptic === "function") {
1496
- bridge.triggerHaptic(type);
1497
- return;
1498
- }
1499
- if (isDevelopment3()) {
1500
- console.warn(
1501
- "[oasiz/sdk] triggerHaptic bridge is unavailable. This is expected in local development."
1502
- );
1503
- }
1504
- }
1505
-
1506
1449
  // src/jibble.ts
1507
1450
  var JIBBLE_DIRECTIONS = [
1508
1451
  "n",
@@ -1514,6 +1457,43 @@ var JIBBLE_DIRECTIONS = [
1514
1457
  "w",
1515
1458
  "nw"
1516
1459
  ];
1460
+ var JIBBLE_DIRECTION_ALIASES = {
1461
+ n: "n",
1462
+ ne: "ne",
1463
+ e: "e",
1464
+ se: "se",
1465
+ s: "s",
1466
+ sw: "sw",
1467
+ w: "w",
1468
+ nw: "nw",
1469
+ north: "n",
1470
+ "north-east": "ne",
1471
+ northeast: "ne",
1472
+ east: "e",
1473
+ "south-east": "se",
1474
+ southeast: "se",
1475
+ south: "s",
1476
+ "south-west": "sw",
1477
+ southwest: "sw",
1478
+ west: "w",
1479
+ "north-west": "nw",
1480
+ northwest: "nw",
1481
+ front: "s",
1482
+ forward: "s",
1483
+ forth: "s",
1484
+ back: "n",
1485
+ backward: "n",
1486
+ left: "w",
1487
+ right: "e",
1488
+ "front-right": "se",
1489
+ "forward-right": "se",
1490
+ "front-left": "sw",
1491
+ "forward-left": "sw",
1492
+ "back-right": "ne",
1493
+ "backward-right": "ne",
1494
+ "back-left": "nw",
1495
+ "backward-left": "nw"
1496
+ };
1517
1497
  var JIBBLE_ANIMATION = {
1518
1498
  Idle: {
1519
1499
  North: "idle_n",
@@ -1556,20 +1536,6 @@ var JIBBLE_ANIMATION_IDS = [
1556
1536
  JIBBLE_ANIMATION.Walk.NorthWest,
1557
1537
  JIBBLE_ANIMATION.Backflip
1558
1538
  ];
1559
- var JIBBLE_DIRECTION_ALIASES = {
1560
- n: "n",
1561
- ne: "ne",
1562
- e: "e",
1563
- se: "se",
1564
- s: "s",
1565
- sw: "sw",
1566
- w: "w",
1567
- nw: "nw",
1568
- front: "s",
1569
- back: "n",
1570
- left: "w",
1571
- right: "e"
1572
- };
1573
1539
  function normalizeJibbleDirection(direction) {
1574
1540
  return JIBBLE_DIRECTION_ALIASES[direction];
1575
1541
  }
@@ -1580,6 +1546,223 @@ function getJibbleAnimationId(action, direction = "front") {
1580
1546
  return `${action}_${normalizeJibbleDirection(direction)}`;
1581
1547
  }
1582
1548
 
1549
+ // src/sample-character.ts
1550
+ var FRAME_SIZE = 32;
1551
+ var COLUMNS = 8;
1552
+ var WALK_FRAME_COUNT = 2;
1553
+ var BACKFLIP_FRAME_COUNT = 4;
1554
+ var COLORS = [
1555
+ "#2563eb",
1556
+ "#7c3aed",
1557
+ "#db2777",
1558
+ "#ea580c",
1559
+ "#16a34a",
1560
+ "#0891b2",
1561
+ "#4f46e5",
1562
+ "#be123c"
1563
+ ];
1564
+ var IDLE_FRAME_NAMES = JIBBLE_DIRECTIONS.map((direction) => `idle_${direction}`);
1565
+ var WALK_FRAME_NAMES = JIBBLE_DIRECTIONS.flatMap(
1566
+ (direction) => Array.from({ length: WALK_FRAME_COUNT }, (_, index) => `walk_${direction}_${index}`)
1567
+ );
1568
+ var BACKFLIP_FRAME_NAMES = Array.from(
1569
+ { length: BACKFLIP_FRAME_COUNT },
1570
+ (_, index) => `backflip_${index}`
1571
+ );
1572
+ var SAMPLE_FRAME_NAMES = [
1573
+ ...IDLE_FRAME_NAMES,
1574
+ ...WALK_FRAME_NAMES,
1575
+ ...BACKFLIP_FRAME_NAMES
1576
+ ];
1577
+ var IMAGE_WIDTH = COLUMNS * FRAME_SIZE;
1578
+ var IMAGE_HEIGHT = Math.ceil(SAMPLE_FRAME_NAMES.length / COLUMNS) * FRAME_SIZE;
1579
+ function getFrameRect(index) {
1580
+ return {
1581
+ x: index % COLUMNS * FRAME_SIZE,
1582
+ y: Math.floor(index / COLUMNS) * FRAME_SIZE,
1583
+ width: FRAME_SIZE,
1584
+ height: FRAME_SIZE
1585
+ };
1586
+ }
1587
+ function createFrame(name, index) {
1588
+ return {
1589
+ name,
1590
+ ...getFrameRect(index)
1591
+ };
1592
+ }
1593
+ function getFrameLabel(name) {
1594
+ return name.replace(/^idle_/, "i ").replace(/^walk_/, "w ").replace(/^backflip_/, "flip ");
1595
+ }
1596
+ function createSvgDataUrl() {
1597
+ const cells = SAMPLE_FRAME_NAMES.map((name, index) => {
1598
+ const { x, y } = getFrameRect(index);
1599
+ const color = COLORS[index % COLORS.length];
1600
+ const label = getFrameLabel(name);
1601
+ return [
1602
+ `<rect x="${x}" y="${y}" width="${FRAME_SIZE}" height="${FRAME_SIZE}" fill="${color}"/>`,
1603
+ `<rect x="${x + 1}" y="${y + 1}" width="${FRAME_SIZE - 2}" height="${FRAME_SIZE - 2}" fill="none" stroke="#ffffff" stroke-opacity="0.35"/>`,
1604
+ `<circle cx="${x + 16}" cy="${y + 11}" r="5" fill="#ffffff" fill-opacity="0.92"/>`,
1605
+ `<rect x="${x + 12}" y="${y + 17}" width="8" height="7" rx="2" fill="#ffffff" fill-opacity="0.86"/>`,
1606
+ `<text x="${x + 16}" y="${y + 30}" text-anchor="middle" font-family="monospace" font-size="5" fill="#ffffff">${label}</text>`
1607
+ ].join("");
1608
+ }).join("");
1609
+ const svg = [
1610
+ `<svg xmlns="http://www.w3.org/2000/svg" width="${IMAGE_WIDTH}" height="${IMAGE_HEIGHT}" viewBox="0 0 ${IMAGE_WIDTH} ${IMAGE_HEIGHT}">`,
1611
+ `<rect width="${IMAGE_WIDTH}" height="${IMAGE_HEIGHT}" fill="#111827"/>`,
1612
+ cells,
1613
+ "</svg>"
1614
+ ].join("");
1615
+ return `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svg)}`;
1616
+ }
1617
+ function createDirectionalAnimation(animationId, role, direction, frames, frameRate) {
1618
+ return {
1619
+ animationId,
1620
+ role,
1621
+ group: "jibble",
1622
+ direction,
1623
+ frameRate,
1624
+ frames,
1625
+ facingFrameMap: null
1626
+ };
1627
+ }
1628
+ var SAMPLE_TEXTURE_ATLAS = {
1629
+ imageUrl: createSvgDataUrl(),
1630
+ imageWidth: IMAGE_WIDTH,
1631
+ imageHeight: IMAGE_HEIGHT,
1632
+ frames: SAMPLE_FRAME_NAMES.map(createFrame),
1633
+ animations: [
1634
+ ...JIBBLE_DIRECTIONS.map(
1635
+ (direction) => createDirectionalAnimation(
1636
+ getJibbleAnimationId("idle", direction),
1637
+ "idle",
1638
+ direction,
1639
+ [`idle_${direction}`],
1640
+ 1
1641
+ )
1642
+ ),
1643
+ ...JIBBLE_DIRECTIONS.map(
1644
+ (direction) => createDirectionalAnimation(
1645
+ getJibbleAnimationId("walk", direction),
1646
+ "walk",
1647
+ direction,
1648
+ Array.from({ length: WALK_FRAME_COUNT }, (_, index) => `walk_${direction}_${index}`),
1649
+ 8
1650
+ )
1651
+ ),
1652
+ {
1653
+ animationId: JIBBLE_ANIMATION.Backflip,
1654
+ role: "action",
1655
+ group: "jibble",
1656
+ direction: null,
1657
+ frameRate: 10,
1658
+ frames: BACKFLIP_FRAME_NAMES,
1659
+ facingFrameMap: null
1660
+ }
1661
+ ]
1662
+ };
1663
+ function cloneTextureAtlas(atlas) {
1664
+ return {
1665
+ ...atlas,
1666
+ frames: atlas.frames.map((frame) => ({ ...frame })),
1667
+ animations: atlas.animations.map((animation) => ({
1668
+ ...animation,
1669
+ frames: [...animation.frames],
1670
+ facingFrameMap: animation.facingFrameMap ? { ...animation.facingFrameMap } : null
1671
+ }))
1672
+ };
1673
+ }
1674
+ function getSampleTextureAtlas() {
1675
+ return cloneTextureAtlas(SAMPLE_TEXTURE_ATLAS);
1676
+ }
1677
+ function getSamplePlayerCharacter() {
1678
+ return {
1679
+ characterName: "SDK Sample Jibble",
1680
+ baseCharacterId: "sdk-sample-jibble",
1681
+ compositionCode: "sdk-sample-jibble-atlas-v1",
1682
+ textureAtlas: getSampleTextureAtlas(),
1683
+ editorTextureAtlas: getSampleTextureAtlas()
1684
+ };
1685
+ }
1686
+
1687
+ // src/character.ts
1688
+ function isDevelopment2() {
1689
+ const nodeEnv = globalThis.process?.env?.NODE_ENV;
1690
+ return nodeEnv !== "production";
1691
+ }
1692
+ function getBridgeWindow2() {
1693
+ if (typeof window === "undefined") {
1694
+ return void 0;
1695
+ }
1696
+ return window;
1697
+ }
1698
+ function isLocalHostname(hostname) {
1699
+ return hostname === "localhost" || hostname === "127.0.0.1" || hostname === "0.0.0.0" || hostname === "::1" || hostname.endsWith(".localhost");
1700
+ }
1701
+ function shouldUseLocalFallback(bridge, localFallback) {
1702
+ if (localFallback === true) {
1703
+ return true;
1704
+ }
1705
+ if (localFallback === false) {
1706
+ return false;
1707
+ }
1708
+ const location = bridge?.location;
1709
+ if (!location) {
1710
+ return false;
1711
+ }
1712
+ return location.protocol === "file:" || isLocalHostname(location.hostname);
1713
+ }
1714
+ function warnMissingBridge2(methodName, usingFallback) {
1715
+ if (isDevelopment2()) {
1716
+ console.warn(
1717
+ "[oasiz/sdk] " + methodName + " bridge is unavailable. " + (usingFallback ? "Using the SDK sample character atlas for local development." : "This is expected in local development.")
1718
+ );
1719
+ }
1720
+ }
1721
+ async function getPlayerCharacter(options = {}) {
1722
+ const bridge = getBridgeWindow2();
1723
+ if (typeof bridge?.__oasizGetPlayerCharacter !== "function") {
1724
+ const useLocalFallback = shouldUseLocalFallback(
1725
+ bridge,
1726
+ options.localFallback ?? "auto"
1727
+ );
1728
+ warnMissingBridge2("getPlayerCharacter", useLocalFallback);
1729
+ return useLocalFallback ? getSamplePlayerCharacter() : null;
1730
+ }
1731
+ try {
1732
+ const result = await bridge.__oasizGetPlayerCharacter();
1733
+ return result ?? null;
1734
+ } catch (error) {
1735
+ if (isDevelopment2()) {
1736
+ console.error("[oasiz/sdk] getPlayerCharacter failed:", error);
1737
+ }
1738
+ return null;
1739
+ }
1740
+ }
1741
+
1742
+ // src/haptics.ts
1743
+ function isDevelopment3() {
1744
+ const nodeEnv = globalThis.process?.env?.NODE_ENV;
1745
+ return nodeEnv !== "production";
1746
+ }
1747
+ function getBridgeWindow3() {
1748
+ if (typeof window === "undefined") {
1749
+ return void 0;
1750
+ }
1751
+ return window;
1752
+ }
1753
+ function triggerHaptic(type) {
1754
+ const bridge = getBridgeWindow3();
1755
+ if (typeof bridge?.triggerHaptic === "function") {
1756
+ bridge.triggerHaptic(type);
1757
+ return;
1758
+ }
1759
+ if (isDevelopment3()) {
1760
+ console.warn(
1761
+ "[oasiz/sdk] triggerHaptic bridge is unavailable. This is expected in local development."
1762
+ );
1763
+ }
1764
+ }
1765
+
1583
1766
  // src/log-overlay.ts
1584
1767
  var CONSOLE_METHODS = [
1585
1768
  "debug",
@@ -3433,6 +3616,8 @@ var oasiz = {
3433
3616
  addScore,
3434
3617
  setScore,
3435
3618
  getPlayerCharacter,
3619
+ getSamplePlayerCharacter,
3620
+ getSampleTextureAtlas,
3436
3621
  share,
3437
3622
  triggerHaptic,
3438
3623
  enableLogOverlay,
@@ -3495,6 +3680,8 @@ var oasiz = {
3495
3680
  getPlayerName,
3496
3681
  getRoomCode,
3497
3682
  getSafeAreaTop,
3683
+ getSamplePlayerCharacter,
3684
+ getSampleTextureAtlas,
3498
3685
  getViewportInsets,
3499
3686
  leaveGame,
3500
3687
  loadGameState,
package/dist/index.d.cts CHANGED
@@ -220,13 +220,22 @@ interface ScoreEditResult {
220
220
  normalizedScore: number | null;
221
221
  }
222
222
 
223
+ interface GetPlayerCharacterOptions {
224
+ /**
225
+ * Controls the SDK sample character returned when the Oasiz app bridge is
226
+ * missing. `"auto"` returns the sample on localhost/file URLs only.
227
+ */
228
+ localFallback?: boolean | "auto";
229
+ }
223
230
  /**
224
231
  * Fetch the authenticated player's character, including a TexturePacker /
225
232
  * Phaser-style texture atlas describing the baked sprite image.
226
233
  *
227
234
  * Returns:
228
235
  * - `null` when the user has no character composition yet, OR
229
- * - `null` when the host bridge is unavailable (local dev / unauthenticated)
236
+ * - a sample character atlas when the host bridge is unavailable on a local
237
+ * browser URL (`localhost` / `file://`), OR
238
+ * - `null` when the host bridge is unavailable and local fallback is disabled
230
239
  *
231
240
  * The host transparently caches and proxies to `GET /api/sdk/me/character`,
232
241
  * so calling this multiple times in a session is cheap. The returned
@@ -245,13 +254,50 @@ interface ScoreEditResult {
245
254
  * ),
246
255
  * });
247
256
  */
248
- declare function getPlayerCharacter(): Promise<PlayerCharacter | null>;
257
+ declare function getPlayerCharacter(options?: GetPlayerCharacterOptions): Promise<PlayerCharacter | null>;
249
258
 
250
259
  declare function triggerHaptic(type: HapticType): void;
251
260
 
252
261
  declare const JIBBLE_DIRECTIONS: readonly ["n", "ne", "e", "se", "s", "sw", "w", "nw"];
253
262
  type JibbleDirectionCode = (typeof JIBBLE_DIRECTIONS)[number];
254
- type JibbleFacingDirection = JibbleDirectionCode | "front" | "back" | "left" | "right";
263
+ declare const JIBBLE_DIRECTION_ALIASES: {
264
+ readonly n: "n";
265
+ readonly ne: "ne";
266
+ readonly e: "e";
267
+ readonly se: "se";
268
+ readonly s: "s";
269
+ readonly sw: "sw";
270
+ readonly w: "w";
271
+ readonly nw: "nw";
272
+ readonly north: "n";
273
+ readonly "north-east": "ne";
274
+ readonly northeast: "ne";
275
+ readonly east: "e";
276
+ readonly "south-east": "se";
277
+ readonly southeast: "se";
278
+ readonly south: "s";
279
+ readonly "south-west": "sw";
280
+ readonly southwest: "sw";
281
+ readonly west: "w";
282
+ readonly "north-west": "nw";
283
+ readonly northwest: "nw";
284
+ readonly front: "s";
285
+ readonly forward: "s";
286
+ readonly forth: "s";
287
+ readonly back: "n";
288
+ readonly backward: "n";
289
+ readonly left: "w";
290
+ readonly right: "e";
291
+ readonly "front-right": "se";
292
+ readonly "forward-right": "se";
293
+ readonly "front-left": "sw";
294
+ readonly "forward-left": "sw";
295
+ readonly "back-right": "ne";
296
+ readonly "backward-right": "ne";
297
+ readonly "back-left": "nw";
298
+ readonly "backward-left": "nw";
299
+ };
300
+ type JibbleFacingDirection = keyof typeof JIBBLE_DIRECTION_ALIASES;
255
301
  type JibbleAnimationAction = "idle" | "walk" | "backflip";
256
302
  declare const JIBBLE_ANIMATION: {
257
303
  readonly Idle: {
@@ -344,6 +390,9 @@ declare function loadGameState(): GameState;
344
390
  declare function saveGameState(state: GameState): void;
345
391
  declare function flushGameState(): void;
346
392
 
393
+ declare function getSampleTextureAtlas(): TextureAtlas;
394
+ declare function getSamplePlayerCharacter(): PlayerCharacter;
395
+
347
396
  declare const oasiz: {
348
397
  submitScore: typeof submitScore;
349
398
  enableAppSimulator: typeof enableAppSimulator;
@@ -376,6 +425,8 @@ declare const oasiz: {
376
425
  addScore: typeof addScore;
377
426
  setScore: typeof setScore;
378
427
  getPlayerCharacter: typeof getPlayerCharacter;
428
+ getSamplePlayerCharacter: typeof getSamplePlayerCharacter;
429
+ getSampleTextureAtlas: typeof getSampleTextureAtlas;
379
430
  share: typeof share;
380
431
  triggerHaptic: typeof triggerHaptic;
381
432
  enableLogOverlay: typeof enableLogOverlay;
@@ -404,4 +455,4 @@ declare const oasiz: {
404
455
  readonly graphicsPerformance: GraphicsPerformanceMetric;
405
456
  };
406
457
 
407
- export { type AppSimulatorDevice, type AppSimulatorDeviceName, type AppSimulatorHandle, type AppSimulatorOptions, type AppSimulatorOrientation, type BackButtonTestingHandle, type BackButtonTestingOptions, type FacingFrameMap, type GameState, type GraphicsPerformanceMetric, type GraphicsPerformanceTier, type HapticType, JIBBLE_ANIMATION, JIBBLE_ANIMATION_IDS, JIBBLE_DIRECTIONS, type JibbleAnimationAction, type JibbleAnimationId, type JibbleDirectionCode, type JibbleFacingDirection, type LogOverlayEntry, type LogOverlayHandle, type LogOverlayLevel, type LogOverlayOptions, type PlayerCharacter, type ScoreEditResult, type ShareRequest, type ShareRoomCodeOptions, type TextureAtlas, type TextureAtlasAnimation, type TextureAtlasFrame, type Unsubscribe, type ViewportInsetEdges, type ViewportInsetSide, type ViewportInsets, addScore, enableAppSimulator, enableBackButtonTesting, enableLogOverlay, flushGameState, getGameId, getGraphicsPerformance, getJibbleAnimationId, getPlayerAvatar, getPlayerCharacter, getPlayerId, getPlayerName, getRoomCode, getSafeAreaTop, getViewportInsets, leaveGame, loadGameState, normalizeJibbleDirection, oasiz, onBackButton, onLeaveGame, onPause, onResume, openInviteModal, saveGameState, setLeaderboardVisible, setScore, share, shareRoomCode, submitScore, triggerHaptic };
458
+ export { type AppSimulatorDevice, type AppSimulatorDeviceName, type AppSimulatorHandle, type AppSimulatorOptions, type AppSimulatorOrientation, type BackButtonTestingHandle, type BackButtonTestingOptions, type FacingFrameMap, type GameState, type GetPlayerCharacterOptions, type GraphicsPerformanceMetric, type GraphicsPerformanceTier, type HapticType, JIBBLE_ANIMATION, JIBBLE_ANIMATION_IDS, JIBBLE_DIRECTIONS, type JibbleAnimationAction, type JibbleAnimationId, type JibbleDirectionCode, type JibbleFacingDirection, type LogOverlayEntry, type LogOverlayHandle, type LogOverlayLevel, type LogOverlayOptions, type PlayerCharacter, type ScoreEditResult, type ShareRequest, type ShareRoomCodeOptions, type TextureAtlas, type TextureAtlasAnimation, type TextureAtlasFrame, type Unsubscribe, type ViewportInsetEdges, type ViewportInsetSide, type ViewportInsets, addScore, enableAppSimulator, enableBackButtonTesting, enableLogOverlay, flushGameState, getGameId, getGraphicsPerformance, getJibbleAnimationId, getPlayerAvatar, getPlayerCharacter, getPlayerId, getPlayerName, getRoomCode, getSafeAreaTop, getSamplePlayerCharacter, getSampleTextureAtlas, getViewportInsets, leaveGame, loadGameState, normalizeJibbleDirection, oasiz, onBackButton, onLeaveGame, onPause, onResume, openInviteModal, saveGameState, setLeaderboardVisible, setScore, share, shareRoomCode, submitScore, triggerHaptic };
package/dist/index.d.ts CHANGED
@@ -220,13 +220,22 @@ interface ScoreEditResult {
220
220
  normalizedScore: number | null;
221
221
  }
222
222
 
223
+ interface GetPlayerCharacterOptions {
224
+ /**
225
+ * Controls the SDK sample character returned when the Oasiz app bridge is
226
+ * missing. `"auto"` returns the sample on localhost/file URLs only.
227
+ */
228
+ localFallback?: boolean | "auto";
229
+ }
223
230
  /**
224
231
  * Fetch the authenticated player's character, including a TexturePacker /
225
232
  * Phaser-style texture atlas describing the baked sprite image.
226
233
  *
227
234
  * Returns:
228
235
  * - `null` when the user has no character composition yet, OR
229
- * - `null` when the host bridge is unavailable (local dev / unauthenticated)
236
+ * - a sample character atlas when the host bridge is unavailable on a local
237
+ * browser URL (`localhost` / `file://`), OR
238
+ * - `null` when the host bridge is unavailable and local fallback is disabled
230
239
  *
231
240
  * The host transparently caches and proxies to `GET /api/sdk/me/character`,
232
241
  * so calling this multiple times in a session is cheap. The returned
@@ -245,13 +254,50 @@ interface ScoreEditResult {
245
254
  * ),
246
255
  * });
247
256
  */
248
- declare function getPlayerCharacter(): Promise<PlayerCharacter | null>;
257
+ declare function getPlayerCharacter(options?: GetPlayerCharacterOptions): Promise<PlayerCharacter | null>;
249
258
 
250
259
  declare function triggerHaptic(type: HapticType): void;
251
260
 
252
261
  declare const JIBBLE_DIRECTIONS: readonly ["n", "ne", "e", "se", "s", "sw", "w", "nw"];
253
262
  type JibbleDirectionCode = (typeof JIBBLE_DIRECTIONS)[number];
254
- type JibbleFacingDirection = JibbleDirectionCode | "front" | "back" | "left" | "right";
263
+ declare const JIBBLE_DIRECTION_ALIASES: {
264
+ readonly n: "n";
265
+ readonly ne: "ne";
266
+ readonly e: "e";
267
+ readonly se: "se";
268
+ readonly s: "s";
269
+ readonly sw: "sw";
270
+ readonly w: "w";
271
+ readonly nw: "nw";
272
+ readonly north: "n";
273
+ readonly "north-east": "ne";
274
+ readonly northeast: "ne";
275
+ readonly east: "e";
276
+ readonly "south-east": "se";
277
+ readonly southeast: "se";
278
+ readonly south: "s";
279
+ readonly "south-west": "sw";
280
+ readonly southwest: "sw";
281
+ readonly west: "w";
282
+ readonly "north-west": "nw";
283
+ readonly northwest: "nw";
284
+ readonly front: "s";
285
+ readonly forward: "s";
286
+ readonly forth: "s";
287
+ readonly back: "n";
288
+ readonly backward: "n";
289
+ readonly left: "w";
290
+ readonly right: "e";
291
+ readonly "front-right": "se";
292
+ readonly "forward-right": "se";
293
+ readonly "front-left": "sw";
294
+ readonly "forward-left": "sw";
295
+ readonly "back-right": "ne";
296
+ readonly "backward-right": "ne";
297
+ readonly "back-left": "nw";
298
+ readonly "backward-left": "nw";
299
+ };
300
+ type JibbleFacingDirection = keyof typeof JIBBLE_DIRECTION_ALIASES;
255
301
  type JibbleAnimationAction = "idle" | "walk" | "backflip";
256
302
  declare const JIBBLE_ANIMATION: {
257
303
  readonly Idle: {
@@ -344,6 +390,9 @@ declare function loadGameState(): GameState;
344
390
  declare function saveGameState(state: GameState): void;
345
391
  declare function flushGameState(): void;
346
392
 
393
+ declare function getSampleTextureAtlas(): TextureAtlas;
394
+ declare function getSamplePlayerCharacter(): PlayerCharacter;
395
+
347
396
  declare const oasiz: {
348
397
  submitScore: typeof submitScore;
349
398
  enableAppSimulator: typeof enableAppSimulator;
@@ -376,6 +425,8 @@ declare const oasiz: {
376
425
  addScore: typeof addScore;
377
426
  setScore: typeof setScore;
378
427
  getPlayerCharacter: typeof getPlayerCharacter;
428
+ getSamplePlayerCharacter: typeof getSamplePlayerCharacter;
429
+ getSampleTextureAtlas: typeof getSampleTextureAtlas;
379
430
  share: typeof share;
380
431
  triggerHaptic: typeof triggerHaptic;
381
432
  enableLogOverlay: typeof enableLogOverlay;
@@ -404,4 +455,4 @@ declare const oasiz: {
404
455
  readonly graphicsPerformance: GraphicsPerformanceMetric;
405
456
  };
406
457
 
407
- export { type AppSimulatorDevice, type AppSimulatorDeviceName, type AppSimulatorHandle, type AppSimulatorOptions, type AppSimulatorOrientation, type BackButtonTestingHandle, type BackButtonTestingOptions, type FacingFrameMap, type GameState, type GraphicsPerformanceMetric, type GraphicsPerformanceTier, type HapticType, JIBBLE_ANIMATION, JIBBLE_ANIMATION_IDS, JIBBLE_DIRECTIONS, type JibbleAnimationAction, type JibbleAnimationId, type JibbleDirectionCode, type JibbleFacingDirection, type LogOverlayEntry, type LogOverlayHandle, type LogOverlayLevel, type LogOverlayOptions, type PlayerCharacter, type ScoreEditResult, type ShareRequest, type ShareRoomCodeOptions, type TextureAtlas, type TextureAtlasAnimation, type TextureAtlasFrame, type Unsubscribe, type ViewportInsetEdges, type ViewportInsetSide, type ViewportInsets, addScore, enableAppSimulator, enableBackButtonTesting, enableLogOverlay, flushGameState, getGameId, getGraphicsPerformance, getJibbleAnimationId, getPlayerAvatar, getPlayerCharacter, getPlayerId, getPlayerName, getRoomCode, getSafeAreaTop, getViewportInsets, leaveGame, loadGameState, normalizeJibbleDirection, oasiz, onBackButton, onLeaveGame, onPause, onResume, openInviteModal, saveGameState, setLeaderboardVisible, setScore, share, shareRoomCode, submitScore, triggerHaptic };
458
+ export { type AppSimulatorDevice, type AppSimulatorDeviceName, type AppSimulatorHandle, type AppSimulatorOptions, type AppSimulatorOrientation, type BackButtonTestingHandle, type BackButtonTestingOptions, type FacingFrameMap, type GameState, type GetPlayerCharacterOptions, type GraphicsPerformanceMetric, type GraphicsPerformanceTier, type HapticType, JIBBLE_ANIMATION, JIBBLE_ANIMATION_IDS, JIBBLE_DIRECTIONS, type JibbleAnimationAction, type JibbleAnimationId, type JibbleDirectionCode, type JibbleFacingDirection, type LogOverlayEntry, type LogOverlayHandle, type LogOverlayLevel, type LogOverlayOptions, type PlayerCharacter, type ScoreEditResult, type ShareRequest, type ShareRoomCodeOptions, type TextureAtlas, type TextureAtlasAnimation, type TextureAtlasFrame, type Unsubscribe, type ViewportInsetEdges, type ViewportInsetSide, type ViewportInsets, addScore, enableAppSimulator, enableBackButtonTesting, enableLogOverlay, flushGameState, getGameId, getGraphicsPerformance, getJibbleAnimationId, getPlayerAvatar, getPlayerCharacter, getPlayerId, getPlayerName, getRoomCode, getSafeAreaTop, getSamplePlayerCharacter, getSampleTextureAtlas, getViewportInsets, leaveGame, loadGameState, normalizeJibbleDirection, oasiz, onBackButton, onLeaveGame, onPause, onResume, openInviteModal, saveGameState, setLeaderboardVisible, setScore, share, shareRoomCode, submitScore, triggerHaptic };
package/dist/index.js CHANGED
@@ -1385,65 +1385,6 @@ function enableAppSimulator(options = {}) {
1385
1385
  return handle;
1386
1386
  }
1387
1387
 
1388
- // src/character.ts
1389
- function isDevelopment2() {
1390
- const nodeEnv = globalThis.process?.env?.NODE_ENV;
1391
- return nodeEnv !== "production";
1392
- }
1393
- function getBridgeWindow2() {
1394
- if (typeof window === "undefined") {
1395
- return void 0;
1396
- }
1397
- return window;
1398
- }
1399
- function warnMissingBridge2(methodName) {
1400
- if (isDevelopment2()) {
1401
- console.warn(
1402
- "[oasiz/sdk] " + methodName + " bridge is unavailable. This is expected in local development."
1403
- );
1404
- }
1405
- }
1406
- async function getPlayerCharacter() {
1407
- const bridge = getBridgeWindow2();
1408
- if (typeof bridge?.__oasizGetPlayerCharacter !== "function") {
1409
- warnMissingBridge2("getPlayerCharacter");
1410
- return null;
1411
- }
1412
- try {
1413
- const result = await bridge.__oasizGetPlayerCharacter();
1414
- return result ?? null;
1415
- } catch (error) {
1416
- if (isDevelopment2()) {
1417
- console.error("[oasiz/sdk] getPlayerCharacter failed:", error);
1418
- }
1419
- return null;
1420
- }
1421
- }
1422
-
1423
- // src/haptics.ts
1424
- function isDevelopment3() {
1425
- const nodeEnv = globalThis.process?.env?.NODE_ENV;
1426
- return nodeEnv !== "production";
1427
- }
1428
- function getBridgeWindow3() {
1429
- if (typeof window === "undefined") {
1430
- return void 0;
1431
- }
1432
- return window;
1433
- }
1434
- function triggerHaptic(type) {
1435
- const bridge = getBridgeWindow3();
1436
- if (typeof bridge?.triggerHaptic === "function") {
1437
- bridge.triggerHaptic(type);
1438
- return;
1439
- }
1440
- if (isDevelopment3()) {
1441
- console.warn(
1442
- "[oasiz/sdk] triggerHaptic bridge is unavailable. This is expected in local development."
1443
- );
1444
- }
1445
- }
1446
-
1447
1388
  // src/jibble.ts
1448
1389
  var JIBBLE_DIRECTIONS = [
1449
1390
  "n",
@@ -1455,6 +1396,43 @@ var JIBBLE_DIRECTIONS = [
1455
1396
  "w",
1456
1397
  "nw"
1457
1398
  ];
1399
+ var JIBBLE_DIRECTION_ALIASES = {
1400
+ n: "n",
1401
+ ne: "ne",
1402
+ e: "e",
1403
+ se: "se",
1404
+ s: "s",
1405
+ sw: "sw",
1406
+ w: "w",
1407
+ nw: "nw",
1408
+ north: "n",
1409
+ "north-east": "ne",
1410
+ northeast: "ne",
1411
+ east: "e",
1412
+ "south-east": "se",
1413
+ southeast: "se",
1414
+ south: "s",
1415
+ "south-west": "sw",
1416
+ southwest: "sw",
1417
+ west: "w",
1418
+ "north-west": "nw",
1419
+ northwest: "nw",
1420
+ front: "s",
1421
+ forward: "s",
1422
+ forth: "s",
1423
+ back: "n",
1424
+ backward: "n",
1425
+ left: "w",
1426
+ right: "e",
1427
+ "front-right": "se",
1428
+ "forward-right": "se",
1429
+ "front-left": "sw",
1430
+ "forward-left": "sw",
1431
+ "back-right": "ne",
1432
+ "backward-right": "ne",
1433
+ "back-left": "nw",
1434
+ "backward-left": "nw"
1435
+ };
1458
1436
  var JIBBLE_ANIMATION = {
1459
1437
  Idle: {
1460
1438
  North: "idle_n",
@@ -1497,20 +1475,6 @@ var JIBBLE_ANIMATION_IDS = [
1497
1475
  JIBBLE_ANIMATION.Walk.NorthWest,
1498
1476
  JIBBLE_ANIMATION.Backflip
1499
1477
  ];
1500
- var JIBBLE_DIRECTION_ALIASES = {
1501
- n: "n",
1502
- ne: "ne",
1503
- e: "e",
1504
- se: "se",
1505
- s: "s",
1506
- sw: "sw",
1507
- w: "w",
1508
- nw: "nw",
1509
- front: "s",
1510
- back: "n",
1511
- left: "w",
1512
- right: "e"
1513
- };
1514
1478
  function normalizeJibbleDirection(direction) {
1515
1479
  return JIBBLE_DIRECTION_ALIASES[direction];
1516
1480
  }
@@ -1521,6 +1485,223 @@ function getJibbleAnimationId(action, direction = "front") {
1521
1485
  return `${action}_${normalizeJibbleDirection(direction)}`;
1522
1486
  }
1523
1487
 
1488
+ // src/sample-character.ts
1489
+ var FRAME_SIZE = 32;
1490
+ var COLUMNS = 8;
1491
+ var WALK_FRAME_COUNT = 2;
1492
+ var BACKFLIP_FRAME_COUNT = 4;
1493
+ var COLORS = [
1494
+ "#2563eb",
1495
+ "#7c3aed",
1496
+ "#db2777",
1497
+ "#ea580c",
1498
+ "#16a34a",
1499
+ "#0891b2",
1500
+ "#4f46e5",
1501
+ "#be123c"
1502
+ ];
1503
+ var IDLE_FRAME_NAMES = JIBBLE_DIRECTIONS.map((direction) => `idle_${direction}`);
1504
+ var WALK_FRAME_NAMES = JIBBLE_DIRECTIONS.flatMap(
1505
+ (direction) => Array.from({ length: WALK_FRAME_COUNT }, (_, index) => `walk_${direction}_${index}`)
1506
+ );
1507
+ var BACKFLIP_FRAME_NAMES = Array.from(
1508
+ { length: BACKFLIP_FRAME_COUNT },
1509
+ (_, index) => `backflip_${index}`
1510
+ );
1511
+ var SAMPLE_FRAME_NAMES = [
1512
+ ...IDLE_FRAME_NAMES,
1513
+ ...WALK_FRAME_NAMES,
1514
+ ...BACKFLIP_FRAME_NAMES
1515
+ ];
1516
+ var IMAGE_WIDTH = COLUMNS * FRAME_SIZE;
1517
+ var IMAGE_HEIGHT = Math.ceil(SAMPLE_FRAME_NAMES.length / COLUMNS) * FRAME_SIZE;
1518
+ function getFrameRect(index) {
1519
+ return {
1520
+ x: index % COLUMNS * FRAME_SIZE,
1521
+ y: Math.floor(index / COLUMNS) * FRAME_SIZE,
1522
+ width: FRAME_SIZE,
1523
+ height: FRAME_SIZE
1524
+ };
1525
+ }
1526
+ function createFrame(name, index) {
1527
+ return {
1528
+ name,
1529
+ ...getFrameRect(index)
1530
+ };
1531
+ }
1532
+ function getFrameLabel(name) {
1533
+ return name.replace(/^idle_/, "i ").replace(/^walk_/, "w ").replace(/^backflip_/, "flip ");
1534
+ }
1535
+ function createSvgDataUrl() {
1536
+ const cells = SAMPLE_FRAME_NAMES.map((name, index) => {
1537
+ const { x, y } = getFrameRect(index);
1538
+ const color = COLORS[index % COLORS.length];
1539
+ const label = getFrameLabel(name);
1540
+ return [
1541
+ `<rect x="${x}" y="${y}" width="${FRAME_SIZE}" height="${FRAME_SIZE}" fill="${color}"/>`,
1542
+ `<rect x="${x + 1}" y="${y + 1}" width="${FRAME_SIZE - 2}" height="${FRAME_SIZE - 2}" fill="none" stroke="#ffffff" stroke-opacity="0.35"/>`,
1543
+ `<circle cx="${x + 16}" cy="${y + 11}" r="5" fill="#ffffff" fill-opacity="0.92"/>`,
1544
+ `<rect x="${x + 12}" y="${y + 17}" width="8" height="7" rx="2" fill="#ffffff" fill-opacity="0.86"/>`,
1545
+ `<text x="${x + 16}" y="${y + 30}" text-anchor="middle" font-family="monospace" font-size="5" fill="#ffffff">${label}</text>`
1546
+ ].join("");
1547
+ }).join("");
1548
+ const svg = [
1549
+ `<svg xmlns="http://www.w3.org/2000/svg" width="${IMAGE_WIDTH}" height="${IMAGE_HEIGHT}" viewBox="0 0 ${IMAGE_WIDTH} ${IMAGE_HEIGHT}">`,
1550
+ `<rect width="${IMAGE_WIDTH}" height="${IMAGE_HEIGHT}" fill="#111827"/>`,
1551
+ cells,
1552
+ "</svg>"
1553
+ ].join("");
1554
+ return `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svg)}`;
1555
+ }
1556
+ function createDirectionalAnimation(animationId, role, direction, frames, frameRate) {
1557
+ return {
1558
+ animationId,
1559
+ role,
1560
+ group: "jibble",
1561
+ direction,
1562
+ frameRate,
1563
+ frames,
1564
+ facingFrameMap: null
1565
+ };
1566
+ }
1567
+ var SAMPLE_TEXTURE_ATLAS = {
1568
+ imageUrl: createSvgDataUrl(),
1569
+ imageWidth: IMAGE_WIDTH,
1570
+ imageHeight: IMAGE_HEIGHT,
1571
+ frames: SAMPLE_FRAME_NAMES.map(createFrame),
1572
+ animations: [
1573
+ ...JIBBLE_DIRECTIONS.map(
1574
+ (direction) => createDirectionalAnimation(
1575
+ getJibbleAnimationId("idle", direction),
1576
+ "idle",
1577
+ direction,
1578
+ [`idle_${direction}`],
1579
+ 1
1580
+ )
1581
+ ),
1582
+ ...JIBBLE_DIRECTIONS.map(
1583
+ (direction) => createDirectionalAnimation(
1584
+ getJibbleAnimationId("walk", direction),
1585
+ "walk",
1586
+ direction,
1587
+ Array.from({ length: WALK_FRAME_COUNT }, (_, index) => `walk_${direction}_${index}`),
1588
+ 8
1589
+ )
1590
+ ),
1591
+ {
1592
+ animationId: JIBBLE_ANIMATION.Backflip,
1593
+ role: "action",
1594
+ group: "jibble",
1595
+ direction: null,
1596
+ frameRate: 10,
1597
+ frames: BACKFLIP_FRAME_NAMES,
1598
+ facingFrameMap: null
1599
+ }
1600
+ ]
1601
+ };
1602
+ function cloneTextureAtlas(atlas) {
1603
+ return {
1604
+ ...atlas,
1605
+ frames: atlas.frames.map((frame) => ({ ...frame })),
1606
+ animations: atlas.animations.map((animation) => ({
1607
+ ...animation,
1608
+ frames: [...animation.frames],
1609
+ facingFrameMap: animation.facingFrameMap ? { ...animation.facingFrameMap } : null
1610
+ }))
1611
+ };
1612
+ }
1613
+ function getSampleTextureAtlas() {
1614
+ return cloneTextureAtlas(SAMPLE_TEXTURE_ATLAS);
1615
+ }
1616
+ function getSamplePlayerCharacter() {
1617
+ return {
1618
+ characterName: "SDK Sample Jibble",
1619
+ baseCharacterId: "sdk-sample-jibble",
1620
+ compositionCode: "sdk-sample-jibble-atlas-v1",
1621
+ textureAtlas: getSampleTextureAtlas(),
1622
+ editorTextureAtlas: getSampleTextureAtlas()
1623
+ };
1624
+ }
1625
+
1626
+ // src/character.ts
1627
+ function isDevelopment2() {
1628
+ const nodeEnv = globalThis.process?.env?.NODE_ENV;
1629
+ return nodeEnv !== "production";
1630
+ }
1631
+ function getBridgeWindow2() {
1632
+ if (typeof window === "undefined") {
1633
+ return void 0;
1634
+ }
1635
+ return window;
1636
+ }
1637
+ function isLocalHostname(hostname) {
1638
+ return hostname === "localhost" || hostname === "127.0.0.1" || hostname === "0.0.0.0" || hostname === "::1" || hostname.endsWith(".localhost");
1639
+ }
1640
+ function shouldUseLocalFallback(bridge, localFallback) {
1641
+ if (localFallback === true) {
1642
+ return true;
1643
+ }
1644
+ if (localFallback === false) {
1645
+ return false;
1646
+ }
1647
+ const location = bridge?.location;
1648
+ if (!location) {
1649
+ return false;
1650
+ }
1651
+ return location.protocol === "file:" || isLocalHostname(location.hostname);
1652
+ }
1653
+ function warnMissingBridge2(methodName, usingFallback) {
1654
+ if (isDevelopment2()) {
1655
+ console.warn(
1656
+ "[oasiz/sdk] " + methodName + " bridge is unavailable. " + (usingFallback ? "Using the SDK sample character atlas for local development." : "This is expected in local development.")
1657
+ );
1658
+ }
1659
+ }
1660
+ async function getPlayerCharacter(options = {}) {
1661
+ const bridge = getBridgeWindow2();
1662
+ if (typeof bridge?.__oasizGetPlayerCharacter !== "function") {
1663
+ const useLocalFallback = shouldUseLocalFallback(
1664
+ bridge,
1665
+ options.localFallback ?? "auto"
1666
+ );
1667
+ warnMissingBridge2("getPlayerCharacter", useLocalFallback);
1668
+ return useLocalFallback ? getSamplePlayerCharacter() : null;
1669
+ }
1670
+ try {
1671
+ const result = await bridge.__oasizGetPlayerCharacter();
1672
+ return result ?? null;
1673
+ } catch (error) {
1674
+ if (isDevelopment2()) {
1675
+ console.error("[oasiz/sdk] getPlayerCharacter failed:", error);
1676
+ }
1677
+ return null;
1678
+ }
1679
+ }
1680
+
1681
+ // src/haptics.ts
1682
+ function isDevelopment3() {
1683
+ const nodeEnv = globalThis.process?.env?.NODE_ENV;
1684
+ return nodeEnv !== "production";
1685
+ }
1686
+ function getBridgeWindow3() {
1687
+ if (typeof window === "undefined") {
1688
+ return void 0;
1689
+ }
1690
+ return window;
1691
+ }
1692
+ function triggerHaptic(type) {
1693
+ const bridge = getBridgeWindow3();
1694
+ if (typeof bridge?.triggerHaptic === "function") {
1695
+ bridge.triggerHaptic(type);
1696
+ return;
1697
+ }
1698
+ if (isDevelopment3()) {
1699
+ console.warn(
1700
+ "[oasiz/sdk] triggerHaptic bridge is unavailable. This is expected in local development."
1701
+ );
1702
+ }
1703
+ }
1704
+
1524
1705
  // src/log-overlay.ts
1525
1706
  var CONSOLE_METHODS = [
1526
1707
  "debug",
@@ -3374,6 +3555,8 @@ var oasiz = {
3374
3555
  addScore,
3375
3556
  setScore,
3376
3557
  getPlayerCharacter,
3558
+ getSamplePlayerCharacter,
3559
+ getSampleTextureAtlas,
3377
3560
  share,
3378
3561
  triggerHaptic,
3379
3562
  enableLogOverlay,
@@ -3435,6 +3618,8 @@ export {
3435
3618
  getPlayerName,
3436
3619
  getRoomCode,
3437
3620
  getSafeAreaTop,
3621
+ getSamplePlayerCharacter,
3622
+ getSampleTextureAtlas,
3438
3623
  getViewportInsets,
3439
3624
  leaveGame,
3440
3625
  loadGameState,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oasiz/sdk",
3
- "version": "1.8.3",
3
+ "version": "1.8.5",
4
4
  "description": "Typed SDK for Oasiz game platform bridge APIs.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",