@uzuhq/code-sdk 0.8.10 → 0.8.12

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
@@ -296,6 +296,21 @@ changeRoom(null); // デフォルトルーム(全員が同じ音声チャン
296
296
  - 初期状態ではすべてのプレイヤーがデフォルトルーム(`null`)に所属する
297
297
  - `roomId` に文字列を指定すると、そのプレイヤーは指定ルームへ移動する
298
298
 
299
+ ### `haptic(kind: 'light' | 'medium' | 'heavy'): void`
300
+
301
+ 端末を短く振動させる。アプリ内では `navigator.vibrate` が効かないので、代わりにこれを使う。
302
+
303
+ ```ts
304
+ import { haptic } from '@uzuhq/code-sdk';
305
+
306
+ haptic('light'); // 手がかり入手など
307
+ haptic('heavy'); // 画面が揺れる演出など
308
+ ```
309
+
310
+ - 長さは指定できない(iOS の触覚は種類でしか選べないため、段階で渡す)
311
+ - 端末の設定で触覚を切っている人には効かない
312
+ - ブラウザで単独で開いたときは `navigator.vibrate` にフォールバックする
313
+
299
314
  ### `finishGame(): void`
300
315
 
301
316
  ゲームを終えて、この端末のプレイ画面を閉じる。ホストはイベントを終了扱いにし、呼んだプレイヤーを退出させる。
package/dist/index.d.ts CHANGED
@@ -198,6 +198,20 @@ declare const playBgm: (sound: string) => void;
198
198
  declare const stopBgm: () => void;
199
199
  declare const setMicEnabled: (enabled: boolean) => void;
200
200
  declare const changeRoom: (roomId: string | null) => void;
201
+ /**
202
+ * 振動の強さ。
203
+ *
204
+ * ミリ秒ではなく段階で渡すのは、iOS の触覚が長さを指定できず種類で選ぶ仕組みだから。
205
+ * 段階なら iOS / Android で同じ意味になる。
206
+ */
207
+ type HapticKind = 'light' | 'medium' | 'heavy';
208
+ /**
209
+ * 端末を短く振動させる。
210
+ *
211
+ * アプリの WebView では `navigator.vibrate` が効かない (iOS には Web から振動させる手段が
212
+ * 無い) ので、ホストへ依頼する。端末の設定で触覚を切っている人には OS 側で効かない。
213
+ */
214
+ declare const haptic: (kind: HapticKind) => void;
201
215
  /**
202
216
  * splash 描画完了を Flutter ホストに通知する。
203
217
  *
@@ -266,4 +280,4 @@ declare const onPlayersChanged: (handler: PlayersChangedHandler) => void;
266
280
  */
267
281
  declare const run: <S, A extends ActionMap<S> = ActionMap<S>, SA extends ServerActionMap<S> = Record<never, never>>(config: GameConfig<S, A, SA>) => void;
268
282
  //#endregion
