@uzuhq/code-cli 0.5.2 → 0.5.3

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.
@@ -290,6 +290,17 @@ export class GameRoom {
290
290
  this.ensureTickLoop();
291
291
  }
292
292
  async handleMessage(ws, msg) {
293
+ // SDK は生文字列で送受信する (本番の setWebSocketAutoResponse と同じ wire 形式)。
294
+ // JSON.parse より前に返さないと heartbeat が落ちて 35 秒ごとに全席が再接続する。
295
+ if (msg === '__ping') {
296
+ try {
297
+ ws.send('__pong');
298
+ }
299
+ catch {
300
+ /* disconnected */
301
+ }
302
+ return;
303
+ }
293
304
  const attachment = this.attachments.get(ws);
294
305
  if (!attachment)
295
306
  return;
@@ -302,10 +313,6 @@ export class GameRoom {
302
313
  return;
303
314
  }
304
315
  const msgType = parsed.type;
305
- if (msgType === '__ping') {
306
- this.sendTo(ws, { type: '__pong' });
307
- return;
308
- }
309
316
  console.log(`[GameRoom] ⬅ recv from=${senderId} type=${msgType}`);
310
317
  if (msgType === '__action') {
311
318
  if (!this.gameState) {
@@ -25,6 +25,17 @@ export class RelayRoom {
25
25
  ws.send(JSON.stringify({ type: '__room_init', myId: playerId }));
26
26
  }
27
27
  handleMessage(ws, msg) {
28
+ // SDK は生文字列で送受信する (本番の setWebSocketAutoResponse と同じ wire 形式)。
29
+ // JSON.parse より前に返さないと heartbeat が落ちて 35 秒ごとに全席が再接続する。
30
+ if (msg === '__ping') {
31
+ try {
32
+ ws.send('__pong');
33
+ }
34
+ catch {
35
+ /* disconnected */
36
+ }
37
+ return;
38
+ }
28
39
  const attachment = this.attachments.get(ws);
29
40
  if (!attachment)
30
41
  return;
@@ -36,15 +47,6 @@ export class RelayRoom {
36
47
  catch {
37
48
  return;
38
49
  }
39
- if (parsed.type === '__ping') {
40
- try {
41
- ws.send(JSON.stringify({ type: '__pong' }));
42
- }
43
- catch {
44
- /* disconnected */
45
- }
46
- return;
47
- }
48
50
  console.log(`[RelayRoom] ⬅ recv from=${senderId}`, JSON.stringify(parsed));
49
51
  const outData = JSON.stringify({ ...parsed, __from: senderId });
50
52
  if (parsed.__to && typeof parsed.__to === 'string') {
@@ -78,6 +78,17 @@ export class SyncRoom {
78
78
  }
79
79
  }
80
80
  handleMessage(ws, msg) {
81
+ // SDK は生文字列で送受信する (本番の setWebSocketAutoResponse と同じ wire 形式)。
82
+ // JSON.parse より前に返さないと heartbeat が落ちて 35 秒ごとに全席が再接続する。
83
+ if (msg === '__ping') {
84
+ try {
85
+ ws.send('__pong');
86
+ }
87
+ catch {
88
+ /* disconnected */
89
+ }
90
+ return;
91
+ }
81
92
  const attachment = this.attachments.get(ws);
82
93
  if (!attachment)
83
94
  return;
@@ -90,15 +101,6 @@ export class SyncRoom {
90
101
  return;
91
102
  }
92
103
  const msgType = parsed.type;
93
- if (msgType === '__ping') {
94
- try {
95
- ws.send(JSON.stringify({ type: '__pong' }));
96
- }
97
- catch {
98
- /* disconnected */
99
- }
100
- return;
101
- }
102
104
  console.log(`[SyncRoom] ⬅ recv from=${senderId} type=${msgType}`);
103
105
  if (msgType === '__init_state') {
104
106
  if (this.stateInitialized) {
@@ -33,6 +33,14 @@ const PHONE_SVG = `<svg width="18" height="18" viewBox="0 0 24 24" fill="none" x
33
33
  <rect x="6.5" y="2" width="11" height="20" rx="2.5" stroke="white" stroke-width="2"/>
34
34
  <line x1="10" y1="18.5" x2="14" y2="18.5" stroke="white" stroke-width="2" stroke-linecap="round"/>
35
35
  </svg>`;
36
+ // 擬似ノッチ表示トグル。 createNotchDecor が実際に描く装飾 (横向き端末・左の
37
+ // Dynamic Island・下中央のホームインジケータ) をそのまま象る。 隣の PHONE_SVG
38
+ // (縦向き) とは向きで見分ける。
39
+ const NOTCH_SVG = `<svg width="18" height="18" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
40
+ <rect x="2" y="4.5" width="20" height="15" rx="3.5" stroke="white" stroke-width="1.8"/>
41
+ <rect x="4.8" y="8.5" width="2.2" height="7" rx="1.1" fill="white"/>
42
+ <line x1="11" y1="17" x2="17" y2="17" stroke="white" stroke-width="1.6" stroke-linecap="round"/>
43
+ </svg>`;
36
44
  /** buildIframeUrl に渡す HUD の避け領域 (Flutter HudInsets と同じ計算)。 */
37
45
  function hudInsetParams(hasMenu, actionCount) {
38
46
  let x = HUD_LEFT;
@@ -429,19 +437,12 @@ function openPlayerSwitcher(opts, currentIndex, anchor) {
429
437
  };
430
438
  setTimeout(() => document.addEventListener('click', close), 0);
431
439
  }
432
- /**
433
- * 各 iframe (= 各プレイヤー画面) の左上に載せる overlay chrome。
434
- * 本番アプリ (Flutter GamePlayScreen) と同じ仕様:
435
- * SafeArea top:8 / left:12 → [UZU ボタン 44] [gap 6] [ActionBar ピル: chat, mic]。
436
- * UZU ボタンは dev メニュー (State/Reset/Emulator) + その player の「スマホで開く」を開く。
437
- * chat / mic は本番の見た目に合わせた dev 表示 (機能は SDK 側が担うため配線しない)。
438
- */
439
440
  function createCellChrome(opts, playerIndex,
440
441
  // facade=true (実機の単体表示) では見た目だけ揃える。 admin channel が無いので
441
442
  // UZU メニュー / 実機 QR は配線せず、 アイコンはタップ無効 (ハリボテ)。
442
443
  facade = false,
443
- // grid では no-op のマイクアイコンを擬似ノッチ表示トグルに転用する。
444
- onMicClick,
444
+ // grid では no-op のマイク枠を擬似ノッチ表示トグルに転用する。
445
+ notchToggle,
445
446
  // 単体表示 (実機) では UZU ボタンをプレイヤー切替に転用する。
446
447
  onUzuClick) {
447
448
  const wrap = document.createElement('div');
@@ -489,17 +490,24 @@ onUzuClick) {
489
490
  'border-radius:18px',
490
491
  'display:flex;align-items:center',
491
492
  ].join(';');
492
- // 1つめ: マイク。 本番は音声トグルだが harness では no-op なので、 grid では
493
- // 擬似ノッチ表示のトグルに転用する (単体表示 facade では装飾のみ)。
494
- const mic = document.createElement(onMicClick ? 'button' : 'div');
495
- mic.innerHTML = MIC_SVG;
496
- mic.title = onMicClick ? '擬似ノッチ表示 ON/OFF' : 'マイク (dev 表示のみ)';
497
- mic.style.cssText = onMicClick
493
+ // 1つめ: 本番は音声トグルだが harness には音声が無い。 grid ではこの枠を擬似ノッチ
494
+ // 表示のトグルに転用し、 アイコンも端末フレームへ差し替える (facade は本番の見た目)。
495
+ const micSlot = document.createElement(notchToggle ? 'button' : 'div');
496
+ micSlot.innerHTML = notchToggle ? NOTCH_SVG : MIC_SVG;
497
+ micSlot.style.cssText = notchToggle
498
498
  ? 'padding:0 10px;height:36px;background:none;border:none;cursor:pointer;display:flex;align-items:center;'
499
499
  : 'padding:0 10px;display:flex;align-items:center;';
500
- if (onMicClick)
501
- mic.onclick = onMicClick;
502
- bar.appendChild(mic);
500
+ if (notchToggle) {
501
+ micSlot.onclick = notchToggle.toggle;
502
+ notchToggle.register((on) => {
503
+ micSlot.style.opacity = on ? '1' : '0.4';
504
+ micSlot.title = `擬似ノッチ表示: ${on ? 'ON' : 'OFF'}`;
505
+ });
506
+ }
507
+ else {
508
+ micSlot.title = 'マイク (dev 表示のみ)';
509
+ }
510
+ bar.appendChild(micSlot);
503
511
  // 2つめ: 携帯 (その player を実機で開く QR)。 facade では装飾のみ。
504
512
  const phone = document.createElement('button');
505
513
  phone.innerHTML = PHONE_SVG;
@@ -528,7 +536,8 @@ export function mountIframeGrid(opts) {
528
536
  const iframeSplashes = new Map();
529
537
  const iframePredictionWarns = new Map();
530
538
  // 擬似ノッチ (各 cell 共通トグル)。 iframe を再生成せず inset だけ切り替える。
531
- let notchSimOn = true;
539
+ // 既定は OFF: ノッチ帯のぶん画面が縮むので、 SafeArea を確認したいときだけ出す。
540
+ let notchSimOn = false;
532
541
  const notchAppliers = [];
533
542
  const toggleNotchSim = () => {
534
543
  notchSimOn = !notchSimOn;
@@ -575,6 +584,7 @@ export function mountIframeGrid(opts) {
575
584
  iframeStatusDots.clear();
576
585
  iframeSplashes.clear();
577
586
  iframePredictionWarns.clear();
587
+ notchAppliers.length = 0;
578
588
  for (let i = 0; i < opts.seats.length; i++) {
579
589
  const seat = seatAt(opts, i);
580
590
  const cell = document.createElement('div');
@@ -616,7 +626,13 @@ export function mountIframeGrid(opts) {
616
626
  screen.appendChild(splash);
617
627
  // admin は端末を模す必要が無いので overlay chrome / 擬似ノッチを載せず素の画面にする。
618
628
  if (seat.kind !== 'admin') {
619
- screen.appendChild(createCellChrome(opts, i, false, toggleNotchSim));
629
+ screen.appendChild(createCellChrome(opts, i, false, {
630
+ toggle: toggleNotchSim,
631
+ register: (apply) => {
632
+ apply(notchSimOn);
633
+ notchAppliers.push(apply);
634
+ },
635
+ }));
620
636
  }
621
637
  // 先読み警告の内訳パネル。 バッジクリックで開閉する。 画面の上に重ねるので
622
638
  // ゲームの見た目は普段どおりのまま、 必要なときだけ前に出る。
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@uzuhq/code-cli",
3
- "version": "0.5.2",
3
+ "version": "0.5.3",
4
4
  "description": "UZU ゲーム開発 CLI - ビルド・パブリッシュ・プロジェクト作成ツール",
5
5
  "type": "module",
6
6
  "bin": {