@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.js CHANGED
@@ -75,7 +75,8 @@ function useObservable(observable) {
75
75
  }, [observable]);
76
76
  return value;
77
77
  }
78
- var trackEvents = new Observable(false);
78
+ var shouldTrackEvents$ = new Observable(false);
79
+ var shouldTrackTasks$ = new Observable(false);
79
80
 
80
81
  // src/inspector/useStateInspector.tsx
81
82
  var import_react4 = __toESM(require("react"));
@@ -119,6 +120,15 @@ var darkTheme = {
119
120
  BASE_BACKGROUND_COLOR: "transparent",
120
121
  OBJECT_NAME_COLOR: "rgba(255,255,255,0.7)"
121
122
  };
123
+ var styles = {
124
+ sectionInner: {
125
+ flex: "1 1 0",
126
+ overflowY: "auto",
127
+ overflowX: "hidden",
128
+ display: "flex",
129
+ flexDirection: "column"
130
+ }
131
+ };
122
132
  function ToggleButton({
123
133
  showInspector,
124
134
  setShowInspector,
@@ -141,7 +151,10 @@ function ToggleButton({
141
151
  justifyContent: "center",
142
152
  padding: "2px 0"
143
153
  },
144
- onClick: () => setShowInspector(!showInspector)
154
+ onClick: (event) => {
155
+ event.stopPropagation();
156
+ setShowInspector(!showInspector);
157
+ }
145
158
  },
146
159
  /* @__PURE__ */ import_react3.default.createElement("span", { style: { width: "12px", height: "12px" } }, showInspector === (anchor === "right") ? /* @__PURE__ */ import_react3.default.createElement(
147
160
  "svg",
@@ -189,7 +202,8 @@ function DisclosureSection({
189
202
  right,
190
203
  children,
191
204
  colorScheme,
192
- isFirst
205
+ isFirst,
206
+ style
193
207
  }) {
194
208
  const theme = colorScheme === "light" ? lightTheme : darkTheme;
195
209
  const borderColor = colorScheme === "light" ? "rgba(0,0,0,0.1)" : "rgba(255,255,255,0.1)";
@@ -199,7 +213,8 @@ function DisclosureSection({
199
213
  style: {
200
214
  flex: open ? "1 1 0" : "0",
201
215
  display: "flex",
202
- flexDirection: "column"
216
+ flexDirection: "column",
217
+ ...style
203
218
  }
204
219
  },
205
220
  /* @__PURE__ */ import_react3.default.createElement(
@@ -227,19 +242,62 @@ function DisclosureSection({
227
242
  }
228
243
  }
229
244
  ),
230
- /* @__PURE__ */ import_react3.default.createElement("span", { style: { flex: "1 1 0" } }, title),
245
+ /* @__PURE__ */ import_react3.default.createElement("span", { style: { flex: "1 1 0", userSelect: "none" } }, title),
231
246
  right
232
247
  ),
233
248
  open && children
234
249
  );
235
250
  }
251
+ function InspectorRow({
252
+ children,
253
+ colorScheme,
254
+ selected,
255
+ style,
256
+ variant
257
+ }) {
258
+ const solidBorderColor = colorScheme === "light" ? "rgb(223 223 223)" : "rgb(29 29 29)";
259
+ return /* @__PURE__ */ import_react3.default.createElement(
260
+ "div",
261
+ {
262
+ style: {
263
+ borderBottom: `1px solid ${solidBorderColor}`,
264
+ fontSize: "12px",
265
+ fontFamily: "Menlo, monospace",
266
+ padding: "2px 12px 1px",
267
+ display: "flex",
268
+ alignItems: "center",
269
+ 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,
270
+ // background:
271
+ // colorScheme === "light"
272
+ // ? "rgba(0,0,0,0.05)"
273
+ // : "rgba(255,255,255,0.05)",
274
+ ...style
275
+ }
276
+ },
277
+ /* @__PURE__ */ import_react3.default.createElement(
278
+ "span",
279
+ {
280
+ style: {
281
+ fontFamily: "Menlo, monospace",
282
+ fontSize: "11px",
283
+ borderRadius: 4,
284
+ display: "inline-block"
285
+ }
286
+ },
287
+ children
288
+ )
289
+ );
290
+ }
236
291
  var HISTORY_ELEMENT_PREFIX = "noya-multiplayer-history-";
237
292
  var StateInspector = (0, import_react3.memo)(function StateInspector2({
238
293
  state,
239
294
  connectionEvents,
295
+ connectedUsers,
296
+ tasks,
297
+ userId,
240
298
  unstyled,
241
299
  colorScheme = "light",
242
- stateManager,
300
+ multiplayerStateManager,
243
301
  anchor = "right",
244
302
  ...props
245
303
  }) {
@@ -261,15 +319,30 @@ var StateInspector = (0, import_react3.memo)(function StateInspector2({
261
319
  "noya-multiplayer-react-show-history",
262
320
  false
263
321
  );
322
+ const [showUsers, setShowUsers] = useLocalStorageState(
323
+ "noya-multiplayer-react-show-users",
324
+ true
325
+ );
326
+ const [showData, setShowData] = useLocalStorageState(
327
+ "noya-multiplayer-react-show-data",
328
+ true
329
+ );
330
+ const [showTasks, setShowTasks] = useLocalStorageState(
331
+ "noya-multiplayer-react-show-tasks",
332
+ false
333
+ );
264
334
  (0, import_react3.useEffect)(() => {
265
- trackEvents.set(showEvents);
335
+ shouldTrackEvents$.set(showEvents);
266
336
  }, [showEvents]);
337
+ (0, import_react3.useEffect)(() => {
338
+ shouldTrackTasks$.set(showTasks);
339
+ }, [showTasks]);
267
340
  (0, import_react3.useEffect)(() => {
268
341
  if (eventsContainerRef.current) {
269
342
  eventsContainerRef.current.scrollTop = eventsContainerRef.current.scrollHeight;
270
343
  }
271
344
  }, [connectionEvents]);
272
- const historySnapshot = useManagedHistory(stateManager);
345
+ const historySnapshot = useManagedHistory(multiplayerStateManager.sm);
273
346
  (0, import_react3.useEffect)(() => {
274
347
  if (historyContainerRef.current) {
275
348
  historyContainerRef.current.scrollTop = historyContainerRef.current.scrollHeight;
@@ -289,12 +362,11 @@ var StateInspector = (0, import_react3.memo)(function StateInspector2({
289
362
  }
290
363
  }, [historySnapshot.historyIndex]);
291
364
  const theme = colorScheme === "light" ? lightTheme : darkTheme;
292
- const borderColor = colorScheme === "light" ? "rgba(0,0,0,0.1)" : "rgba(255,255,255,0.1)";
293
365
  const solidBorderColor = colorScheme === "light" ? "rgb(223 223 223)" : "rgb(29 29 29)";
294
366
  const baseStyle = {
295
367
  position: "fixed",
296
368
  top: 12,
297
- ...anchor === "right" ? { right: 12 } : { left: 12 },
369
+ ...anchor.includes("right") ? { right: 12 } : { left: 12 },
298
370
  bottom: 12,
299
371
  width: 400,
300
372
  background: colorScheme === "light" ? "rgba(255,255,255,0.95)" : "rgba(0,0,0,0.95)",
@@ -320,8 +392,8 @@ var StateInspector = (0, import_react3.memo)(function StateInspector2({
320
392
  style: {
321
393
  ...baseStyle,
322
394
  padding: "4px 10px",
323
- bottom: void 0,
324
- width: void 0
395
+ width: void 0,
396
+ ...anchor.includes("bottom") ? { bottom: 12, top: void 0 } : { bottom: void 0 }
325
397
  },
326
398
  onClick: () => setShowInspector(true)
327
399
  },
@@ -348,10 +420,15 @@ var StateInspector = (0, import_react3.memo)(function StateInspector2({
348
420
  /* @__PURE__ */ import_react3.default.createElement(
349
421
  DisclosureSection,
350
422
  {
351
- open: true,
352
- title: "Data",
353
- colorScheme,
354
423
  isFirst: true,
424
+ open: showUsers,
425
+ setOpen: setShowUsers,
426
+ title: "Users",
427
+ colorScheme,
428
+ style: {
429
+ flex: "0 0 auto",
430
+ maxHeight: "200px"
431
+ },
355
432
  right: /* @__PURE__ */ import_react3.default.createElement(
356
433
  ToggleButton,
357
434
  {
@@ -362,20 +439,54 @@ var StateInspector = (0, import_react3.memo)(function StateInspector2({
362
439
  }
363
440
  )
364
441
  },
365
- /* @__PURE__ */ import_react3.default.createElement(
442
+ /* @__PURE__ */ import_react3.default.createElement("div", { style: { ...styles.sectionInner, flex: "0 0 auto" } }, connectedUsers?.map((user) => /* @__PURE__ */ import_react3.default.createElement(
443
+ InspectorRow,
444
+ {
445
+ key: user.id,
446
+ colorScheme,
447
+ variant: user.id === userId ? "up" : void 0
448
+ },
449
+ user.name,
450
+ " (",
451
+ ellipsis(user.id.toString(), 8, "middle"),
452
+ ")"
453
+ )), !connectedUsers && /* @__PURE__ */ import_react3.default.createElement(
366
454
  "div",
367
455
  {
368
456
  style: {
369
- flex: "1 1 0",
370
- overflowY: "auto",
457
+ padding: "12px",
458
+ fontSize: "12px",
371
459
  display: "flex",
372
460
  flexDirection: "column",
373
- gap: "1px",
374
- padding: "1px 12px"
461
+ gap: "4px"
375
462
  }
376
463
  },
377
- /* @__PURE__ */ import_react3.default.createElement(import_react_inspector.ObjectInspector, { data: state, theme })
378
- )
464
+ /* @__PURE__ */ import_react3.default.createElement("span", null, "No connected users")
465
+ ))
466
+ ),
467
+ /* @__PURE__ */ import_react3.default.createElement(
468
+ DisclosureSection,
469
+ {
470
+ title: "Data",
471
+ colorScheme,
472
+ setOpen: setShowData,
473
+ open: showData
474
+ },
475
+ /* @__PURE__ */ import_react3.default.createElement("div", { style: styles.sectionInner }, /* @__PURE__ */ import_react3.default.createElement(InspectorRow, { colorScheme }, /* @__PURE__ */ import_react3.default.createElement(
476
+ import_react_inspector.ObjectInspector,
477
+ {
478
+ name: multiplayerStateManager.sm.schema ? "state" : void 0,
479
+ data: state,
480
+ theme
481
+ }
482
+ )), multiplayerStateManager.sm.schema && /* @__PURE__ */ import_react3.default.createElement(InspectorRow, { colorScheme }, /* @__PURE__ */ import_react3.default.createElement(
483
+ import_react_inspector.ObjectInspector,
484
+ {
485
+ name: "schema",
486
+ data: multiplayerStateManager.sm.schema,
487
+ theme
488
+ }
489
+ )))
379
490
  ),
380
491
  /* @__PURE__ */ import_react3.default.createElement(
381
492
  DisclosureSection,
@@ -385,173 +496,144 @@ var StateInspector = (0, import_react3.memo)(function StateInspector2({
385
496
  title: "History",
386
497
  colorScheme
387
498
  },
388
- /* @__PURE__ */ import_react3.default.createElement(
499
+ /* @__PURE__ */ import_react3.default.createElement("div", { ref: historyContainerRef, style: styles.sectionInner }, /* @__PURE__ */ import_react3.default.createElement(
389
500
  "div",
390
501
  {
391
- ref: historyContainerRef,
502
+ id: `${HISTORY_ELEMENT_PREFIX}0`,
392
503
  style: {
393
- flex: "1 1 0",
394
- overflowY: "auto",
395
- overflowX: "hidden",
504
+ borderBottom: `1px solid ${solidBorderColor}`,
505
+ fontSize: "12px",
506
+ fontFamily: "Menlo, monospace",
507
+ padding: "2px 12px 1px",
396
508
  display: "flex",
397
- flexDirection: "column"
509
+ alignItems: "center",
510
+ background: historySnapshot.historyIndex === 0 ? "rgb(59 130 246 / 15%)" : void 0
398
511
  }
399
512
  },
400
513
  /* @__PURE__ */ import_react3.default.createElement(
401
- "div",
514
+ "span",
402
515
  {
403
- id: `${HISTORY_ELEMENT_PREFIX}0`,
404
516
  style: {
405
- borderBottom: `1px solid ${solidBorderColor}`,
406
- fontSize: "12px",
407
517
  fontFamily: "Menlo, monospace",
408
- padding: "2px 12px 1px",
409
- display: "flex",
410
- alignItems: "center",
411
- background: historySnapshot.historyIndex === 0 ? "rgb(59 130 246 / 15%)" : void 0
518
+ fontSize: "11px",
519
+ borderRadius: 4,
520
+ display: "inline-block"
412
521
  }
413
522
  },
414
- /* @__PURE__ */ import_react3.default.createElement(
415
- "span",
416
- {
417
- style: {
418
- fontFamily: "Menlo, monospace",
419
- fontSize: "11px",
420
- borderRadius: 4,
421
- display: "inline-block"
422
- }
423
- },
424
- "Initial state"
425
- )
426
- ),
427
- historySnapshot.history.map((entry, index) => /* @__PURE__ */ import_react3.default.createElement(
428
- "div",
523
+ "Initial state"
524
+ )
525
+ ), historySnapshot.history.map((entry, index) => /* @__PURE__ */ import_react3.default.createElement(
526
+ "div",
527
+ {
528
+ id: `${HISTORY_ELEMENT_PREFIX}${index + 1}`,
529
+ key: index,
530
+ style: {
531
+ padding: "0px 12px 1px",
532
+ borderBottom: `1px solid ${solidBorderColor}`,
533
+ background: index + 1 === historySnapshot.historyIndex ? "rgb(59 130 246 / 15%)" : void 0
534
+ }
535
+ },
536
+ entry.redoPatches?.map((patch, j) => /* @__PURE__ */ import_react3.default.createElement(
537
+ "pre",
429
538
  {
430
- id: `${HISTORY_ELEMENT_PREFIX}${index + 1}`,
431
- key: index,
539
+ key: j,
432
540
  style: {
433
- padding: "0px 12px 1px",
434
- borderBottom: `1px solid ${solidBorderColor}`,
435
- background: index + 1 === historySnapshot.historyIndex ? "rgb(59 130 246 / 15%)" : void 0
541
+ fontSize: "11px",
542
+ display: "flex",
543
+ flexWrap: "wrap"
436
544
  }
437
545
  },
438
- entry.redoPatches?.map((patch, j) => /* @__PURE__ */ import_react3.default.createElement(
439
- "pre",
440
- {
441
- key: j,
442
- style: {
443
- fontSize: "11px",
444
- display: "flex",
445
- flexWrap: "wrap"
446
- }
447
- },
448
- patch.op === "add" && /* @__PURE__ */ import_react3.default.createElement(import_react3.default.Fragment, null, /* @__PURE__ */ import_react3.default.createElement("span", { style: { fontStyle: "italic" } }, pathToString(patch.path)), " = ", /* @__PURE__ */ import_react3.default.createElement(import_react_inspector.ObjectInspector, { data: patch.value, theme })),
449
- patch.op === "replace" && /* @__PURE__ */ import_react3.default.createElement(import_react3.default.Fragment, null, /* @__PURE__ */ import_react3.default.createElement("span", { style: { fontStyle: "italic" } }, pathToString(patch.path)), " = ", /* @__PURE__ */ import_react3.default.createElement(import_react_inspector.ObjectInspector, { data: patch.value, theme })),
450
- patch.op === "remove" && /* @__PURE__ */ import_react3.default.createElement(import_react3.default.Fragment, null, /* @__PURE__ */ import_react3.default.createElement("span", { style: { color: theme.OBJECT_VALUE_STRING_COLOR } }, "delete", " "), /* @__PURE__ */ import_react3.default.createElement("span", { style: { fontStyle: "italic" } }, pathToString(patch.path)))
451
- ))
546
+ patch.op === "add" && /* @__PURE__ */ import_react3.default.createElement(import_react3.default.Fragment, null, /* @__PURE__ */ import_react3.default.createElement("span", { style: { fontStyle: "italic" } }, pathToString(patch.path)), " = ", /* @__PURE__ */ import_react3.default.createElement(import_react_inspector.ObjectInspector, { data: patch.value, theme })),
547
+ patch.op === "replace" && /* @__PURE__ */ import_react3.default.createElement(import_react3.default.Fragment, null, /* @__PURE__ */ import_react3.default.createElement("span", { style: { fontStyle: "italic" } }, pathToString(patch.path)), " = ", /* @__PURE__ */ import_react3.default.createElement(import_react_inspector.ObjectInspector, { data: patch.value, theme })),
548
+ patch.op === "remove" && /* @__PURE__ */ import_react3.default.createElement(import_react3.default.Fragment, null, /* @__PURE__ */ import_react3.default.createElement("span", { style: { color: theme.OBJECT_VALUE_STRING_COLOR } }, "delete", " "), /* @__PURE__ */ import_react3.default.createElement("span", { style: { fontStyle: "italic" } }, pathToString(patch.path)))
452
549
  ))
453
- )
550
+ )))
454
551
  ),
455
552
  /* @__PURE__ */ import_react3.default.createElement(
456
553
  DisclosureSection,
457
554
  {
458
- open: showEvents,
459
- setOpen: setShowEvents,
460
- title: "Events",
461
- colorScheme
555
+ title: "Tasks",
556
+ colorScheme,
557
+ open: showTasks,
558
+ setOpen: setShowTasks
462
559
  },
463
- /* @__PURE__ */ import_react3.default.createElement(
464
- "div",
560
+ /* @__PURE__ */ import_react3.default.createElement("div", { style: styles.sectionInner }, tasks?.map((task) => /* @__PURE__ */ import_react3.default.createElement(
561
+ InspectorRow,
465
562
  {
466
- ref: eventsContainerRef,
563
+ key: task.id,
564
+ colorScheme,
467
565
  style: {
468
- flex: "1 1 0",
469
- overflowY: "auto",
470
- display: "flex",
471
- flexDirection: "column"
566
+ backgroundColor: task.status === "done" ? "rgba(0,255,0,0.2)" : task.status === "error" ? "rgba(255,0,0,0.2)" : void 0
472
567
  }
473
568
  },
474
- connectionEvents?.map((event, index) => /* @__PURE__ */ import_react3.default.createElement(
475
- "div",
569
+ /* @__PURE__ */ import_react3.default.createElement(
570
+ import_react_inspector.ObjectInspector,
571
+ {
572
+ name: task.name,
573
+ data: task.payload,
574
+ theme
575
+ }
576
+ )
577
+ )))
578
+ ),
579
+ /* @__PURE__ */ import_react3.default.createElement(
580
+ DisclosureSection,
581
+ {
582
+ open: showEvents,
583
+ setOpen: setShowEvents,
584
+ title: "Events",
585
+ colorScheme
586
+ },
587
+ /* @__PURE__ */ import_react3.default.createElement("div", { ref: eventsContainerRef, style: styles.sectionInner }, connectionEvents?.map(
588
+ (event, index) => event.type === "stateChange" ? /* @__PURE__ */ import_react3.default.createElement(InspectorRow, { key: index, colorScheme }, "connection:", " ", /* @__PURE__ */ import_react3.default.createElement("span", { style: { fontStyle: "italic" } }, event.state.toLowerCase())) : /* @__PURE__ */ import_react3.default.createElement(
589
+ InspectorRow,
476
590
  {
477
591
  key: index,
592
+ colorScheme,
593
+ variant: event.type === "send" ? "up" : "down",
478
594
  style: {
479
- borderBottom: `1px solid ${borderColor}`
595
+ padding: "0px 12px 1px"
480
596
  }
481
597
  },
482
- event.type === "stateChange" ? /* @__PURE__ */ import_react3.default.createElement(
483
- "div",
484
- {
485
- style: {
486
- padding: "2px 12px",
487
- background: colorScheme === "light" ? "rgba(0,0,0,0.05)" : "rgba(255,255,255,0.05)",
488
- display: "flex",
489
- alignItems: "center"
490
- }
491
- },
492
- /* @__PURE__ */ import_react3.default.createElement(
493
- "span",
494
- {
495
- style: {
496
- fontFamily: "Menlo, monospace",
497
- fontSize: "11px",
498
- borderRadius: 4,
499
- display: "inline-block"
500
- }
501
- },
502
- "connection:",
503
- " ",
504
- /* @__PURE__ */ import_react3.default.createElement("span", { style: { fontStyle: "italic" } }, event.state.toLowerCase())
505
- )
506
- ) : /* @__PURE__ */ import_react3.default.createElement(
507
- "div",
598
+ /* @__PURE__ */ import_react3.default.createElement(
599
+ import_react_inspector.ObjectInspector,
508
600
  {
509
- style: {
510
- padding: "0px 12px 1px",
511
- background: event.type === "send" ? "rgba(0,255,0,0.1)" : "rgba(255,0,0,0.1)"
512
- }
513
- },
514
- /* @__PURE__ */ import_react3.default.createElement(
515
- import_react_inspector.ObjectInspector,
516
- {
517
- data: event.message,
518
- theme,
519
- nodeRenderer: ({
520
- depth,
521
- name,
522
- data,
523
- isNonenumerable,
524
- expanded
525
- }) => {
526
- const direction = event.type === "send" ? "up" : "down";
527
- return depth === 0 ? /* @__PURE__ */ import_react3.default.createElement(ObjectRootLabel, { direction, data }) : /* @__PURE__ */ import_react3.default.createElement(
528
- import_react_inspector.ObjectLabel,
529
- {
530
- direction,
531
- name,
532
- data,
533
- isNonenumerable
534
- }
535
- );
536
- }
601
+ data: event.message,
602
+ theme,
603
+ nodeRenderer: ({
604
+ depth,
605
+ name,
606
+ data,
607
+ isNonenumerable,
608
+ expanded
609
+ }) => {
610
+ const direction = event.type === "send" ? "up" : "down";
611
+ return depth === 0 ? /* @__PURE__ */ import_react3.default.createElement(ObjectRootLabel, { direction, data }) : /* @__PURE__ */ import_react3.default.createElement(
612
+ import_react_inspector.ObjectLabel,
613
+ {
614
+ direction,
615
+ name,
616
+ data,
617
+ isNonenumerable
618
+ }
619
+ );
537
620
  }
538
- )
539
- )
540
- )),
541
- !connectionEvents && /* @__PURE__ */ import_react3.default.createElement(
542
- "div",
543
- {
544
- style: {
545
- padding: "12px",
546
- fontSize: "12px",
547
- display: "flex",
548
- flexDirection: "column",
549
- gap: "4px"
550
621
  }
551
- },
552
- /* @__PURE__ */ import_react3.default.createElement("span", null, "No recorded events")
622
+ )
553
623
  )
554
- )
624
+ ), !connectionEvents && /* @__PURE__ */ import_react3.default.createElement(
625
+ "div",
626
+ {
627
+ style: {
628
+ padding: "12px",
629
+ fontSize: "12px",
630
+ display: "flex",
631
+ flexDirection: "column",
632
+ gap: "4px"
633
+ }
634
+ },
635
+ /* @__PURE__ */ import_react3.default.createElement("span", null, "No recorded events")
636
+ ))
555
637
  )
556
638
  );
557
639
  });
@@ -634,10 +716,13 @@ function createPortalElement() {
634
716
  function useStateInspector({
635
717
  state,
636
718
  connectionEvents,
719
+ connectedUsers,
720
+ tasks,
721
+ userId,
637
722
  disabled = false,
638
723
  colorScheme,
639
724
  anchor,
640
- stateManager
725
+ multiplayerStateManager
641
726
  }) {
642
727
  const [root, setRoot] = import_react4.default.useState(null);
643
728
  (0, import_react4.useLayoutEffect)(() => {
@@ -660,13 +745,26 @@ function useStateInspector({
660
745
  {
661
746
  state,
662
747
  connectionEvents,
748
+ connectedUsers,
749
+ tasks,
750
+ userId,
663
751
  colorScheme,
664
752
  anchor,
665
- stateManager
753
+ multiplayerStateManager
666
754
  }
667
755
  )
668
756
  );
669
- }, [anchor, colorScheme, connectionEvents, root, state, stateManager]);
757
+ }, [
758
+ anchor,
759
+ colorScheme,
760
+ connectedUsers,
761
+ connectionEvents,
762
+ root,
763
+ state,
764
+ multiplayerStateManager,
765
+ userId,
766
+ tasks
767
+ ]);
670
768
  }
671
769
 
672
770
  // src/hooks.ts
@@ -737,45 +835,80 @@ function useSyncMultiplayerStateManager(multiplayerStateManager) {
737
835
  }
738
836
  var defaultStubSync = (0, import_state_manager.stubSync)();
739
837
  function useMultiplayerState(initialState, options) {
740
- const { sync = defaultStubSync, ...rest } = options;
838
+ const { sync = defaultStubSync, ...rest } = options ?? {};
741
839
  const [multiplayerStateManager] = (0, import_react5.useState)(
742
840
  () => new import_state_manager.MultiplayerStateManager(initialState, rest)
743
841
  );
744
842
  const syncRef = (0, import_react5.useRef)(sync);
745
843
  const [connectionEvents, setConnectionEvents] = (0, import_react5.useState)();
844
+ const [connectedUsers, setConnectedUsers] = (0, import_react5.useState)([]);
845
+ const [tasks, setTasks] = (0, import_react5.useState)([]);
846
+ const [userId, setUserId] = (0, import_react5.useState)();
746
847
  const extras = (0, import_react5.useMemo)(() => {
747
848
  return {
748
- connectionEvents
849
+ tasks,
850
+ connectionEvents,
851
+ connectedUsers
852
+ // evaluate: async (code: string) => {
853
+ // const payload = await rpcManager.request({ type: "eval", code });
854
+ // if (payload.type !== "eval") {
855
+ // throw new Error(`Unexpected rpc response type: ${payload.type}`);
856
+ // }
857
+ // },
749
858
  };
750
- }, [connectionEvents]);
751
- const globalTrackEvents = useObservable(trackEvents);
752
- const trackConnectionEvents = options.trackConnectionEvents ?? globalTrackEvents;
859
+ }, [tasks, connectionEvents, connectedUsers]);
860
+ const globalTrackEvents = useObservable(shouldTrackEvents$);
861
+ const globalTrackTasks = useObservable(shouldTrackTasks$);
862
+ const shouldTrackEvents = options?.trackConnectionEvents ?? globalTrackEvents;
863
+ const shouldTrackTasks = options?.trackTasks ?? globalTrackTasks;
753
864
  const trackEventsCallbackRef = (0, import_react5.useRef)();
865
+ const trackConnectedUsersCallbackRef = (0, import_react5.useRef)();
866
+ const trackTasksCallbackRef = (0, import_react5.useRef)();
754
867
  (0, import_react5.useEffect)(() => {
755
- trackEventsCallbackRef.current = trackConnectionEvents ? (e) => setConnectionEvents((events) => {
868
+ trackEventsCallbackRef.current = shouldTrackEvents ? (e) => setConnectionEvents((events) => {
756
869
  const updated = events ? [...events, e] : [e];
757
870
  return updated.length > 50 ? updated.slice(-50) : updated;
758
871
  }) : void 0;
759
- }, [trackConnectionEvents]);
872
+ }, [shouldTrackEvents]);
873
+ (0, import_react5.useEffect)(() => {
874
+ trackConnectedUsersCallbackRef.current = (users, userId2) => {
875
+ setConnectedUsers(users);
876
+ setUserId(userId2);
877
+ };
878
+ }, []);
879
+ (0, import_react5.useEffect)(() => {
880
+ trackTasksCallbackRef.current = shouldTrackTasks ? (tasks2) => {
881
+ setTasks((old) => {
882
+ const newIds = new Set(tasks2.map((t) => t.id));
883
+ const unchanged = old.filter((t) => !newIds.has(t.id));
884
+ return [...unchanged, ...tasks2];
885
+ });
886
+ } : void 0;
887
+ }, [shouldTrackTasks]);
760
888
  (0, import_react5.useEffect)(() => {
761
889
  if (syncRef.current) {
762
- return syncRef.current(
763
- multiplayerStateManager,
764
- (e) => trackEventsCallbackRef.current?.(e)
765
- );
890
+ return syncRef.current({
891
+ ms: multiplayerStateManager,
892
+ onConnectionEvent: (e) => trackEventsCallbackRef.current?.(e),
893
+ onChangeConnectedUsers: (users, userId2) => trackConnectedUsersCallbackRef.current?.(users, userId2),
894
+ onChangeTasks: (tasks2) => trackTasksCallbackRef.current?.(tasks2)
895
+ });
766
896
  }
767
897
  }, [multiplayerStateManager]);
768
898
  const state = useSyncMultiplayerStateManager(multiplayerStateManager);
769
899
  useStateInspector({
770
- stateManager: multiplayerStateManager.sm,
900
+ multiplayerStateManager,
771
901
  state,
772
902
  connectionEvents,
773
- disabled: !options.inspector,
774
- ...typeof options.inspector === "object" && options.inspector
903
+ connectedUsers,
904
+ tasks,
905
+ userId,
906
+ disabled: !options?.inspector,
907
+ ...typeof options?.inspector === "object" && options.inspector
775
908
  });
776
909
  return [
777
910
  state,
778
- multiplayerStateManager.setOptimisticState,
911
+ multiplayerStateManager.setState,
779
912
  multiplayerStateManager,
780
913
  extras
781
914
  ];