@noya-app/noya-multiplayer-react 0.1.10 → 0.1.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/dist/index.mjs CHANGED
@@ -101,6 +101,15 @@ var darkTheme = {
101
101
  BASE_BACKGROUND_COLOR: "transparent",
102
102
  OBJECT_NAME_COLOR: "rgba(255,255,255,0.7)"
103
103
  };
104
+ var styles = {
105
+ sectionInner: {
106
+ flex: "1 1 0",
107
+ overflowY: "auto",
108
+ overflowX: "hidden",
109
+ display: "flex",
110
+ flexDirection: "column"
111
+ }
112
+ };
104
113
  function ToggleButton({
105
114
  showInspector,
106
115
  setShowInspector,
@@ -123,7 +132,10 @@ function ToggleButton({
123
132
  justifyContent: "center",
124
133
  padding: "2px 0"
125
134
  },
126
- onClick: () => setShowInspector(!showInspector)
135
+ onClick: (event) => {
136
+ event.stopPropagation();
137
+ setShowInspector(!showInspector);
138
+ }
127
139
  },
128
140
  /* @__PURE__ */ React2.createElement("span", { style: { width: "12px", height: "12px" } }, showInspector === (anchor === "right") ? /* @__PURE__ */ React2.createElement(
129
141
  "svg",
@@ -171,7 +183,8 @@ function DisclosureSection({
171
183
  right,
172
184
  children,
173
185
  colorScheme,
174
- isFirst
186
+ isFirst,
187
+ style
175
188
  }) {
176
189
  const theme = colorScheme === "light" ? lightTheme : darkTheme;
177
190
  const borderColor = colorScheme === "light" ? "rgba(0,0,0,0.1)" : "rgba(255,255,255,0.1)";
@@ -181,7 +194,8 @@ function DisclosureSection({
181
194
  style: {
182
195
  flex: open ? "1 1 0" : "0",
183
196
  display: "flex",
184
- flexDirection: "column"
197
+ flexDirection: "column",
198
+ ...style
185
199
  }
186
200
  },
187
201
  /* @__PURE__ */ React2.createElement(
@@ -215,10 +229,52 @@ function DisclosureSection({
215
229
  open && children
216
230
  );
217
231
  }
232
+ function InspectorRow({
233
+ children,
234
+ colorScheme,
235
+ selected,
236
+ style,
237
+ variant
238
+ }) {
239
+ const solidBorderColor = colorScheme === "light" ? "rgb(223 223 223)" : "rgb(29 29 29)";
240
+ return /* @__PURE__ */ React2.createElement(
241
+ "div",
242
+ {
243
+ style: {
244
+ borderBottom: `1px solid ${solidBorderColor}`,
245
+ fontSize: "12px",
246
+ fontFamily: "Menlo, monospace",
247
+ padding: "2px 12px 1px",
248
+ display: "flex",
249
+ alignItems: "center",
250
+ background: variant === "up" ? "rgba(0,255,0,0.1)" : variant === "down" ? "rgba(255,0,0,0.1)" : selected ? "rgb(59 130 246 / 15%)" : void 0,
251
+ // background:
252
+ // colorScheme === "light"
253
+ // ? "rgba(0,0,0,0.05)"
254
+ // : "rgba(255,255,255,0.05)",
255
+ ...style
256
+ }
257
+ },
258
+ /* @__PURE__ */ React2.createElement(
259
+ "span",
260
+ {
261
+ style: {
262
+ fontFamily: "Menlo, monospace",
263
+ fontSize: "11px",
264
+ borderRadius: 4,
265
+ display: "inline-block"
266
+ }
267
+ },
268
+ children
269
+ )
270
+ );
271
+ }
218
272
  var HISTORY_ELEMENT_PREFIX = "noya-multiplayer-history-";
219
273
  var StateInspector = memo(function StateInspector2({
220
274
  state,
221
275
  connectionEvents,
276
+ connectedUsers,
277
+ userId,
222
278
  unstyled,
223
279
  colorScheme = "light",
224
280
  stateManager,
@@ -243,6 +299,14 @@ var StateInspector = memo(function StateInspector2({
243
299
  "noya-multiplayer-react-show-history",
244
300
  false
245
301
  );
302
+ const [showUsers, setShowUsers] = useLocalStorageState(
303
+ "noya-multiplayer-react-show-users",
304
+ true
305
+ );
306
+ const [showData, setShowData] = useLocalStorageState(
307
+ "noya-multiplayer-react-show-data",
308
+ true
309
+ );
246
310
  useEffect2(() => {
247
311
  trackEvents.set(showEvents);
248
312
  }, [showEvents]);
@@ -261,7 +325,7 @@ var StateInspector = memo(function StateInspector2({
261
325
  if (!historyContainerRef.current)
262
326
  return;
263
327
  const historyEntry = historyContainerRef.current.querySelector(
264
- `#${HISTORY_ELEMENT_PREFIX}${historySnapshot.historyIndex - 1}`
328
+ `#${HISTORY_ELEMENT_PREFIX}${historySnapshot.historyIndex}`
265
329
  );
266
330
  if (historyEntry) {
267
331
  historyEntry.scrollIntoView({
@@ -271,7 +335,6 @@ var StateInspector = memo(function StateInspector2({
271
335
  }
272
336
  }, [historySnapshot.historyIndex]);
273
337
  const theme = colorScheme === "light" ? lightTheme : darkTheme;
274
- const borderColor = colorScheme === "light" ? "rgba(0,0,0,0.1)" : "rgba(255,255,255,0.1)";
275
338
  const solidBorderColor = colorScheme === "light" ? "rgb(223 223 223)" : "rgb(29 29 29)";
276
339
  const baseStyle = {
277
340
  position: "fixed",
@@ -330,10 +393,15 @@ var StateInspector = memo(function StateInspector2({
330
393
  /* @__PURE__ */ React2.createElement(
331
394
  DisclosureSection,
332
395
  {
333
- open: true,
334
- title: "Data",
335
- colorScheme,
336
396
  isFirst: true,
397
+ open: showUsers,
398
+ setOpen: setShowUsers,
399
+ title: "Users",
400
+ colorScheme,
401
+ style: {
402
+ flex: "0 0 auto",
403
+ maxHeight: "200px"
404
+ },
337
405
  right: /* @__PURE__ */ React2.createElement(
338
406
  ToggleButton,
339
407
  {
@@ -344,14 +412,44 @@ var StateInspector = memo(function StateInspector2({
344
412
  }
345
413
  )
346
414
  },
347
- /* @__PURE__ */ React2.createElement(
415
+ /* @__PURE__ */ React2.createElement("div", { style: { ...styles.sectionInner, flex: "0 0 auto" } }, connectedUsers?.map((user) => /* @__PURE__ */ React2.createElement(
416
+ InspectorRow,
417
+ {
418
+ key: user.id,
419
+ colorScheme,
420
+ variant: user.id === userId ? "up" : void 0
421
+ },
422
+ user.name,
423
+ " (",
424
+ ellipsis(user.id.toString(), 8, "middle"),
425
+ ")"
426
+ )), !connectedUsers && /* @__PURE__ */ React2.createElement(
348
427
  "div",
349
428
  {
350
429
  style: {
351
- flex: "1 1 0",
352
- overflowY: "auto",
430
+ padding: "12px",
431
+ fontSize: "12px",
353
432
  display: "flex",
354
433
  flexDirection: "column",
434
+ gap: "4px"
435
+ }
436
+ },
437
+ /* @__PURE__ */ React2.createElement("span", null, "No connected users")
438
+ ))
439
+ ),
440
+ /* @__PURE__ */ React2.createElement(
441
+ DisclosureSection,
442
+ {
443
+ title: "Data",
444
+ colorScheme,
445
+ setOpen: setShowData,
446
+ open: showData
447
+ },
448
+ /* @__PURE__ */ React2.createElement(
449
+ "div",
450
+ {
451
+ style: {
452
+ ...styles.sectionInner,
355
453
  gap: "1px",
356
454
  padding: "1px 12px"
357
455
  }
@@ -367,72 +465,58 @@ var StateInspector = memo(function StateInspector2({
367
465
  title: "History",
368
466
  colorScheme
369
467
  },
370
- /* @__PURE__ */ React2.createElement(
468
+ /* @__PURE__ */ React2.createElement("div", { ref: historyContainerRef, style: styles.sectionInner }, /* @__PURE__ */ React2.createElement(
371
469
  "div",
372
470
  {
373
- ref: historyContainerRef,
471
+ id: `${HISTORY_ELEMENT_PREFIX}0`,
374
472
  style: {
375
- flex: "1 1 0",
376
- overflowY: "auto",
377
- overflowX: "hidden",
473
+ borderBottom: `1px solid ${solidBorderColor}`,
474
+ fontSize: "12px",
475
+ fontFamily: "Menlo, monospace",
476
+ padding: "2px 12px 1px",
378
477
  display: "flex",
379
- flexDirection: "column"
478
+ alignItems: "center",
479
+ background: historySnapshot.historyIndex === 0 ? "rgb(59 130 246 / 15%)" : void 0
380
480
  }
381
481
  },
382
- historySnapshot.history.length < 50 && /* @__PURE__ */ React2.createElement(
383
- "div",
482
+ /* @__PURE__ */ React2.createElement(
483
+ "span",
384
484
  {
385
- id: `${HISTORY_ELEMENT_PREFIX}0`,
386
485
  style: {
387
- borderBottom: `1px solid ${solidBorderColor}`,
388
- fontSize: "12px",
389
486
  fontFamily: "Menlo, monospace",
390
- padding: "2px 12px 1px",
391
- display: "flex",
392
- alignItems: "center",
393
- background: historySnapshot.historyIndex === 0 ? "rgb(59 130 246 / 15%)" : void 0
487
+ fontSize: "11px",
488
+ borderRadius: 4,
489
+ display: "inline-block"
394
490
  }
395
491
  },
396
- /* @__PURE__ */ React2.createElement(
397
- "span",
398
- {
399
- style: {
400
- fontFamily: "Menlo, monospace",
401
- fontSize: "11px",
402
- borderRadius: 4,
403
- display: "inline-block"
404
- }
405
- },
406
- "Initial state"
407
- )
408
- ),
409
- historySnapshot.history.slice(-50).map((entry, index) => /* @__PURE__ */ React2.createElement(
410
- "div",
492
+ "Initial state"
493
+ )
494
+ ), historySnapshot.history.map((entry, index) => /* @__PURE__ */ React2.createElement(
495
+ "div",
496
+ {
497
+ id: `${HISTORY_ELEMENT_PREFIX}${index + 1}`,
498
+ key: index,
499
+ style: {
500
+ padding: "0px 12px 1px",
501
+ borderBottom: `1px solid ${solidBorderColor}`,
502
+ background: index + 1 === historySnapshot.historyIndex ? "rgb(59 130 246 / 15%)" : void 0
503
+ }
504
+ },
505
+ entry.redoPatches?.map((patch, j) => /* @__PURE__ */ React2.createElement(
506
+ "pre",
411
507
  {
412
- id: `noya-multiplayer-history-${index}`,
413
- key: index,
508
+ key: j,
414
509
  style: {
415
- padding: "0px 12px 1px",
416
- borderBottom: `1px solid ${solidBorderColor}`,
417
- background: index === historySnapshot.historyIndex - 1 ? "rgb(59 130 246 / 15%)" : void 0
510
+ fontSize: "11px",
511
+ display: "flex",
512
+ flexWrap: "wrap"
418
513
  }
419
514
  },
420
- entry.redoPatches?.map((patch, j) => /* @__PURE__ */ React2.createElement(
421
- "pre",
422
- {
423
- key: j,
424
- style: {
425
- fontSize: "11px",
426
- display: "flex",
427
- flexWrap: "wrap"
428
- }
429
- },
430
- patch.op === "add" && /* @__PURE__ */ React2.createElement(React2.Fragment, null, /* @__PURE__ */ React2.createElement("span", { style: { fontStyle: "italic" } }, pathToString(patch.path)), " = ", /* @__PURE__ */ React2.createElement(ObjectInspector, { data: patch.value, theme })),
431
- patch.op === "replace" && /* @__PURE__ */ React2.createElement(React2.Fragment, null, /* @__PURE__ */ React2.createElement("span", { style: { fontStyle: "italic" } }, pathToString(patch.path)), " = ", /* @__PURE__ */ React2.createElement(ObjectInspector, { data: patch.value, theme })),
432
- patch.op === "remove" && /* @__PURE__ */ React2.createElement(React2.Fragment, null, /* @__PURE__ */ React2.createElement("span", { style: { color: theme.OBJECT_VALUE_STRING_COLOR } }, "delete", " "), /* @__PURE__ */ React2.createElement("span", { style: { fontStyle: "italic" } }, pathToString(patch.path)))
433
- ))
515
+ patch.op === "add" && /* @__PURE__ */ React2.createElement(React2.Fragment, null, /* @__PURE__ */ React2.createElement("span", { style: { fontStyle: "italic" } }, pathToString(patch.path)), " = ", /* @__PURE__ */ React2.createElement(ObjectInspector, { data: patch.value, theme })),
516
+ patch.op === "replace" && /* @__PURE__ */ React2.createElement(React2.Fragment, null, /* @__PURE__ */ React2.createElement("span", { style: { fontStyle: "italic" } }, pathToString(patch.path)), " = ", /* @__PURE__ */ React2.createElement(ObjectInspector, { data: patch.value, theme })),
517
+ patch.op === "remove" && /* @__PURE__ */ React2.createElement(React2.Fragment, null, /* @__PURE__ */ React2.createElement("span", { style: { color: theme.OBJECT_VALUE_STRING_COLOR } }, "delete", " "), /* @__PURE__ */ React2.createElement("span", { style: { fontStyle: "italic" } }, pathToString(patch.path)))
434
518
  ))
435
- )
519
+ )))
436
520
  ),
437
521
  /* @__PURE__ */ React2.createElement(
438
522
  DisclosureSection,
@@ -442,98 +526,56 @@ var StateInspector = memo(function StateInspector2({
442
526
  title: "Events",
443
527
  colorScheme
444
528
  },
445
- /* @__PURE__ */ React2.createElement(
446
- "div",
447
- {
448
- ref: eventsContainerRef,
449
- style: {
450
- flex: "1 1 0",
451
- overflowY: "auto",
452
- display: "flex",
453
- flexDirection: "column"
454
- }
455
- },
456
- connectionEvents?.map((event, index) => /* @__PURE__ */ React2.createElement(
457
- "div",
529
+ /* @__PURE__ */ React2.createElement("div", { ref: eventsContainerRef, style: styles.sectionInner }, connectionEvents?.map(
530
+ (event, index) => event.type === "stateChange" ? /* @__PURE__ */ React2.createElement(InspectorRow, { key: index, colorScheme }, "connection:", " ", /* @__PURE__ */ React2.createElement("span", { style: { fontStyle: "italic" } }, event.state.toLowerCase())) : /* @__PURE__ */ React2.createElement(
531
+ InspectorRow,
458
532
  {
459
533
  key: index,
534
+ colorScheme,
535
+ variant: event.type === "send" ? "up" : "down",
460
536
  style: {
461
- borderBottom: `1px solid ${borderColor}`
537
+ padding: "0px 12px 1px"
462
538
  }
463
539
  },
464
- event.type === "stateChange" ? /* @__PURE__ */ React2.createElement(
465
- "div",
466
- {
467
- style: {
468
- padding: "2px 12px",
469
- background: colorScheme === "light" ? "rgba(0,0,0,0.05)" : "rgba(255,255,255,0.05)",
470
- display: "flex",
471
- alignItems: "center"
472
- }
473
- },
474
- /* @__PURE__ */ React2.createElement(
475
- "span",
476
- {
477
- style: {
478
- fontFamily: "Menlo, monospace",
479
- fontSize: "11px",
480
- borderRadius: 4,
481
- display: "inline-block"
482
- }
483
- },
484
- "connection:",
485
- " ",
486
- /* @__PURE__ */ React2.createElement("span", { style: { fontStyle: "italic" } }, event.state.toLowerCase())
487
- )
488
- ) : /* @__PURE__ */ React2.createElement(
489
- "div",
540
+ /* @__PURE__ */ React2.createElement(
541
+ ObjectInspector,
490
542
  {
491
- style: {
492
- padding: "0px 12px 1px",
493
- background: event.type === "send" ? "rgba(0,255,0,0.1)" : "rgba(255,0,0,0.1)"
543
+ data: event.message,
544
+ theme,
545
+ nodeRenderer: ({
546
+ depth,
547
+ name,
548
+ data,
549
+ isNonenumerable,
550
+ expanded
551
+ }) => {
552
+ const direction = event.type === "send" ? "up" : "down";
553
+ return depth === 0 ? /* @__PURE__ */ React2.createElement(ObjectRootLabel, { direction, data }) : /* @__PURE__ */ React2.createElement(
554
+ ObjectLabel,
555
+ {
556
+ direction,
557
+ name,
558
+ data,
559
+ isNonenumerable
560
+ }
561
+ );
494
562
  }
495
- },
496
- /* @__PURE__ */ React2.createElement(
497
- ObjectInspector,
498
- {
499
- data: event.message,
500
- theme,
501
- nodeRenderer: ({
502
- depth,
503
- name,
504
- data,
505
- isNonenumerable,
506
- expanded
507
- }) => {
508
- const direction = event.type === "send" ? "up" : "down";
509
- return depth === 0 ? /* @__PURE__ */ React2.createElement(ObjectRootLabel, { direction, data }) : /* @__PURE__ */ React2.createElement(
510
- ObjectLabel,
511
- {
512
- direction,
513
- name,
514
- data,
515
- isNonenumerable
516
- }
517
- );
518
- }
519
- }
520
- )
521
- )
522
- )),
523
- !connectionEvents && /* @__PURE__ */ React2.createElement(
524
- "div",
525
- {
526
- style: {
527
- padding: "12px",
528
- fontSize: "12px",
529
- display: "flex",
530
- flexDirection: "column",
531
- gap: "4px"
532
563
  }
533
- },
534
- /* @__PURE__ */ React2.createElement("span", null, "No recorded events")
564
+ )
535
565
  )
536
- )
566
+ ), !connectionEvents && /* @__PURE__ */ React2.createElement(
567
+ "div",
568
+ {
569
+ style: {
570
+ padding: "12px",
571
+ fontSize: "12px",
572
+ display: "flex",
573
+ flexDirection: "column",
574
+ gap: "4px"
575
+ }
576
+ },
577
+ /* @__PURE__ */ React2.createElement("span", null, "No recorded events")
578
+ ))
537
579
  )
538
580
  );
539
581
  });
@@ -616,6 +658,8 @@ function createPortalElement() {
616
658
  function useStateInspector({
617
659
  state,
618
660
  connectionEvents,
661
+ connectedUsers,
662
+ userId,
619
663
  disabled = false,
620
664
  colorScheme,
621
665
  anchor,
@@ -642,13 +686,24 @@ function useStateInspector({
642
686
  {
643
687
  state,
644
688
  connectionEvents,
689
+ connectedUsers,
690
+ userId,
645
691
  colorScheme,
646
692
  anchor,
647
693
  stateManager
648
694
  }
649
695
  )
650
696
  );
651
- }, [anchor, colorScheme, connectionEvents, root, state, stateManager]);
697
+ }, [
698
+ anchor,
699
+ colorScheme,
700
+ connectedUsers,
701
+ connectionEvents,
702
+ root,
703
+ state,
704
+ stateManager,
705
+ userId
706
+ ]);
652
707
  }
653
708
 
654
709
  // src/hooks.ts
@@ -725,26 +780,37 @@ function useMultiplayerState(initialState, options) {
725
780
  );
726
781
  const syncRef = useRef(sync);
727
782
  const [connectionEvents, setConnectionEvents] = useState2();
783
+ const [connectedUsers, setConnectedUsers] = useState2([]);
784
+ const [userId, setUserId] = useState2();
728
785
  const extras = useMemo(() => {
729
786
  return {
730
- connectionEvents
787
+ connectionEvents,
788
+ connectedUsers
731
789
  };
732
- }, [connectionEvents]);
790
+ }, [connectionEvents, connectedUsers]);
733
791
  const globalTrackEvents = useObservable(trackEvents);
734
792
  const trackConnectionEvents = options.trackConnectionEvents ?? globalTrackEvents;
735
793
  const trackEventsCallbackRef = useRef();
794
+ const trackConnectedUsersCallbackRef = useRef();
736
795
  useEffect3(() => {
737
796
  trackEventsCallbackRef.current = trackConnectionEvents ? (e) => setConnectionEvents((events) => {
738
797
  const updated = events ? [...events, e] : [e];
739
798
  return updated.length > 50 ? updated.slice(-50) : updated;
740
799
  }) : void 0;
741
800
  }, [trackConnectionEvents]);
801
+ useEffect3(() => {
802
+ trackConnectedUsersCallbackRef.current = (users, userId2) => {
803
+ setConnectedUsers(users);
804
+ setUserId(userId2);
805
+ };
806
+ });
742
807
  useEffect3(() => {
743
808
  if (syncRef.current) {
744
- return syncRef.current(
745
- multiplayerStateManager,
746
- (e) => trackEventsCallbackRef.current?.(e)
747
- );
809
+ return syncRef.current({
810
+ ms: multiplayerStateManager,
811
+ onConnectionEvent: (e) => trackEventsCallbackRef.current?.(e),
812
+ onChangeConnectedUsers: (users, userId2) => trackConnectedUsersCallbackRef.current?.(users, userId2)
813
+ });
748
814
  }
749
815
  }, [multiplayerStateManager]);
750
816
  const state = useSyncMultiplayerStateManager(multiplayerStateManager);
@@ -752,6 +818,8 @@ function useMultiplayerState(initialState, options) {
752
818
  stateManager: multiplayerStateManager.sm,
753
819
  state,
754
820
  connectionEvents,
821
+ connectedUsers,
822
+ userId,
755
823
  disabled: !options.inspector,
756
824
  ...typeof options.inspector === "object" && options.inspector
757
825
  });