269
- export { type ActionArgs, type ActionContext, type ActionHandler, type ActionMap, type BridgeChannel, type BridgeMessage, type ConnectionCallbacks, type ConnectionState, DEFAULT_ICON_URLS, type Deadline, type DeadlineArgs, type DeadlineContext, type DevHooksCtx, type Duration, type Emit, type EventHandler, type EventSubscription, GAME_START, type GameConfig, type GameLogic, type GameTime, type Insets, type JsonMergePatch, type JsonPatchOp, type Operation, type PayloadMap, type PlayScreenMessage, type PlayerVoiceState, type PlayersChangedMessage, type PredictionWarning, type ReconnectableWSOptions, ReconnectableWebSocket, type RunHandle, type Seat, type SeatKind, type SeededRandom, SeededRandomImpl, type SendAction, type ServerActionArgs, type ServerActionContext, type ServerActionHandler, type ServerActionMap, type ServerEvent, type ServerOnlyAction, type ServerOnlyActionContext, type ServerOnlyActionHandlerFn, type SetupArgs, type SetupContext, type UpdateArgs, type UpdateContext, type UzuDevHooks, type UzuInsets, applyJsonMergePatch, applyJsonPatch, attachDevHooks, changeRoom, createDevHooks, finishGame, firstFrameReady, gameReady, gameTime, getInsets, getPredictionWarnings, init, isHosted, isPaused, isServerOnlyAction, minus, on, onInsetsChange, onPauseChange, onPlayersChanged, playBgm, playSound, plus, run, send, serverOnly, setMicEnabled, stopBgm, sub };
283
+ export { type ActionArgs, type ActionContext, type ActionHandler, type ActionMap, type BridgeChannel, type BridgeMessage, type ConnectionCallbacks, type ConnectionState, DEFAULT_ICON_URLS, type Deadline, type DeadlineArgs, type DeadlineContext, type DevHooksCtx, type Duration, type Emit, type EventHandler, type EventSubscription, GAME_START, type GameConfig, type GameLogic, type GameTime, HapticKind, type Insets, type JsonMergePatch, type JsonPatchOp, type Operation, type PayloadMap, type PlayScreenMessage, type PlayerVoiceState, type PlayersChangedMessage, type PredictionWarning, type ReconnectableWSOptions, ReconnectableWebSocket, type RunHandle, type Seat, type SeatKind, type SeededRandom, SeededRandomImpl, type SendAction, type ServerActionArgs, type ServerActionContext, type ServerActionHandler, type ServerActionMap, type ServerEvent, type ServerOnlyAction, type ServerOnlyActionContext, type ServerOnlyActionHandlerFn, type SetupArgs, type SetupContext, type UpdateArgs, type UpdateContext, type UzuDevHooks, type UzuInsets, applyJsonMergePatch, applyJsonPatch, attachDevHooks, changeRoom, createDevHooks, finishGame, firstFrameReady, gameReady, gameTime, getInsets, getPredictionWarnings, haptic, init, isHosted, isPaused, isServerOnlyAction, minus, on, onInsetsChange, onPauseChange, onPlayersChanged, playBgm, playSound, plus, run, send, serverOnly, setMicEnabled, stopBgm, sub };
package/dist/index.js CHANGED
@@ -1293,6 +1293,35 @@ const onInsetsChange = (cb) => {
1293
1293
  };
1294
1294
  };
1295
1295
  //#endregion
1296
+ //#region src/bridge-envelope.ts
1297
+ const isBridgeChannel = (value) => value === "sdk" || value === "game";
1298
+ const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
1299
+ /**
1300
+ * 親 frame から postMessage で届く envelope を BridgeMessage に復元する。
1301
+ * string でも object でも来るので、形を確かめてから通す。
1302
+ *
1303
+ * payload を欠いた envelope は `{}` で埋める。受け手の handleMessage は
1304
+ * `payload.players` のように payload を直に読むので、ここで落とすと
1305
+ * 例外やハンドラへの `undefined` 渡しになる。
1306
+ */
1307
+ const parseBridgeEnvelope = (raw) => {
1308
+ let value = raw;
1309
+ if (typeof value === "string") try {
1310
+ value = JSON.parse(value);
1311
+ } catch {
1312
+ return null;
1313
+ }
1314
+ if (!isRecord(value)) return null;
1315
+ const { channel, type, payload, playerId } = value;
1316
+ if (!isBridgeChannel(channel) || typeof type !== "string") return null;
1317
+ return {
1318
+ channel,
1319
+ type,
1320
+ payload: isRecord(payload) ? payload : {},
1321
+ ...typeof playerId === "string" ? { playerId } : {}
1322
+ };
1323
+ };
1324
+ //#endregion
1296
1325
  //#region src/scenario-callback.ts
