@noya-app/noya-multiplayer-react 0.1.18 → 0.1.20

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
@@ -1,6 +1,198 @@
1
1
  // src/index.ts
2
2
  export * from "@noya-app/state-manager";
3
3
 
4
+ // src/avatarStyle.ts
5
+ function hashCode(string) {
6
+ let hash = 0;
7
+ for (const char of string) {
8
+ hash = char.charCodeAt(0) + ((hash << 5) - hash);
9
+ }
10
+ return hash;
11
+ }
12
+ function elementFromHash(array, hash) {
13
+ return array[Math.abs(hash) % array.length];
14
+ }
15
+ function getAvatarInitials(name) {
16
+ return name.split(" ").filter((c) => c.length > 0).map((c) => c[0]).join("").toLocaleUpperCase();
17
+ }
18
+ function getAvatarStyle(name) {
19
+ const hash = hashCode(name);
20
+ const color = elementFromHash(Object.values(colors500), hash);
21
+ return {
22
+ color: "white",
23
+ backgroundColor: color
24
+ // primaryColors: {
25
+ // "500": color,
26
+ // "800": darkerColor,
27
+ // },
28
+ };
29
+ }
30
+ var colors500 = {
31
+ red500: "#ef4444",
32
+ orange500: "#f97316",
33
+ amber500: "#f59e0b",
34
+ yellow500: "#eab308",
35
+ lime500: "#84cc16",
36
+ green500: "#22c55e",
37
+ emerald500: "#10b981",
38
+ teal500: "#14b8a6",
39
+ cyan500: "#06b6d4",
40
+ sky500: "#0ea5e9",
41
+ blue500: "#3b82f6",
42
+ indigo500: "#6366f1",
43
+ violet500: "#8b5cf6",
44
+ purple500: "#a855f7",
45
+ fuchsia500: "#d946ef",
46
+ pink500: "#ec4899",
47
+ rose500: "#f43f5e"
48
+ };
49
+
50
+ // src/components/UserPointersOverlay.tsx
51
+ import React, { memo, useEffect, useMemo as useMemo2, useState } from "react";
52
+
53
+ // src/useObservable.ts
54
+ import { useMemo, useSyncExternalStore } from "react";
55
+ function useObservable(observable, path) {
56
+ const { get, listen } = useMemo(() => {
57
+ const listen2 = path ? (callback) => observable.listen(path, callback) : observable.listen;
58
+ const get2 = path ? () => observable.get(path) : observable.get;
59
+ return { get: get2, listen: listen2 };
60
+ }, [observable, path]);
61
+ return useSyncExternalStore(listen, get, get);
62
+ }
63
+
64
+ // src/components/UserPointersOverlay.tsx
65
+ function shouldShow(hideAfter, updatedAt) {
66
+ return !!updatedAt && Date.now() - updatedAt < hideAfter;
67
+ }
68
+ var UserPointer_ = function UserPointer({
69
+ user,
70
+ ephemeralUserData,
71
+ hideAfter = 5e3
72
+ }) {
73
+ const metadata = useObservable(ephemeralUserData.metadata$, [
74
+ user.id
75
+ ]);
76
+ const data = useObservable(ephemeralUserData.data$, [user.id]);
77
+ const avatarStyle = useMemo2(() => getAvatarStyle(user.id), [user.id]);
78
+ const [, setForceUpdate] = useState(0);
79
+ const updatedAt = metadata?.updatedAt ?? 0;
80
+ useEffect(() => {
81
+ if (!shouldShow(hideAfter, updatedAt))
82
+ return;
83
+ const timeoutId = setTimeout(() => {
84
+ setForceUpdate((prev) => prev + 1);
85
+ }, hideAfter);
86
+ return () => clearTimeout(timeoutId);
87
+ }, [hideAfter, updatedAt]);
88
+ const show = shouldShow(hideAfter, updatedAt);
89
+ if (!data || !data.pointer)
90
+ return null;
91
+ return /* @__PURE__ */ React.createElement(UserPointerContainer, { key: user.id, point: data.pointer, show }, /* @__PURE__ */ React.createElement(UserPointerIcon, { color: avatarStyle.backgroundColor }), /* @__PURE__ */ React.createElement(UserNameTag, { background: avatarStyle.backgroundColor }, user.name));
92
+ };
93
+ var UserPointer2 = memo(UserPointer_);
94
+ var UserPointerContainer = memo(function UserPointerContainer2({
95
+ point,
96
+ children,
97
+ show = true
98
+ }) {
99
+ return /* @__PURE__ */ React.createElement(
100
+ "div",
101
+ {
102
+ style: {
103
+ position: "absolute",
104
+ top: 0,
105
+ left: 0,
106
+ transform: `translate(${Math.round(point.x)}px, ${Math.round(
107
+ point.y
108
+ )}px)`,
109
+ opacity: show ? 1 : 0,
110
+ transition: "transform 0.1s, opacity 0.2s"
111
+ }
112
+ },
113
+ children
114
+ );
115
+ });
116
+ var UserNameTag = memo(function UserNameTag2({
117
+ background,
118
+ children
119
+ }) {
120
+ return /* @__PURE__ */ React.createElement(
121
+ "span",
122
+ {
123
+ style: {
124
+ position: "relative",
125
+ top: "8px",
126
+ left: "15px",
127
+ fontSize: 11,
128
+ lineHeight: "1",
129
+ fontWeight: 500,
130
+ boxShadow: "0 1px 1px rgba(0,0,0,0.1)",
131
+ padding: "2px 4px 3px",
132
+ pointerEvents: "none",
133
+ letterSpacing: "-0.3px",
134
+ outline: `1px solid rgba(255,255,255,0.3)`,
135
+ outlineOffset: "-1px",
136
+ background,
137
+ color: "white"
138
+ }
139
+ },
140
+ children
141
+ );
142
+ });
143
+ var UserPointersOverlay = memo(function UserPointers({ connectedUsers, ephemeralUserData }) {
144
+ const currentUserId = useObservable(ephemeralUserData.currentUserId$);
145
+ return /* @__PURE__ */ React.createElement(React.Fragment, null, connectedUsers.map((user) => {
146
+ if (user.id === currentUserId)
147
+ return null;
148
+ return /* @__PURE__ */ React.createElement(
149
+ UserPointer2,
150
+ {
151
+ key: user.id,
152
+ user,
153
+ ephemeralUserData
154
+ }
155
+ );
156
+ }));
157
+ });
158
+ var UserPointerIcon = memo(function CursorIcon({
159
+ color = "black"
160
+ }) {
161
+ return /* @__PURE__ */ React.createElement(
162
+ "svg",
163
+ {
164
+ width: "15",
165
+ height: "15",
166
+ viewBox: "0 0 15 15",
167
+ fill: "none",
168
+ xmlns: "http://www.w3.org/2000/svg",
169
+ style: {
170
+ position: "absolute",
171
+ filter: "drop-shadow(0 1px 1px rgba(0,0,0,0.1))"
172
+ // filter: "drop-shadow(0 1px 1px rgba(0,0,0,0.6))",
173
+ }
174
+ },
175
+ /* @__PURE__ */ React.createElement(
176
+ "path",
177
+ {
178
+ fillRule: "evenodd",
179
+ clipRule: "evenodd",
180
+ d: "M3.29227 0.0451998C3.47033 -0.0361222 3.67946 -0.00606629 3.8274 0.122107L12.8587 7.94648C13.0134 8.08054 13.0708 8.29538 13.0035 8.48873C12.9362 8.68208 12.7578 8.81488 12.5533 8.8239L9.21887 8.97096L11.1504 13.2149C11.2648 13.4662 11.1538 13.7626 10.9026 13.877L8.75024 14.8575C8.499 14.972 8.20255 14.8611 8.08802 14.6099L6.15339 10.3665L3.86279 12.7817C3.72196 12.9302 3.50487 12.9779 3.31479 12.9021C3.1247 12.8263 3 12.6423 3 12.4376V0.500008C3 0.304264 3.11422 0.126522 3.29227 0.0451998Z",
181
+ fill: color
182
+ }
183
+ ),
184
+ /* @__PURE__ */ React.createElement(
185
+ "path",
186
+ {
187
+ fillRule: "evenodd",
188
+ clipRule: "evenodd",
189
+ d: "M3.29227 0.048984C3.47033 -0.032338 3.67946 -0.00228211 3.8274 0.125891L12.8587 7.95026C13.0134 8.08432 13.0708 8.29916 13.0035 8.49251C12.9362 8.68586 12.7578 8.81866 12.5533 8.82768L9.21887 8.97474L11.1504 13.2187C11.2648 13.47 11.1538 13.7664 10.9026 13.8808L8.75024 14.8613C8.499 14.9758 8.20255 14.8649 8.08802 14.6137L6.15339 10.3703L3.86279 12.7855C3.72196 12.934 3.50487 12.9817 3.31479 12.9059C3.1247 12.8301 3 12.6461 3 12.4414V0.503792C3 0.308048 3.11422 0.130306 3.29227 0.048984ZM4 1.59852V11.1877L5.93799 9.14425C6.05238 9.02363 6.21924 8.96776 6.38319 8.99516C6.54715 9.02256 6.68677 9.12965 6.75573 9.2809L8.79056 13.7441L10.0332 13.178L8.00195 8.71497C7.93313 8.56376 7.94391 8.38824 8.03072 8.24659C8.11753 8.10494 8.26903 8.01566 8.435 8.00834L11.2549 7.88397L4 1.59852Z",
190
+ fill: "rgba(255,255,255,0.5)"
191
+ }
192
+ )
193
+ );
194
+ });
195
+
4
196
  // src/hooks.ts
