@noya-app/noya-multiplayer-react 0.1.11 → 0.1.13

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
@@ -46,7 +46,8 @@ function useObservable(observable) {
46
46
  }, [observable]);
47
47
  return value;
48
48
  }
49
- var trackEvents = new Observable(false);
49
+ var shouldTrackEvents$ = new Observable(false);
50
+ var shouldTrackTasks$ = new Observable(false);
50
51
 
51
52
  // src/inspector/useStateInspector.tsx
52
53
  import React3, { useLayoutEffect as useLayoutEffect2 } from "react";
@@ -101,6 +102,15 @@ var darkTheme = {
101
102
  BASE_BACKGROUND_COLOR: "transparent",
102
103
  OBJECT_NAME_COLOR: "rgba(255,255,255,0.7)"
103
104
  };
105
+ var styles = {
106
+ sectionInner: {
107
+ flex: "1 1 0",
108
+ overflowY: "auto",
109
+ overflowX: "hidden",
110
+ display: "flex",
111
+ flexDirection: "column"
112
+ }
113
+ };
104
114
  function ToggleButton({
105
115
  showInspector,
106
116
  setShowInspector,
@@ -123,7 +133,10 @@ function ToggleButton({
123
133
  justifyContent: "center",
124
134
  padding: "2px 0"
125
135
  },
126
- onClick: () => setShowInspector(!showInspector)
136
+ onClick: (event) => {
137
+ event.stopPropagation();
138
+ setShowInspector(!showInspector);
139
+ }
127
140
  },
128
141
  /* @__PURE__ */ React2.createElement("span", { style: { width: "12px", height: "12px" } }, showInspector === (anchor === "right") ? /* @__PURE__ */ React2.createElement(
129
142
  "svg",
@@ -171,7 +184,8 @@ function DisclosureSection({
171
184
  right,
172
185
  children,
173
186
  colorScheme,
174
- isFirst
187
+ isFirst,
188
+ style
175
189
  }) {
176
190
  const theme = colorScheme === "light" ? lightTheme : darkTheme;
177
191
  const borderColor = colorScheme === "light" ? "rgba(0,0,0,0.1)" : "rgba(255,255,255,0.1)";
@@ -181,7 +195,8 @@ function DisclosureSection({
181
195
  style: {
182
196
  flex: open ? "1 1 0" : "0",
183
197
  display: "flex",
184
- flexDirection: "column"
198
+ flexDirection: "column",
199
+ ...style
185
200
  }
186
201
  },
187
202
  /* @__PURE__ */ React2.createElement(
@@ -209,19 +224,62 @@ function DisclosureSection({
209
224
  }
210
225
  }
211
226
  ),
212
- /* @__PURE__ */ React2.createElement("span", { style: { flex: "1 1 0" } }, title),
227
+ /* @__PURE__ */ React2.createElement("span", { style: { flex: "1 1 0", userSelect: "none" } }, title),
213
228
  right
214
229
  ),
215
230
  open && children
216
231
  );
217
232
  }
233
+ function InspectorRow({
234
+ children,
235
+ colorScheme,
236
+ selected,
237
+ style,
238
+ variant
239
+ }) {
240
+ const solidBorderColor = colorScheme === "light" ? "rgb(223 223 223)" : "rgb(29 29 29)";
241
+ return /* @__PURE__ */ React2.createElement(
242
+ "div",
243
+ {
244
+ style: {
245
+ borderBottom: `1px solid ${solidBorderColor}`,
246
+ fontSize: "12px",
247
+ fontFamily: "Menlo, monospace",
248
+ padding: "2px 12px 1px",
249
+ display: "flex",
250
+ alignItems: "center",
251
+ 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,
252
+ // background:
253
+ // colorScheme === "light"
254
+ // ? "rgba(0,0,0,0.05)"
255
+ // : "rgba(255,255,255,0.05)",
256
+ ...style
257
+ }
258
+ },
259
+ /* @__PURE__ */ React2.createElement(
260
+ "span",
261
+ {
262
+ style: {
263
+ fontFamily: "Menlo, monospace",
264
+ fontSize: "11px",
265
+ borderRadius: 4,
266
+ display: "inline-block"
267
+ }
268
+ },
269
+ children
270
+ )
271
+ );
272
+ }
218
273
  var HISTORY_ELEMENT_PREFIX = "noya-multiplayer-history-";
219
274
  var StateInspector = memo(function StateInspector2({
220
275
  state,
221
276
  connectionEvents,
277
+ connectedUsers,
278
+ tasks,
279
+ userId,
222
280
  unstyled,
223
281
  colorScheme = "light",
224
- stateManager,
282
+ multiplayerStateManager,
225
283
  anchor = "right",
226
284
  ...props
227
285
  }) {
@@ -243,15 +301,30 @@ var StateInspector = memo(function StateInspector2({
243
301
  "noya-multiplayer-react-show-history",
244
302
  false
245
303
  );
304
+ const [showUsers, setShowUsers] = useLocalStorageState(
305
+ "noya-multiplayer-react-show-users",
306
+ true
307
+ );
308
+ const [showData, setShowData] = useLocalStorageState(
309
+ "noya-multiplayer-react-show-data",
310
+ true
311
+ );
312
+ const [showTasks, setShowTasks] = useLocalStorageState(
313
+ "noya-multiplayer-react-show-tasks",
314
+ false
315
+ );
246
316
  useEffect2(() => {
247
- trackEvents.set(showEvents);
317
+ shouldTrackEvents$.set(showEvents);
248
318
  }, [showEvents]);
319
+ useEffect2(() => {
320
+ shouldTrackTasks$.set(showTasks);
321
+ }, [showTasks]);
249
322
  useEffect2(() => {
250
323
  if (eventsContainerRef.current) {
251
324
  eventsContainerRef.current.scrollTop = eventsContainerRef.current.scrollHeight;
252
325
  }
253
326
  }, [connectionEvents]);
254
- const historySnapshot = useManagedHistory(stateManager);
327
+ const historySnapshot = useManagedHistory(multiplayerStateManager.sm);
255
328
  useEffect2(() => {
256
329
  if (historyContainerRef.current) {
257
330
  historyContainerRef.current.scrollTop = historyContainerRef.current.scrollHeight;
@@ -271,12 +344,11 @@ var StateInspector = memo(function StateInspector2({
271
344
  }
272
345
  }, [historySnapshot.historyIndex]);
273
346
  const theme = colorScheme === "light" ? lightTheme : darkTheme;
274
- const borderColor = colorScheme === "light" ? "rgba(0,0,0,0.1)" : "rgba(255,255,255,0.1)";
275
347
  const solidBorderColor = colorScheme === "light" ? "rgb(223 223 223)" : "rgb(29 29 29)";
276
348
  const baseStyle = {
277
349
  position: "fixed",
278
350
  top: 12,
279
- ...anchor === "right" ? { right: 12 } : { left: 12 },
351
+ ...anchor.includes("right") ? { right: 12 } : { left: 12 },
280
352
  bottom: 12,
281
353
  width: 400,
282
354
  background: colorScheme === "light" ? "rgba(255,255,255,0.95)" : "rgba(0,0,0,0.95)",
@@ -302,8 +374,8 @@ var StateInspector = memo(function StateInspector2({
302
374
  style: {
303
375
  ...baseStyle,
304
376
  padding: "4px 10px",
305
- bottom: void 0,
306
- width: void 0
377
+ width: void 0,
378
+ ...anchor.includes("bottom") ? { bottom: 12, top: void 0 } : { bottom: void 0 }
307
379
  },
308
380
  onClick: () => setShowInspector(true)
309
381
  },
@@ -330,10 +402,15 @@ var StateInspector = memo(function StateInspector2({
330
402
  /* @__PURE__ */ React2.createElement(
331
403
  DisclosureSection,
332
404
  {
333
- open: true,
334
- title: "Data",
335
- colorScheme,
336
405
  isFirst: true,
406
+ open: showUsers,
407
+ setOpen: setShowUsers,
408
+ title: "Users",
409
+ colorScheme,
410
+ style: {
411
+ flex: "0 0 auto",
412
+ maxHeight: "200px"
413
+ },
337
414
  right: /* @__PURE__ */ React2.createElement(
338
415
  ToggleButton,
339
416
  {
@@ -344,20 +421,54 @@ var StateInspector = memo(function StateInspector2({
344
421
  }
345
422
  )
346
423
  },
347
- /* @__PURE__ */ React2.createElement(
424
+ /* @__PURE__ */ React2.createElement("div", { style: { ...styles.sectionInner, flex: "0 0 auto" } }, connectedUsers?.map((user) => /* @__PURE__ */ React2.createElement(
425
+ InspectorRow,
426
+ {
427
+ key: user.id,
428
+ colorScheme,
429
+ variant: user.id === userId ? "up" : void 0
430
+ },
431
+ user.name,
432
+ " (",
433
+ ellipsis(user.id.toString(), 8, "middle"),
434
+ ")"
435
+ )), !connectedUsers && /* @__PURE__ */ React2.createElement(
348
436
  "div",
349
437
  {
350
438
  style: {
351
- flex: "1 1 0",
352
- overflowY: "auto",
439
+ padding: "12px",
440
+ fontSize: "12px",
353
441
  display: "flex",
354
442
  flexDirection: "column",
355
- gap: "1px",
356
- padding: "1px 12px"
443
+ gap: "4px"
357
444
  }
358
445
  },
359
- /* @__PURE__ */ React2.createElement(ObjectInspector, { data: state, theme })
360
- )
446
+ /* @__PURE__ */ React2.createElement("span", null, "No connected users")
447
+ ))
448
+ ),
449
+ /* @__PURE__ */ React2.createElement(
450
+ DisclosureSection,
451
+ {
452
+ title: "Data",
453
+ colorScheme,
454
+ setOpen: setShowData,
455
+ open: showData
456
+ },
457
+ /* @__PURE__ */ React2.createElement("div", { style: styles.sectionInner }, /* @__PURE__ */ React2.createElement(InspectorRow, { colorScheme }, /* @__PURE__ */ React2.createElement(
458
+ ObjectInspector,
459
+ {
460
+ name: multiplayerStateManager.sm.schema ? "state" : void 0,
461
+ data: state,
462
+ theme
463
+ }
464
+ )), multiplayerStateManager.sm.schema && /* @__PURE__ */ React2.createElement(InspectorRow, { colorScheme }, /* @__PURE__ */ React2.createElement(
465
+ ObjectInspector,
466
+ {
467
+ name: "schema",
468
+ data: multiplayerStateManager.sm.schema,
469
+ theme
470
+ }
471
+ )))
361
472
  ),
362
473
  /* @__PURE__ */ React2.createElement(
363
474
  DisclosureSection,
@@ -367,173 +478,144 @@ var StateInspector = memo(function StateInspector2({
367
478
  title: "History",
368
479
  colorScheme
369
480
  },
370
- /* @__PURE__ */ React2.createElement(
481
+ /* @__PURE__ */ React2.createElement("div", { ref: historyContainerRef, style: styles.sectionInner }, /* @__PURE__ */ React2.createElement(
371
482
  "div",
372
483
  {
373
- ref: historyContainerRef,
484
+ id: `${HISTORY_ELEMENT_PREFIX}0`,
374
485
  style: {
375
- flex: "1 1 0",
376
- overflowY: "auto",
377
- overflowX: "hidden",
486
+ borderBottom: `1px solid ${solidBorderColor}`,
487
+ fontSize: "12px",
488
+ fontFamily: "Menlo, monospace",
489
+ padding: "2px 12px 1px",
378
490
  display: "flex",
379
- flexDirection: "column"
491
+ alignItems: "center",
492
+ background: historySnapshot.historyIndex === 0 ? "rgb(59 130 246 / 15%)" : void 0
380
493
  }
381
494
  },
382
495
  /* @__PURE__ */ React2.createElement(
383
- "div",
496
+ "span",
384
497
  {
385
- id: `${HISTORY_ELEMENT_PREFIX}0`,
386
498
  style: {
387
- borderBottom: `1px solid ${solidBorderColor}`,
388
- fontSize: "12px",
389
499
  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
500
+ fontSize: "11px",
501
+ borderRadius: 4,
502
+ display: "inline-block"
394
503
  }
395
504
  },
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.map((entry, index) => /* @__PURE__ */ React2.createElement(
410
- "div",
505
+ "Initial state"
506
+ )
507
+ ), historySnapshot.history.map((entry, index) => /* @__PURE__ */ React2.createElement(
508
+ "div",
509
+ {
510
+ id: `${HISTORY_ELEMENT_PREFIX}${index + 1}`,
511
+ key: index,
512
+ style: {
513
+ padding: "0px 12px 1px",
514
+ borderBottom: `1px solid ${solidBorderColor}`,
515
+ background: index + 1 === historySnapshot.historyIndex ? "rgb(59 130 246 / 15%)" : void 0
516
+ }
517
+ },
518
+ entry.redoPatches?.map((patch, j) => /* @__PURE__ */ React2.createElement(
519
+ "pre",
411
520
  {
412
- id: `${HISTORY_ELEMENT_PREFIX}${index + 1}`,
413
- key: index,
521
+ key: j,
414
522
  style: {
415
- padding: "0px 12px 1px",
416
- borderBottom: `1px solid ${solidBorderColor}`,
417
- background: index + 1 === historySnapshot.historyIndex ? "rgb(59 130 246 / 15%)" : void 0
523
+ fontSize: "11px",
524
+ display: "flex",
525
+ flexWrap: "wrap"
418
526
  }
419
527
  },
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
- ))
528
+ 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 })),
529
+ 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 })),
530
+ 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
531
  ))
435
- )
532
+ )))
436
533
  ),
437
534
  /* @__PURE__ */ React2.createElement(
438
535
  DisclosureSection,
439
536
  {
440
- open: showEvents,
441
- setOpen: setShowEvents,
442
- title: "Events",
443
- colorScheme
537
+ title: "Tasks",
538
+ colorScheme,
539
+ open: showTasks,
540
+ setOpen: setShowTasks
444
541
  },
445
- /* @__PURE__ */ React2.createElement(
446
- "div",
542
+ /* @__PURE__ */ React2.createElement("div", { style: styles.sectionInner }, tasks?.map((task) => /* @__PURE__ */ React2.createElement(
543
+ InspectorRow,
447
544
  {
448
- ref: eventsContainerRef,
545
+ key: task.id,
546
+ colorScheme,
449
547
  style: {
450
- flex: "1 1 0",
451
- overflowY: "auto",
452
- display: "flex",
453
- flexDirection: "column"
548
+ backgroundColor: task.status === "done" ? "rgba(0,255,0,0.2)" : task.status === "error" ? "rgba(255,0,0,0.2)" : void 0
454
549
  }
455
550
  },
456
- connectionEvents?.map((event, index) => /* @__PURE__ */ React2.createElement(
457
- "div",
551
+ /* @__PURE__ */ React2.createElement(
552
+ ObjectInspector,
553
+ {
554
+ name: task.name,
555
+ data: task.payload,
556
+ theme
557
+ }
558
+ )
559
+ )))
560
+ ),
561
+ /* @__PURE__ */ React2.createElement(
562
+ DisclosureSection,
563
+ {
564
+ open: showEvents,
565
+ setOpen: setShowEvents,
566
+ title: "Events",
567
+ colorScheme
568
+ },
569
+ /* @__PURE__ */ React2.createElement("div", { ref: eventsContainerRef, style: styles.sectionInner }, connectionEvents?.map(
570
+ (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(
571
+ InspectorRow,
458
572
  {
459
573
  key: index,
574
+ colorScheme,
575
+ variant: event.type === "send" ? "up" : "down",
460
576
  style: {
461
- borderBottom: `1px solid ${borderColor}`
577
+ padding: "0px 12px 1px"
462
578
  }
463
579
  },
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",
580
+ /* @__PURE__ */ React2.createElement(
581
+ ObjectInspector,
490
582
  {
491
- style: {
492
- padding: "0px 12px 1px",
493
- background: event.type === "send" ? "rgba(0,255,0,0.1)" : "rgba(255,0,0,0.1)"
494
- }
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
- }
583
+ data: event.message,
584
+ theme,
585
+ nodeRenderer: ({
586
+ depth,
587
+ name,
588
+ data,
589
+ isNonenumerable,
590
+ expanded
591
+ }) => {
592
+ const direction = event.type === "send" ? "up" : "down";
593
+ return depth === 0 ? /* @__PURE__ */ React2.createElement(ObjectRootLabel, { direction, data }) : /* @__PURE__ */ React2.createElement(
594
+ ObjectLabel,
595
+ {
596
+ direction,
597
+ name,
598
+ data,
599
+ isNonenumerable
600
+ }
601
+ );
519
602
  }
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
603
  }
533
- },
534
- /* @__PURE__ */ React2.createElement("span", null, "No recorded events")
604
+ )
535
605
  )
536
- )
606
+ ), !connectionEvents && /* @__PURE__ */ React2.createElement(
607
+ "div",
608
+ {
609
+ style: {
610
+ padding: "12px",
611
+ fontSize: "12px",
612
+ display: "flex",
613
+ flexDirection: "column",
614
+ gap: "4px"
615
+ }
616
+ },
617
+ /* @__PURE__ */ React2.createElement("span", null, "No recorded events")
618
+ ))
537
619
  )
538
620
  );
539
621
  });
@@ -616,10 +698,13 @@ function createPortalElement() {
616
698
  function useStateInspector({
617
699
  state,
618
700
  connectionEvents,
701
+ connectedUsers,
702
+ tasks,
703
+ userId,
619
704
  disabled = false,
620
705
  colorScheme,
621
706
  anchor,
622
- stateManager
707
+ multiplayerStateManager
623
708
  }) {
624
709
  const [root, setRoot] = React3.useState(null);
625
710
  useLayoutEffect2(() => {
@@ -642,13 +727,26 @@ function useStateInspector({
642
727
  {
643
728
  state,
644
729
  connectionEvents,
730
+ connectedUsers,
731
+ tasks,
732
+ userId,
645
733
  colorScheme,
646
734
  anchor,
647
- stateManager
735
+ multiplayerStateManager
648
736
  }
649
737
  )
650
738
  );
651
- }, [anchor, colorScheme, connectionEvents, root, state, stateManager]);
739
+ }, [
740
+ anchor,
741
+ colorScheme,
742
+ connectedUsers,
743
+ connectionEvents,
744
+ root,
745
+ state,
746
+ multiplayerStateManager,
747
+ userId,
748
+ tasks
749
+ ]);
652
750
  }
653
751
 
654
752
  // src/hooks.ts
@@ -719,45 +817,80 @@ function useSyncMultiplayerStateManager(multiplayerStateManager) {
719
817
  }
720
818
  var defaultStubSync = stubSync();
721
819
  function useMultiplayerState(initialState, options) {
722
- const { sync = defaultStubSync, ...rest } = options;
820
+ const { sync = defaultStubSync, ...rest } = options ?? {};
723
821
  const [multiplayerStateManager] = useState2(
724
822
  () => new MultiplayerStateManager(initialState, rest)
725
823
  );
726
824
  const syncRef = useRef(sync);
727
825
  const [connectionEvents, setConnectionEvents] = useState2();
826
+ const [connectedUsers, setConnectedUsers] = useState2([]);
827
+ const [tasks, setTasks] = useState2([]);
828
+ const [userId, setUserId] = useState2();
728
829
  const extras = useMemo(() => {
729
830
  return {
730
- connectionEvents
831
+ tasks,
832
+ connectionEvents,
833
+ connectedUsers
834
+ // evaluate: async (code: string) => {
835
+ // const payload = await rpcManager.request({ type: "eval", code });
836
+ // if (payload.type !== "eval") {
837
+ // throw new Error(`Unexpected rpc response type: ${payload.type}`);
838
+ // }
839
+ // },
731
840
  };
732
- }, [connectionEvents]);
733
- const globalTrackEvents = useObservable(trackEvents);
734
- const trackConnectionEvents = options.trackConnectionEvents ?? globalTrackEvents;
841
+ }, [tasks, connectionEvents, connectedUsers]);
842
+ const globalTrackEvents = useObservable(shouldTrackEvents$);
843
+ const globalTrackTasks = useObservable(shouldTrackTasks$);
844
+ const shouldTrackEvents = options?.trackConnectionEvents ?? globalTrackEvents;
845
+ const shouldTrackTasks = options?.trackTasks ?? globalTrackTasks;
735
846
  const trackEventsCallbackRef = useRef();
847
+ const trackConnectedUsersCallbackRef = useRef();
848
+ const trackTasksCallbackRef = useRef();
736
849
  useEffect3(() => {
737
- trackEventsCallbackRef.current = trackConnectionEvents ? (e) => setConnectionEvents((events) => {
850
+ trackEventsCallbackRef.current = shouldTrackEvents ? (e) => setConnectionEvents((events) => {
738
851
  const updated = events ? [...events, e] : [e];
739
852
  return updated.length > 50 ? updated.slice(-50) : updated;
740
853
  }) : void 0;
741
- }, [trackConnectionEvents]);
854
+ }, [shouldTrackEvents]);
855
+ useEffect3(() => {
856
+ trackConnectedUsersCallbackRef.current = (users, userId2) => {
857
+ setConnectedUsers(users);
858
+ setUserId(userId2);
859
+ };
860
+ }, []);
861
+ useEffect3(() => {
862
+ trackTasksCallbackRef.current = shouldTrackTasks ? (tasks2) => {
863
+ setTasks((old) => {
864
+ const newIds = new Set(tasks2.map((t) => t.id));
865
+ const unchanged = old.filter((t) => !newIds.has(t.id));
866
+ return [...unchanged, ...tasks2];
867
+ });
868
+ } : void 0;
869
+ }, [shouldTrackTasks]);
742
870
  useEffect3(() => {
743
871
  if (syncRef.current) {
744
- return syncRef.current(
745
- multiplayerStateManager,
746
- (e) => trackEventsCallbackRef.current?.(e)
747
- );
872
+ return syncRef.current({
873
+ ms: multiplayerStateManager,
874
+ onConnectionEvent: (e) => trackEventsCallbackRef.current?.(e),
875
+ onChangeConnectedUsers: (users, userId2) => trackConnectedUsersCallbackRef.current?.(users, userId2),
876
+ onChangeTasks: (tasks2) => trackTasksCallbackRef.current?.(tasks2)
877
+ });
748
878
  }
749
879
  }, [multiplayerStateManager]);
750
880
  const state = useSyncMultiplayerStateManager(multiplayerStateManager);
751
881
  useStateInspector({
752
- stateManager: multiplayerStateManager.sm,
882
+ multiplayerStateManager,
753
883
  state,
754
884
  connectionEvents,
755
- disabled: !options.inspector,
756
- ...typeof options.inspector === "object" && options.inspector
885
+ connectedUsers,
886
+ tasks,
887
+ userId,
888
+ disabled: !options?.inspector,
889
+ ...typeof options?.inspector === "object" && options.inspector
757
890
  });
758
891
  return [
759
892
  state,
760
- multiplayerStateManager.setOptimisticState,
893
+ multiplayerStateManager.setState,
761
894
  multiplayerStateManager,
762
895
  extras
763
896
  ];