1297
1326
  /**
1298
1327
  * @docs
@@ -2031,6 +2060,24 @@ const setMicEnabled = (enabled) => {
2031
2060
  const changeRoom = (roomId) => {
2032
2061
  sendRaw("sdk", "changeRoom", { roomId });
2033
2062
  };
2063
+ const HAPTIC_FALLBACK_MS = {
2064
+ light: 10,
2065
+ medium: 20,
2066
+ heavy: 30
2067
+ };
2068
+ /**
2069
+ * 端末を短く振動させる。
2070
+ *
2071
+ * アプリの WebView では `navigator.vibrate` が効かない (iOS には Web から振動させる手段が
2072
+ * 無い) ので、ホストへ依頼する。端末の設定で触覚を切っている人には OS 側で効かない。
2073
+ */
2074
+ const haptic = (kind) => {
2075
+ if (!isHosted) {
2076
+ if ("vibrate" in navigator) navigator.vibrate(HAPTIC_FALLBACK_MS[kind]);
2077
+ return;
2078
+ }
2079
+ sendRaw("sdk", "haptic", { kind });
2080
+ };
2034
2081
  /**
2035
2082
  * splash 描画完了を Flutter ホストに通知する。
2036
2083
  *
@@ -2178,22 +2225,6 @@ const sendRaw = (channel, type, payload) => {
2178
2225
  if (window.FlutterHost) window.FlutterHost.postMessage(JSON.stringify(msg));
2179
2226
  else if (window.parent !== window) window.parent.postMessage(JSON.stringify(msg), "*");
2180
2227
  };
2181
- /** 親 frame から届く envelope。string でも object でも来るので、形を確かめてから通す。 */
2182
- const parseBridgeEnvelope = (raw) => {
2183
- let value = raw;
2184
- if (typeof value === "string") try {
2185
- value = JSON.parse(value);
2186
- } catch {
2187
- return null;
2188
- }
2189
- if (typeof value !== "object" || value === null) return null;
2190
- const envelope = value;
2191
- if (typeof envelope.channel !== "string" || typeof envelope.type !== "string") return null;
2192
- return {
2193
- channel: envelope.channel,
2194
- type: envelope.type
2195
- };
2196
- };
2197
2228
  const handleMessage = (msg) => {
2198
2229
  const { channel, type, payload } = msg;
2199
2230
  if (channel === "sdk") {
@@ -2226,4 +2257,4 @@ const handleMessage = (msg) => {
2226
2257
  for (const fn of h) callScenario(`game/${type} ハンドラ`, () => fn(payload));
2227
2258
  };
2228
2259
  //#endregion
2229
- export { DEFAULT_ICON_URLS, GAME_START, ReconnectableWebSocket, SeededRandomImpl, applyJsonMergePatch, applyJsonPatch, attachDevHooks, changeRoom, createDevHooks, finishGame, firstFrameReady, gameReady, gameTime, getInsets, getPredictionWarnings, init, isHosted, isPaused, isServerOnlyAction, minus, on, onInsetsChange, onPauseChange, onPlayersChanged, playBgm, playSound, plus, run, send, serverOnly, setMicEnabled, stopBgm, sub };
2260
+ export { DEFAULT_ICON_URLS, GAME_START, ReconnectableWebSocket, SeededRandomImpl, applyJsonMergePatch, applyJsonPatch, attachDevHooks, changeRoom, createDevHooks, finishGame, firstFrameReady, gameReady, gameTime, getInsets, getPredictionWarnings, haptic, init, isHosted, isPaused, isServerOnlyAction, minus, on, onInsetsChange, onPauseChange, onPlayersChanged, playBgm, playSound, plus, run, send, serverOnly, setMicEnabled, stopBgm, sub };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@uzuhq/code-sdk",
3
- "version": "0.8.10",
3
+ "version": "0.8.12",
4
4
  "description": "UZU PlayScreen SDK - Flutter ↔ JS ゲーム通信ライブラリ",
5
5
  "type": "module",
6
6
  "exports": {