5
197
  import {
6
198
  EphemeralUserData,
@@ -11,52 +203,24 @@ import {
11
203
  import {
12
204
  useCallback,
13
205
  useEffect as useEffect3,
14
- useMemo,
206
+ useMemo as useMemo3,
15
207
  useRef,
16
208
  useState as useState2,
17
- useSyncExternalStore
209
+ useSyncExternalStore as useSyncExternalStore2
18
210
  } from "react";
19
211
 
20
212
  // src/globals.ts
21
- import { Emitter } from "@noya-app/emitter";
22
- import { useEffect, useState } from "react";
23
- var Observable = class {
24
- value;
25
- emitter = new Emitter();
26
- constructor(value) {
27
- this.value = value;
28
- }
29
- get() {
30
- return this.value;
31
- }
32
- set(value) {
33
- if (this.value === value)
34
- return;
35
- this.value = value;
36
- this.emitter.emit(value);
37
- }
38
- listen(callback) {
39
- callback(this.value);
40
- return this.emitter.addListener(callback);
41
- }
42
- };
43
- function useObservable(observable) {
44
- const [value, setValue] = useState(observable.get());
45
- useEffect(() => {
46
- return observable.listen(setValue);
47
- }, [observable]);
48
- return value;
49
- }
213
+ import { Observable } from "@noya-app/observable";
50
214
  var shouldTrackEvents$ = new Observable(false);
51
215
  var shouldTrackTasks$ = new Observable(false);
52
216
 
53
217
  // src/inspector/useStateInspector.tsx
54
- import React3, { useLayoutEffect as useLayoutEffect2 } from "react";
218
+ import React4, { useLayoutEffect as useLayoutEffect2 } from "react";
55
219
  import { createRoot } from "react-dom/client";
56
220
 
57
221
  // src/inspector/StateInspector.tsx
58
- import React2, {
59
- memo,
222
+ import React3, {
223
+ memo as memo2,
60
224
  useEffect as useEffect2,
61
225
  useLayoutEffect
62
226
  } from "react";
@@ -70,10 +234,10 @@ import {
70
234
  } from "react-inspector";
71
235
 
72
236
  // src/inspector/useLocalStorageState.tsx
73
- import React from "react";
237
+ import React2 from "react";
74
238
  var localStorage = typeof window !== "undefined" ? window.localStorage : null;
75
239
  function useLocalStorageState(key, defaultValue) {
76
- const [state, setState] = React.useState(() => {
240
+ const [state, setState] = React2.useState(() => {
77
241
  const value = localStorage?.getItem(key);
78
242
  let result = defaultValue;
79
243
  if (value) {
@@ -118,7 +282,7 @@ function ToggleButton({
118
282
  theme,
119
283
  anchor
120
284
  }) {
121
- return /* @__PURE__ */ React2.createElement(
285
+ return /* @__PURE__ */ React3.createElement(
122
286
  "span",
123
287
  {
124
288
  role: "button",
@@ -139,7 +303,7 @@ function ToggleButton({
139
303
  setShowInspector(!showInspector);
140
304
  }
141
305
  },
142
- /* @__PURE__ */ React2.createElement("span", { style: { width: "12px", height: "12px" } }, showInspector === (anchor === "right") ? /* @__PURE__ */ React2.createElement(
306
+ /* @__PURE__ */ React3.createElement("span", { style: { width: "12px", height: "12px" } }, showInspector === (anchor === "right") ? /* @__PURE__ */ React3.createElement(
143
307
  "svg",
144
308
  {
145
309
  xmlns: "http://www.w3.org/2000/svg",
@@ -149,7 +313,7 @@ function ToggleButton({
149
313
  stroke: "currentColor",
150
314
  className: "size-6"
151
315
  },
152
- /* @__PURE__ */ React2.createElement(
316
+ /* @__PURE__ */ React3.createElement(
153
317
  "path",
154
318
  {
155
319
  strokeLinecap: "round",
@@ -157,7 +321,7 @@ function ToggleButton({
157
321
  d: "m5.25 4.5 7.5 7.5-7.5 7.5m6-15 7.5 7.5-7.5 7.5"
158
322
  }
159
323
  )
160
- ) : /* @__PURE__ */ React2.createElement(
324
+ ) : /* @__PURE__ */ React3.createElement(
161
325
  "svg",
162
326
  {
163
327
  xmlns: "http://www.w3.org/2000/svg",
@@ -167,7 +331,7 @@ function ToggleButton({
167
331
  stroke: "currentColor",
168
332
  className: "size-6"
169
333
  },
170
- /* @__PURE__ */ React2.createElement(
334
+ /* @__PURE__ */ React3.createElement(
171
335
  "path",
172
336
  {
173
337
  strokeLinecap: "round",
@@ -190,7 +354,7 @@ function DisclosureSection({
190
354
  }) {
191
355
  const theme = colorScheme === "light" ? lightTheme : darkTheme;
192
356
  const borderColor = colorScheme === "light" ? "rgba(0,0,0,0.1)" : "rgba(255,255,255,0.1)";
193
- return /* @__PURE__ */ React2.createElement(
357
+ return /* @__PURE__ */ React3.createElement(
194
358
  "div",
195
359
  {
196
360
  style: {
@@ -200,7 +364,7 @@ function DisclosureSection({
200
364
  ...style
201
365
  }
202
366
  },
203
- /* @__PURE__ */ React2.createElement(
367
+ /* @__PURE__ */ React3.createElement(
204
368
  "div",
205
369
  {
206
370
  onClick: () => setOpen?.(!open),
@@ -214,7 +378,7 @@ function DisclosureSection({
214
378
  ...open && { borderBottom: `1px solid ${borderColor}` }
215
379
  }
216
380
  },
217
- setOpen && /* @__PURE__ */ React2.createElement(
381
+ setOpen && /* @__PURE__ */ React3.createElement(
218
382
  Arrow,
219
383
  {
220
384
  expanded: open,
@@ -225,7 +389,7 @@ function DisclosureSection({
225
389
  }
226
390
  }
227
391
  ),
228
- /* @__PURE__ */ React2.createElement("span", { style: { flex: "1 1 0", userSelect: "none" } }, title),
392
+ /* @__PURE__ */ React3.createElement("span", { style: { flex: "1 1 0", userSelect: "none" } }, title),
229
393
  right
230
394
  ),
231
395
  open && children
@@ -239,7 +403,7 @@ function InspectorRow({
239
403
  variant
240
404
  }) {
241
405
  const solidBorderColor = colorScheme === "light" ? "rgb(223 223 223)" : "rgb(29 29 29)";
242
- return /* @__PURE__ */ React2.createElement(
406
+ return /* @__PURE__ */ React3.createElement(
243
407
  "div",
244
408
  {
245
409
  style: {
@@ -257,7 +421,7 @@ function InspectorRow({
257
421
  ...style
258
422
  }
259
423
  },
260
- /* @__PURE__ */ React2.createElement(
424
+ /* @__PURE__ */ React3.createElement(
261
425
  "span",
262
426
  {
263
427
  style: {
@@ -272,7 +436,7 @@ function InspectorRow({
272
436
  );
273
437
  }
274
438
  var HISTORY_ELEMENT_PREFIX = "noya-multiplayer-history-";
275
- var StateInspector = memo(function StateInspector2({
439
+ var StateInspector = memo2(function StateInspector2({
276
440
  state,
277
441
  connectionEvents,
278
442
  connectedUsers,
@@ -286,12 +450,12 @@ var StateInspector = memo(function StateInspector2({
286
450
  forceInit,
287
451
  ...props
288
452
  }) {
289
- const [didMount, setDidMount] = React2.useState(false);
453
+ const [didMount, setDidMount] = React3.useState(false);
290
454
  useLayoutEffect(() => {
291
455
  setDidMount(true);
292
456
  }, []);
293
- const eventsContainerRef = React2.useRef(null);
294
- const historyContainerRef = React2.useRef(null);
457
+ const eventsContainerRef = React3.useRef(null);
458
+ const historyContainerRef = React3.useRef(null);
295
459
  const [showInspector, setShowInspector] = useLocalStorageState(
296
460
  "noya-multiplayer-react-show-inspector",
297
461
  true
@@ -375,7 +539,7 @@ var StateInspector = memo(function StateInspector2({
375
539
  if (!didMount)
376
540
  return null;
377
541
  if (!showInspector) {
378
- return /* @__PURE__ */ React2.createElement(
542
+ return /* @__PURE__ */ React3.createElement(
379
543
  "div",
380
544
  {
381
545
  ...props,
@@ -387,7 +551,7 @@ var StateInspector = memo(function StateInspector2({
387
551
  },
388
552
  onClick: () => setShowInspector(true)
389
553
  },
390
- /* @__PURE__ */ React2.createElement(
554
+ /* @__PURE__ */ React3.createElement(
391
555
  ToggleButton,
392
556
  {
393
557
  showInspector,
@@ -398,7 +562,7 @@ var StateInspector = memo(function StateInspector2({
398
562
  )
399
563
  );
400
564
  }
401
- return /* @__PURE__ */ React2.createElement(
565
+ return /* @__PURE__ */ React3.createElement(
402
566
  "div",
403
567
  {
404
568
  ...props,
@@ -407,7 +571,7 @@ var StateInspector = memo(function StateInspector2({
407
571
  ...props.style
408
572
  }
409
573
  },
410
- /* @__PURE__ */ React2.createElement(
574
+ /* @__PURE__ */ React3.createElement(
411
575
  DisclosureSection,
412
576
  {
413
577
  isFirst: true,
@@ -419,7 +583,7 @@ var StateInspector = memo(function StateInspector2({
419
583
  flex: "0 0 auto",
420
584
  maxHeight: "200px"
421
585
  },
422
- right: /* @__PURE__ */ React2.createElement(
586
+ right: /* @__PURE__ */ React3.createElement(
423
587
  ToggleButton,
424
588
  {
425
589
  showInspector,
@@ -429,18 +593,35 @@ var StateInspector = memo(function StateInspector2({
429
593
  }
430
594
  )
431
595
  },
432
- /* @__PURE__ */ React2.createElement("div", { style: { ...styles.sectionInner, flex: "0 0 auto" } }, connectedUsers?.map((user) => /* @__PURE__ */ React2.createElement(
596
+ /* @__PURE__ */ React3.createElement("div", { style: { ...styles.sectionInner, flex: "0 0 auto" } }, connectedUsers?.map((user) => /* @__PURE__ */ React3.createElement(
433
597
  InspectorRow,
434
598
  {
435
599
  key: user.id,
436
600
  colorScheme,
437
601
  variant: user.id === userId ? "up" : void 0
438
602
  },
603
+ user.image && /* @__PURE__ */ React3.createElement(
604
+ "img",
605
+ {
606
+ src: user.image,
607
+ alt: user.name,
608
+ style: {
609
+ width: "12px",
610
+ height: "12px",
611
+ borderRadius: "50%",
612
+ marginRight: "4px",
613
+ display: "inline-block",
614
+ position: "relative",
615
+ top: "-1px",
616
+ background: solidBorderColor
617
+ }
618
+ }
619
+ ),
439
620
  user.name,
440
621
  " (",
441
622
  ellipsis(user.id.toString(), 8, "middle"),
442
623
  ")"
443
- )), !connectedUsers && /* @__PURE__ */ React2.createElement(
624
+ )), !connectedUsers && /* @__PURE__ */ React3.createElement(
444
625
  "div",
445
626
  {
446
627
  style: {
@@ -451,17 +632,17 @@ var StateInspector = memo(function StateInspector2({
451
632
  gap: "4px"
452
633
  }
453
634
  },
454
- /* @__PURE__ */ React2.createElement("span", null, "No connected users")
635
+ /* @__PURE__ */ React3.createElement("span", null, "No connected users")
455
636
  ))
456
637
  ),
457
- /* @__PURE__ */ React2.createElement(
638
+ /* @__PURE__ */ React3.createElement(
458
639
  DisclosureSection,
459
640
  {
460
641
  title: "Data",
461
642
  colorScheme,
462
643
  setOpen: setShowData,
463
644
  open: showData,
464
- right: /* @__PURE__ */ React2.createElement(
645
+ right: /* @__PURE__ */ React3.createElement(
465
646
  "span",
466
647
  {
467
648
  style: {
@@ -469,7 +650,7 @@ var StateInspector = memo(function StateInspector2({
469
650
  gap: "4px"
470
651
  }
471
652
  },
472
- /* @__PURE__ */ React2.createElement(
653
+ /* @__PURE__ */ React3.createElement(
473
654
  SmallButton,
474
655
  {
475
656
  theme,
@@ -481,14 +662,14 @@ var StateInspector = memo(function StateInspector2({
481
662
  )
482
663
  )
483
664
  },
484
- /* @__PURE__ */ React2.createElement("div", { style: styles.sectionInner }, /* @__PURE__ */ React2.createElement(InspectorRow, { colorScheme }, /* @__PURE__ */ React2.createElement(
665
+ /* @__PURE__ */ React3.createElement("div", { style: styles.sectionInner }, /* @__PURE__ */ React3.createElement(InspectorRow, { colorScheme }, /* @__PURE__ */ React3.createElement(
485
666
  ObjectInspector,
486
667
  {
487
668
  name: multiplayerStateManager.schema ? "state" : void 0,
488
669
  data: state,
489
670
  theme
490
671
  }
491
- )), multiplayerStateManager.schema && /* @__PURE__ */ React2.createElement(InspectorRow, { colorScheme }, /* @__PURE__ */ React2.createElement(
672
+ )), multiplayerStateManager.schema && /* @__PURE__ */ React3.createElement(InspectorRow, { colorScheme }, /* @__PURE__ */ React3.createElement(
492
673
  ObjectInspector,
493
674
  {
494
675
  name: "schema",
@@ -497,14 +678,14 @@ var StateInspector = memo(function StateInspector2({
497
678
  }
498
679
  )))
499
680
  ),
500
- /* @__PURE__ */ React2.createElement(
681
+ /* @__PURE__ */ React3.createElement(
501
682
  DisclosureSection,
502
683
  {
503
684
  open: showHistory,
504
685
  setOpen: setShowHistory,
505
686
  title: "History",
506
687
  colorScheme,
507
- right: /* @__PURE__ */ React2.createElement(
688
+ right: /* @__PURE__ */ React3.createElement(
508
689
  "span",
509
690
  {
510
691
  style: {
@@ -512,7 +693,7 @@ var StateInspector = memo(function StateInspector2({
512
693
  gap: "4px"
513
694
  }
514
695
  },
515
- /* @__PURE__ */ React2.createElement(
696
+ /* @__PURE__ */ React3.createElement(
516
697
  SmallButton,
517
698
  {
518
699
  disabled: !historySnapshot.canUndo,
@@ -521,7 +702,7 @@ var StateInspector = memo(function StateInspector2({
521
702
  multiplayerStateManager.undo();
522
703
  }
523
704
  },
524
- /* @__PURE__ */ React2.createElement("span", { style: { width: "12px", height: "12px" } }, /* @__PURE__ */ React2.createElement(
705
+ /* @__PURE__ */ React3.createElement("span", { style: { width: "12px", height: "12px" } }, /* @__PURE__ */ React3.createElement(
525
706
  "svg",
526
707
  {
527
708
  xmlns: "http://www.w3.org/2000/svg",
@@ -529,7 +710,7 @@ var StateInspector = memo(function StateInspector2({
529
710
  height: "1em",
530
711
  viewBox: "0 0 20 20"
531
712
  },
532
- /* @__PURE__ */ React2.createElement(
713
+ /* @__PURE__ */ React3.createElement(
533
714
  "path",
534
715
  {
535
716
  fill: "currentColor",
@@ -538,7 +719,7 @@ var StateInspector = memo(function StateInspector2({
538
719
  )
539
720
  ))
540
721
  ),
541
- /* @__PURE__ */ React2.createElement(
722
+ /* @__PURE__ */ React3.createElement(
542
723
  SmallButton,
543
724
  {
544
725
  disabled: !historySnapshot.canRedo,
@@ -547,7 +728,7 @@ var StateInspector = memo(function StateInspector2({
547
728
  multiplayerStateManager.redo();
548
729
  }
549
730
  },
550
- /* @__PURE__ */ React2.createElement("span", { style: { width: "12px", height: "12px" } }, /* @__PURE__ */ React2.createElement(
731
+ /* @__PURE__ */ React3.createElement("span", { style: { width: "12px", height: "12px" } }, /* @__PURE__ */ React3.createElement(
551
732
  "svg",
552
733
  {
553
734
  xmlns: "http://www.w3.org/2000/svg",
@@ -555,7 +736,7 @@ var StateInspector = memo(function StateInspector2({
555
736
  height: "1em",
556
737
  viewBox: "0 0 20 20"
557
738
  },
558
- /* @__PURE__ */ React2.createElement(
739
+ /* @__PURE__ */ React3.createElement(
559
740
  "path",
560
741
  {
561
742
  fill: "currentColor",
@@ -566,7 +747,7 @@ var StateInspector = memo(function StateInspector2({
566
747
  )
567
748
  )
568
749
  },
569
- /* @__PURE__ */ React2.createElement("div", { ref: historyContainerRef, style: styles.sectionInner }, /* @__PURE__ */ React2.createElement(
750
+ /* @__PURE__ */ React3.createElement("div", { ref: historyContainerRef, style: styles.sectionInner }, /* @__PURE__ */ React3.createElement(
570
751
  "div",
571
752
  {
572
753
  id: `${HISTORY_ELEMENT_PREFIX}0`,
@@ -580,7 +761,7 @@ var StateInspector = memo(function StateInspector2({
580
761
  background: historySnapshot.historyIndex === 0 ? "rgb(59 130 246 / 15%)" : void 0
581
762
  }
582
763
  },
583
- /* @__PURE__ */ React2.createElement(
764
+ /* @__PURE__ */ React3.createElement(
584
765
  "span",
585
766
  {
586
767
  style: {
@@ -592,7 +773,7 @@ var StateInspector = memo(function StateInspector2({
592
773
  },
593
774
  "Initial state"
594
775
  )
595
- ), historySnapshot.history.map((entry, index) => /* @__PURE__ */ React2.createElement(
776
+ ), historySnapshot.history.map((entry, index) => /* @__PURE__ */ React3.createElement(
596
777
  "div",
597
778
  {
598
779
  id: `${HISTORY_ELEMENT_PREFIX}${index + 1}`,
@@ -603,7 +784,7 @@ var StateInspector = memo(function StateInspector2({
603
784
  background: index + 1 === historySnapshot.historyIndex ? "rgb(59 130 246 / 15%)" : void 0
604
785
  }
605
786
  },
606
- entry.redoPatches?.map((patch, j) => /* @__PURE__ */ React2.createElement(
787
+ entry.redoPatches?.map((patch, j) => /* @__PURE__ */ React3.createElement(
607
788
  "pre",
608
789
  {
609
790
  key: j,
@@ -613,14 +794,14 @@ var StateInspector = memo(function StateInspector2({
613
794
  flexWrap: "wrap"
614
795
  }
615
796
  },
616
- 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 })),
617
- 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 })),
618
- 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))),
619
- patch.op === "move" && /* @__PURE__ */ React2.createElement(React2.Fragment, null, /* @__PURE__ */ React2.createElement("span", { style: { fontStyle: "italic" } }, pathToString(patch.from)), " -> ", /* @__PURE__ */ React2.createElement("span", { style: { fontStyle: "italic" } }, pathToString(patch.path)))
797
+ patch.op === "add" && /* @__PURE__ */ React3.createElement(React3.Fragment, null, /* @__PURE__ */ React3.createElement("span", { style: { fontStyle: "italic" } }, pathToString(patch.path)), " = ", /* @__PURE__ */ React3.createElement(ObjectInspector, { data: patch.value, theme })),
798
+ patch.op === "replace" && /* @__PURE__ */ React3.createElement(React3.Fragment, null, /* @__PURE__ */ React3.createElement("span", { style: { fontStyle: "italic" } }, pathToString(patch.path)), " = ", /* @__PURE__ */ React3.createElement(ObjectInspector, { data: patch.value, theme })),
799
+ patch.op === "remove" && /* @__PURE__ */ React3.createElement(React3.Fragment, null, /* @__PURE__ */ React3.createElement("span", { style: { color: theme.OBJECT_VALUE_STRING_COLOR } }, "delete", " "), /* @__PURE__ */ React3.createElement("span", { style: { fontStyle: "italic" } }, pathToString(patch.path))),
800
+ patch.op === "move" && /* @__PURE__ */ React3.createElement(React3.Fragment, null, /* @__PURE__ */ React3.createElement("span", { style: { fontStyle: "italic" } }, pathToString(patch.from)), " -> ", /* @__PURE__ */ React3.createElement("span", { style: { fontStyle: "italic" } }, pathToString(patch.path)))
620
801
  ))
621
802
  )))
622
803
  ),
623
- /* @__PURE__ */ React2.createElement(
804
+ /* @__PURE__ */ React3.createElement(
624
805
  DisclosureSection,
625
806
  {
626
807
  title: "Tasks",
@@ -628,7 +809,7 @@ var StateInspector = memo(function StateInspector2({
628
809
  open: showTasks,
629
810
  setOpen: setShowTasks
630
811
  },
631
- /* @__PURE__ */ React2.createElement("div", { style: styles.sectionInner }, tasks?.map((task) => /* @__PURE__ */ React2.createElement(
812
+ /* @__PURE__ */ React3.createElement("div", { style: styles.sectionInner }, tasks?.map((task) => /* @__PURE__ */ React3.createElement(
632
813
  InspectorRow,
633
814
  {
634
815
  key: task.id,
@@ -637,7 +818,7 @@ var StateInspector = memo(function StateInspector2({
637
818
  backgroundColor: task.status === "done" ? "rgba(0,255,0,0.2)" : task.status === "error" ? "rgba(255,0,0,0.2)" : void 0
638
819
  }
639
820
  },
640
- /* @__PURE__ */ React2.createElement(
821
+ /* @__PURE__ */ React3.createElement(
641
822
  ObjectInspector,
642
823
  {
643
824
  name: task.name,
@@ -647,7 +828,7 @@ var StateInspector = memo(function StateInspector2({
647
828
  )
648
829
  )))
649
830
  ),
650
- /* @__PURE__ */ React2.createElement(
831
+ /* @__PURE__ */ React3.createElement(
651
832
  DisclosureSection,
652
833
  {
653
834
  open: showEphemeral,
@@ -655,9 +836,17 @@ var StateInspector = memo(function StateInspector2({
655
836
  title: "Ephemeral User Data",
656
837
  colorScheme
657
838
  },
658
- /* @__PURE__ */ React2.createElement("div", { style: styles.sectionInner }, Object.entries(ephemeral).map(([key, value]) => /* @__PURE__ */ React2.createElement(InspectorRow, { key, colorScheme }, /* @__PURE__ */ React2.createElement(ObjectInspector, { name: key, data: value, theme }))))
839
+ /* @__PURE__ */ React3.createElement("div", { style: styles.sectionInner }, Object.entries(ephemeral).map(([key, value]) => /* @__PURE__ */ React3.createElement(InspectorRow, { key, colorScheme }, /* @__PURE__ */ React3.createElement(
840
+ ObjectInspector,
841
+ {
842
+ name: key,
843
+ data: value,
844
+ theme,
845
+ expandLevel: 10
846
+ }
847
+ ))))
659
848
  ),
660
- /* @__PURE__ */ React2.createElement(
849
+ /* @__PURE__ */ React3.createElement(
661
850
  DisclosureSection,
662
851
  {
663
852
  open: showEvents,
@@ -665,8 +854,8 @@ var StateInspector = memo(function StateInspector2({
665
854
  title: "Events",
666
855
  colorScheme
667
856
  },
668
- /* @__PURE__ */ React2.createElement("div", { ref: eventsContainerRef, style: styles.sectionInner }, connectionEvents?.map(
669
- (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(
857
+ /* @__PURE__ */ React3.createElement("div", { ref: eventsContainerRef, style: styles.sectionInner }, connectionEvents?.map(
858
+ (event, index) => event.type === "stateChange" ? /* @__PURE__ */ React3.createElement(InspectorRow, { key: index, colorScheme }, "connection:", " ", /* @__PURE__ */ React3.createElement("span", { style: { fontStyle: "italic" } }, event.state.toLowerCase())) : /* @__PURE__ */ React3.createElement(
670
859
  InspectorRow,
671
860
  {
672
861
  key: index,
@@ -676,7 +865,7 @@ var StateInspector = memo(function StateInspector2({
676
865
  padding: "0px 12px 1px"
677
866
  }
678
867
  },
679
- /* @__PURE__ */ React2.createElement(
868
+ /* @__PURE__ */ React3.createElement(
680
869
  ObjectInspector,
681
870
  {
682
871
  data: event.message,
@@ -689,7 +878,7 @@ var StateInspector = memo(function StateInspector2({
689
878
  expanded
690
879
  }) => {
691
880
  const direction = event.type === "send" ? "up" : "down";
692
- return depth === 0 ? /* @__PURE__ */ React2.createElement(ObjectRootLabel, { direction, data }) : /* @__PURE__ */ React2.createElement(
881
+ return depth === 0 ? /* @__PURE__ */ React3.createElement(ObjectRootLabel, { direction, data }) : /* @__PURE__ */ React3.createElement(
693
882
  ObjectLabel,
694
883
  {
695
884
  direction,
@@ -702,7 +891,7 @@ var StateInspector = memo(function StateInspector2({
702
891
  }
703
892
  )
704
893
  )
705
- ), !connectionEvents && /* @__PURE__ */ React2.createElement(
894
+ ), !connectionEvents && /* @__PURE__ */ React3.createElement(
706
895
  "div",
707
896
  {
708
897
  style: {
@@ -713,18 +902,18 @@ var StateInspector = memo(function StateInspector2({
713
902
  gap: "4px"
714
903
  }
715
904
  },
716
- /* @__PURE__ */ React2.createElement("span", null, "No recorded events")
905
+ /* @__PURE__ */ React3.createElement("span", null, "No recorded events")
717
906
  ))
718
907
  )
719
908
  );
720
909
  });
721
910
  var ObjectRootLabel = ({ name, data, direction }) => {
722
911
  if (typeof name === "string") {
723
- return /* @__PURE__ */ React2.createElement("span", null, /* @__PURE__ */ React2.createElement(ObjectName, { name }), /* @__PURE__ */ React2.createElement("span", null, ": "), /* @__PURE__ */ React2.createElement(ObjectPreview, { data }));
912
+ return /* @__PURE__ */ React3.createElement("span", null, /* @__PURE__ */ React3.createElement(ObjectName, { name }), /* @__PURE__ */ React3.createElement("span", null, ": "), /* @__PURE__ */ React3.createElement(ObjectPreview, { data }));
724
913
  }
725
914
  if (direction === "up" || direction === "down") {
726
915
  const arrow = direction === "up" ? "\u2191" : "\u2193";
727
- return /* @__PURE__ */ React2.createElement("span", null, /* @__PURE__ */ React2.createElement(
916
+ return /* @__PURE__ */ React3.createElement("span", null, /* @__PURE__ */ React3.createElement(
728
917
  "span",
729
918
  {
730
919
  style: {
@@ -741,9 +930,9 @@ var ObjectRootLabel = ({ name, data, direction }) => {
741
930
  }
742
931
  },
743
932
  arrow
744
- ), /* @__PURE__ */ React2.createElement(ObjectPreview, { data }));
933
+ ), /* @__PURE__ */ React3.createElement(ObjectPreview, { data }));
745
934
  } else {
746
- return /* @__PURE__ */ React2.createElement(ObjectPreview, { data });
935
+ return /* @__PURE__ */ React3.createElement(ObjectPreview, { data });
747
936
  }
748
937
  };
749
938
  function Arrow({
@@ -751,7 +940,7 @@ function Arrow({
751
940
  onClick,
752
941
  style
753
942
  }) {
754
- return /* @__PURE__ */ React2.createElement(
943
+ return /* @__PURE__ */ React3.createElement(
755
944
  "span",
756
945
  {
757
946
  role: "button",
@@ -794,7 +983,7 @@ function SmallButton({
794
983
  theme,
795
984
  disabled
796
985
  }) {
797
- return /* @__PURE__ */ React2.createElement(
986
+ return /* @__PURE__ */ React3.createElement(
798
987
  "button",
799
988
  {
800
989
  type: "button",
@@ -843,7 +1032,7 @@ function useStateInspector({
843
1032
  ephemeralUserData,
844
1033
  forceInit
845
1034
  }) {
846
- const [root, setRoot] = React3.useState(null);
1035
+ const [root, setRoot] = React4.useState(null);
847
1036
  useLayoutEffect2(() => {
848
1037
  if (disabled)
849
1038
  return;
@@ -859,7 +1048,7 @@ function useStateInspector({
859
1048
  if (!root)
860
1049
  return;
861
1050
  root.render(
862
- /* @__PURE__ */ React3.createElement(
1051
+ /* @__PURE__ */ React4.createElement(
863
1052
  StateInspector,
864
1053
  {
865
1054
  state,
@@ -896,7 +1085,7 @@ function useSyncStateManager(stateManager, path) {
896
1085
  () => typeof path === "string" ? stateManager.getState(path) : stateManager.getState(),
897
1086
  [stateManager, path]
898
1087
  );
899
- return useSyncExternalStore(
1088
+ return useSyncExternalStore2(
900
1089
  stateManager.addListener,
901
1090
  getSnapshot,
902
1091
  getSnapshot
@@ -943,21 +1132,21 @@ function useManagedState(createInitialState, options) {
943
1132
  return [state, setState, stateManager];
944
1133
  }
945
1134
  function useManagedHistory(stateManager) {
946
- return useSyncExternalStore(
1135
+ return useSyncExternalStore2(
947
1136
  stateManager.historyEmittor.addListener,
948
1137
  stateManager.getHistorySnapshot,
949
1138
  stateManager.getHistorySnapshot
950
1139
  );
951
1140
  }
952
1141
  function useSyncMultiplayerStateManager(multiplayerStateManager) {
953
- return useSyncExternalStore(
1142
+ return useSyncExternalStore2(
954
1143
  multiplayerStateManager.addListener,
955
1144
  multiplayerStateManager.getOptimisticState,
956
1145
  multiplayerStateManager.getOptimisticState
957
1146
  );
958
1147
  }
959
1148
  function useEphemeralUserData(ephemeralUserData) {
960
- return useSyncExternalStore(
1149
+ return useSyncExternalStore2(
961
1150
  ephemeralUserData.addListener,
962
1151
  ephemeralUserData.getSnapshot,
963
1152
  ephemeralUserData.getSnapshot
@@ -977,9 +1166,10 @@ function useMultiplayerState(initialState, options) {
977
1166
  );
978
1167
  const [tasks, setTasks] = useState2([]);
979
1168
  const [userId, setUserId] = useState2();
980
- const extras = useMemo(() => {
1169
+ const extras = useMemo3(() => {
981
1170
  return {
982
1171
  tasks,
1172
+ userId,
983
1173
  connectionEvents,
984
1174
  connectedUsers,
985
1175
  ephemeralUserData
@@ -990,7 +1180,7 @@ function useMultiplayerState(initialState, options) {
990
1180
  // }
991
1181
  // },
992
1182
  };
993
- }, [tasks, connectionEvents, connectedUsers, ephemeralUserData]);
1183
+ }, [tasks, connectionEvents, connectedUsers, ephemeralUserData, userId]);
994
1184
  const globalTrackEvents = useObservable(shouldTrackEvents$);
995
1185
  const globalTrackTasks = useObservable(shouldTrackTasks$);
996
1186
  const shouldTrackEvents = options?.trackConnectionEvents ?? globalTrackEvents;
@@ -1058,6 +1248,13 @@ function useMultiplayerState(initialState, options) {
1058
1248
  }
1059
1249
  export {
1060
1250
  StateInspector,
1251
+ UserNameTag,
1252
+ UserPointer2 as UserPointer,
1253
+ UserPointerContainer,
1254
+ UserPointerIcon,
1255
+ UserPointersOverlay,
1256
+ getAvatarInitials,
1257
+ getAvatarStyle,
1061
1258
  useEphemeralUserData,
1062
1259
  useManagedHistory,
1063
1260
  useManagedState,