@noya-app/noya-multiplayer-react 0.1.70 → 0.1.72

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.
@@ -23,6 +23,7 @@ import {
23
23
  import { ActivityEventsSection } from "./sections/ActivityEventsSection";
24
24
  import { EventsSection } from "./sections/EventsSection";
25
25
  import { HistorySection } from "./sections/HistorySection";
26
+ import { ServerScriptLogsSection } from "./sections/ServerScriptLogsSection";
26
27
  import { exportAll, importAll } from "./serialization";
27
28
  import { StateInspectorButton } from "./StateInspectorButton";
28
29
  import {
@@ -44,6 +45,7 @@ export type StateInspectorAnchor =
44
45
  export type StateInspectorOptions = {
45
46
  colorScheme?: "light" | "dark";
46
47
  anchor?: StateInspectorAnchor;
48
+ advanced?: boolean;
47
49
  };
48
50
 
49
51
  export const StateInspector = memoGeneric(function StateInspector<
@@ -51,28 +53,31 @@ export const StateInspector = memoGeneric(function StateInspector<
51
53
  M extends object,
52
54
  E extends object,
53
55
  MenuT extends string = string,
56
+ I extends Record<string, any> = Record<string, unknown>,
54
57
  >({
55
58
  noyaManager,
56
59
  colorScheme = "light",
57
60
  anchor = "right",
58
61
  unstyled = false,
62
+ advanced = false,
59
63
  ...props
60
64
  }: {
61
- noyaManager: NoyaManager<S, M, E, MenuT>;
65
+ noyaManager: NoyaManager<S, M, E, MenuT, I>;
62
66
  unstyled?: boolean;
63
67
  } & StateInspectorOptions &
64
68
  ComponentPropsWithoutRef<"div">) {
65
69
  const {
66
70
  multiplayerStateManager,
67
71
  assetManager,
68
- connectedUsersManager,
72
+ userManager,
69
73
  secretManager,
70
- ephemeralUserDataManager,
74
+ sharedConnectionDataManager,
71
75
  connectionEventManager,
72
76
  taskManager,
73
77
  ioManager,
74
78
  resourceManager,
75
79
  activityEventsManager,
80
+ logManager,
76
81
  } = noyaManager;
77
82
 
78
83
  const [didMount, setDidMount] = React.useState(false);
@@ -86,6 +91,7 @@ export const StateInspector = memoGeneric(function StateInspector<
86
91
 
87
92
  const eventsContainerRef = React.useRef<HTMLDivElement>(null);
88
93
  const activityEventsContainerRef = React.useRef<HTMLDivElement>(null);
94
+ const serverLogsContainerRef = React.useRef<HTMLDivElement>(null);
89
95
 
90
96
  const [showInspector, setShowInspector] = useLocalStorageState(
91
97
  "noya-multiplayer-react-show-inspector",
@@ -111,8 +117,8 @@ export const StateInspector = memoGeneric(function StateInspector<
111
117
  "noya-multiplayer-react-show-tasks",
112
118
  false
113
119
  );
114
- const [showEphemeral, setShowEphemeral] = useLocalStorageState(
115
- "noya-multiplayer-react-show-ephemeral",
120
+ const [showShared, setShowShared] = useLocalStorageState(
121
+ "noya-multiplayer-react-show-shared",
116
122
  false
117
123
  );
118
124
  const [showAssets, setShowAssets] = useLocalStorageState(
@@ -139,6 +145,10 @@ export const StateInspector = memoGeneric(function StateInspector<
139
145
  "noya-multiplayer-react-show-activity-events",
140
146
  false
141
147
  );
148
+ const [showServerLogs, setShowServerLogs] = useLocalStorageState(
149
+ "noya-multiplayer-react-show-server-logs",
150
+ false
151
+ );
142
152
 
143
153
  const connectionEvents = useObservable(connectionEventManager.events$);
144
154
  useEffect(() => {
@@ -147,11 +157,38 @@ export const StateInspector = memoGeneric(function StateInspector<
147
157
  eventsContainerRef.current.scrollHeight;
148
158
  }
149
159
  }, [connectionEvents]);
160
+ const serverLogs = useObservable(logManager.logs$);
161
+ const [serverLogsPaused, setServerLogsPaused] = React.useState(false);
162
+ const [visibleServerLogs, setVisibleServerLogs] = React.useState(
163
+ serverLogs ?? []
164
+ );
165
+
166
+ const toggleServerLogsPaused = useCallback(() => {
167
+ setServerLogsPaused((value) => {
168
+ if (!value) {
169
+ setVisibleServerLogs(serverLogs ?? []);
170
+ }
171
+ return !value;
172
+ });
173
+ }, [serverLogs]);
174
+
175
+ useEffect(() => {
176
+ if (serverLogsPaused) return;
177
+
178
+ setVisibleServerLogs(serverLogs ?? []);
179
+ }, [serverLogs, serverLogsPaused]);
180
+ useEffect(() => {
181
+ if (!showServerLogs) return;
182
+ if (serverLogsContainerRef.current) {
183
+ serverLogsContainerRef.current.scrollTop =
184
+ serverLogsContainerRef.current.scrollHeight;
185
+ }
186
+ }, [showServerLogs, visibleServerLogs]);
150
187
 
151
188
  const multipeerStateInitialized = useObservable(
152
189
  multiplayerStateManager.isInitialized$
153
190
  );
154
- const ephemeral = useObservable(ephemeralUserDataManager.data$);
191
+ const shared = useObservable(sharedConnectionDataManager.data$);
155
192
  const historySnapshot = useManagedHistory(multiplayerStateManager.sm);
156
193
  const assets = useObservable(assetManager.assets$);
157
194
  const resources = useObservable(resourceManager.resources$);
@@ -159,8 +196,8 @@ export const StateInspector = memoGeneric(function StateInspector<
159
196
  const assetsInitialized = useObservable(assetManager.isInitialized$);
160
197
  const secrets = useObservable(secretManager.secrets$);
161
198
  const secretsInitialized = useObservable(secretManager.isInitialized$);
162
- const connectedUsers = useObservable(connectedUsersManager.connectedUsers$);
163
- const userId = useObservable(connectedUsersManager.currentUserId$);
199
+ const connectedUsers = useObservable(userManager.connectedUsers$);
200
+ const connectionId = useObservable(userManager.currentConnectionId$);
164
201
  const state = useObservable(multiplayerStateManager.optimisticState$);
165
202
  const inputsInitialized = useObservable(ioManager.inputsInitialized$);
166
203
  const outputTransformsInitialized = useObservable(
@@ -263,31 +300,36 @@ export const StateInspector = memoGeneric(function StateInspector<
263
300
  }
264
301
  >
265
302
  <StateInspectorDisclosureRowInner style={{ flex: "0 0 auto" }}>
266
- {connectedUsers?.map((user) => (
267
- <StateInspectorRow
268
- key={user.id}
269
- colorScheme={colorScheme}
270
- variant={user.id === userId ? "up" : undefined}
271
- >
272
- {user.image && (
273
- <img
274
- src={user.image}
275
- alt={user.name}
276
- style={{
277
- width: "13px",
278
- height: "13px",
279
- borderRadius: "50%",
280
- marginRight: "4px",
281
- display: "inline-block",
282
- position: "relative",
283
- background: solidBorderColor,
284
- verticalAlign: "middle",
285
- }}
286
- />
287
- )}
288
- {user.name} ({ellipsis(user.id.toString(), 8, "middle")})
289
- </StateInspectorRow>
290
- ))}
303
+ {connectedUsers?.map((user, index, array) => {
304
+ const identifier = user.userId ?? user.connectionId;
305
+
306
+ return (
307
+ <StateInspectorRow
308
+ key={user.connectionId}
309
+ colorScheme={colorScheme}
310
+ variant={user.connectionId === connectionId ? "up" : undefined}
311
+ bordered={index !== array.length - 1}
312
+ >
313
+ {user.image && (
314
+ <img
315
+ src={user.image}
316
+ alt={user.name}
317
+ style={{
318
+ width: "13px",
319
+ height: "13px",
320
+ borderRadius: "50%",
321
+ marginRight: "4px",
322
+ display: "inline-block",
323
+ position: "relative",
324
+ background: solidBorderColor,
325
+ verticalAlign: "middle",
326
+ }}
327
+ />
328
+ )}
329
+ {user.name} ({ellipsis(identifier, 8, "middle")})
330
+ </StateInspectorRow>
331
+ );
332
+ })}
291
333
  {!connectedUsers && (
292
334
  <div
293
335
  style={{
@@ -447,239 +489,249 @@ export const StateInspector = memoGeneric(function StateInspector<
447
489
  ))}
448
490
  </StateInspectorDisclosureRowInner>
449
491
  </StateInspectorDisclosureSection>
450
- <StateInspectorDisclosureSection
451
- title={
452
- <StateInspectorTitleLabel>
453
- Resources ({resources.length})
454
- <ColoredDot type={resourcesInitialized ? "success" : "error"} />
455
- </StateInspectorTitleLabel>
456
- }
457
- colorScheme={colorScheme}
458
- open={showResources}
459
- setOpen={setShowResources}
460
- right={
461
- <span style={{ display: "flex", gap: "10px" }}>
462
- <StateInspectorButton
463
- theme={theme}
464
- onClick={async () => {
465
- const ok = confirm(
466
- "Are you sure you want to delete all resources?"
467
- );
468
- if (!ok) return;
469
- await resourceManager.deleteResource(
470
- resources.map((resource) => ({ id: resource.id }))
471
- );
472
- }}
473
- >
474
- Delete all
475
- </StateInspectorButton>
476
- <StateInspectorButton
477
- theme={theme}
478
- onClick={async () => {
479
- const path = prompt("Enter directory path") || uuid();
480
- await resourceManager.createResource({
481
- type: "directory",
482
- path,
483
- });
484
- }}
485
- >
486
- Create Directory
487
- </StateInspectorButton>
492
+ {advanced && (
493
+ <StateInspectorDisclosureSection
494
+ title={
495
+ <StateInspectorTitleLabel>
496
+ Resources ({resources.length})
497
+ <ColoredDot type={resourcesInitialized ? "success" : "error"} />
498
+ </StateInspectorTitleLabel>
499
+ }
500
+ colorScheme={colorScheme}
501
+ open={showResources}
502
+ setOpen={setShowResources}
503
+ right={
504
+ <span style={{ display: "flex", gap: "10px" }}>
505
+ <StateInspectorButton
506
+ theme={theme}
507
+ onClick={async () => {
508
+ const ok = confirm(
509
+ "Are you sure you want to delete all resources?"
510
+ );
511
+ if (!ok) return;
512
+ await resourceManager.deleteResource(
513
+ resources.map((resource) => ({ id: resource.id }))
514
+ );
515
+ }}
516
+ >
517
+ Delete all
518
+ </StateInspectorButton>
519
+ <StateInspectorButton
520
+ theme={theme}
521
+ onClick={async () => {
522
+ const path = prompt("Enter directory path") || uuid();
523
+ await resourceManager.createResource({
524
+ type: "directory",
525
+ path,
526
+ });
527
+ }}
528
+ >
529
+ Create Directory
530
+ </StateInspectorButton>
531
+ <StateInspectorButton
532
+ theme={theme}
533
+ onClick={async () => {
534
+ const file = await uploadFile();
535
+
536
+ resourceManager.createResource({
537
+ type: "asset",
538
+ path: file.name || uuid(),
539
+ asset: {
540
+ content: Base64.encode(await file.arrayBuffer()),
541
+ contentType: file.type,
542
+ encoding: "base64",
543
+ },
544
+ });
545
+ }}
546
+ >
547
+ Create Asset
548
+ </StateInspectorButton>
549
+ </span>
550
+ }
551
+ >
552
+ <StateInspectorDisclosureRowInner>
553
+ {resources?.map((resource) => (
554
+ <StateInspectorRow key={resource.id} colorScheme={colorScheme}>
555
+ <ObjectInspector
556
+ name={resource.path}
557
+ data={resource}
558
+ theme={theme}
559
+ />
560
+ <div style={{ display: "flex", gap: "4px" }}>
561
+ <StateInspectorButton
562
+ theme={theme}
563
+ onClick={() =>
564
+ resourceManager.deleteResource({ id: resource.id })
565
+ }
566
+ >
567
+ Delete
568
+ </StateInspectorButton>
569
+ <StateInspectorButton
570
+ theme={theme}
571
+ onClick={() => {
572
+ const path = prompt("Enter new path", resource.path);
573
+ if (!path) return;
574
+ resourceManager.updateResource({ id: resource.id, path });
575
+ }}
576
+ >
577
+ Rename
578
+ </StateInspectorButton>
579
+ </div>
580
+ </StateInspectorRow>
581
+ ))}
582
+ </StateInspectorDisclosureRowInner>
583
+ </StateInspectorDisclosureSection>
584
+ )}
585
+ {advanced && (
586
+ <StateInspectorDisclosureSection
587
+ title={
588
+ <StateInspectorTitleLabel>
589
+ Secrets
590
+ <ColoredDot type={secretsInitialized ? "success" : "error"} />
591
+ </StateInspectorTitleLabel>
592
+ }
593
+ colorScheme={colorScheme}
594
+ open={showSecrets}
595
+ setOpen={setShowSecrets}
596
+ right={
488
597
  <StateInspectorButton
489
598
  theme={theme}
490
- onClick={async () => {
491
- const file = await uploadFile();
492
-
493
- resourceManager.createResource({
494
- type: "asset",
495
- path: file.name || uuid(),
496
- asset: {
497
- content: Base64.encode(await file.arrayBuffer()),
498
- contentType: file.type,
499
- encoding: "base64",
500
- },
501
- });
599
+ onClick={() => {
600
+ const name = prompt("Enter secret name");
601
+ if (!name) return;
602
+ const value = prompt("Enter secret value");
603
+ if (!value) return;
604
+ secretManager.createSecret(null, name, value);
502
605
  }}
503
606
  >
504
- Create Asset
607
+ Create
505
608
  </StateInspectorButton>
506
- </span>
507
- }
508
- >
509
- <StateInspectorDisclosureRowInner>
510
- {resources?.map((resource) => (
511
- <StateInspectorRow key={resource.id} colorScheme={colorScheme}>
512
- <ObjectInspector
513
- name={resource.path}
514
- data={resource}
515
- theme={theme}
516
- />
517
- <div style={{ display: "flex", gap: "4px" }}>
609
+ }
610
+ >
611
+ <StateInspectorDisclosureRowInner>
612
+ {secrets.map((secret) => (
613
+ <StateInspectorRow key={secret.id} colorScheme={colorScheme}>
614
+ <ObjectInspector data={secret} theme={theme} />
518
615
  <StateInspectorButton
519
616
  theme={theme}
520
- onClick={() =>
521
- resourceManager.deleteResource({ id: resource.id })
522
- }
617
+ onClick={() => secretManager.deleteSecret(secret.id)}
523
618
  >
524
619
  Delete
525
620
  </StateInspectorButton>
526
- <StateInspectorButton
527
- theme={theme}
528
- onClick={() => {
529
- const path = prompt("Enter new path", resource.path);
530
- if (!path) return;
531
- resourceManager.updateResource({ id: resource.id, path });
532
- }}
533
- >
534
- Rename
535
- </StateInspectorButton>
536
- </div>
537
- </StateInspectorRow>
538
- ))}
539
- </StateInspectorDisclosureRowInner>
540
- </StateInspectorDisclosureSection>
541
- <StateInspectorDisclosureSection
542
- title={
543
- <StateInspectorTitleLabel>
544
- Secrets
545
- <ColoredDot type={secretsInitialized ? "success" : "error"} />
546
- </StateInspectorTitleLabel>
547
- }
548
- colorScheme={colorScheme}
549
- open={showSecrets}
550
- setOpen={setShowSecrets}
551
- right={
552
- <StateInspectorButton
553
- theme={theme}
554
- onClick={() => {
555
- const name = prompt("Enter secret name");
556
- if (!name) return;
557
- const value = prompt("Enter secret value");
558
- if (!value) return;
559
- secretManager.createSecret(null, name, value);
560
- }}
561
- >
562
- Create
563
- </StateInspectorButton>
564
- }
565
- >
566
- <StateInspectorDisclosureRowInner>
567
- {secrets.map((secret) => (
568
- <StateInspectorRow key={secret.id} colorScheme={colorScheme}>
569
- <ObjectInspector data={secret} theme={theme} />
570
- <StateInspectorButton
571
- theme={theme}
572
- onClick={() => secretManager.deleteSecret(secret.id)}
621
+ </StateInspectorRow>
622
+ ))}
623
+ </StateInspectorDisclosureRowInner>
624
+ </StateInspectorDisclosureSection>
625
+ )}
626
+ {advanced && (
627
+ <StateInspectorDisclosureSection
628
+ open={showInputs}
629
+ setOpen={setShowInputs}
630
+ title={
631
+ <StateInspectorTitleLabel>
632
+ Inputs
633
+ <ColoredDot type={inputsInitialized ? "success" : "error"} />
634
+ </StateInspectorTitleLabel>
635
+ }
636
+ colorScheme={colorScheme}
637
+ >
638
+ <StateInspectorDisclosureRowInner>
639
+ {inputs?.map((input: Input) => (
640
+ <StateInspectorRow key={input.id} colorScheme={colorScheme}>
641
+ <ObjectInspector data={input} theme={theme} />
642
+ </StateInspectorRow>
643
+ ))}
644
+ {!inputs?.length && (
645
+ <div
646
+ style={{
647
+ padding: "12px",
648
+ fontSize: "12px",
649
+ display: "flex",
650
+ flexDirection: "column",
651
+ gap: "4px",
652
+ }}
573
653
  >
574
- Delete
575
- </StateInspectorButton>
576
- </StateInspectorRow>
577
- ))}
578
- </StateInspectorDisclosureRowInner>
579
- </StateInspectorDisclosureSection>{" "}
580
- <StateInspectorDisclosureSection
581
- open={showInputs}
582
- setOpen={setShowInputs}
583
- title={
584
- <StateInspectorTitleLabel>
585
- Inputs
586
- <ColoredDot type={inputsInitialized ? "success" : "error"} />
587
- </StateInspectorTitleLabel>
588
- }
589
- colorScheme={colorScheme}
590
- >
591
- <StateInspectorDisclosureRowInner>
592
- {inputs?.map((input: Input) => (
593
- <StateInspectorRow key={input.id} colorScheme={colorScheme}>
594
- <ObjectInspector data={input} theme={theme} />
595
- </StateInspectorRow>
596
- ))}
597
- {!inputs?.length && (
598
- <div
599
- style={{
600
- padding: "12px",
601
- fontSize: "12px",
602
- display: "flex",
603
- flexDirection: "column",
604
- gap: "4px",
605
- }}
606
- >
607
- <span>No inputs</span>
608
- </div>
609
- )}
610
- </StateInspectorDisclosureRowInner>
611
- </StateInspectorDisclosureSection>
612
- <StateInspectorDisclosureSection
613
- open={showOutputTransforms}
614
- setOpen={setShowOutputTransforms}
615
- title={
616
- <StateInspectorTitleLabel>
617
- Output Transforms
618
- <ColoredDot
619
- type={outputTransformsInitialized ? "success" : "error"}
620
- />
621
- </StateInspectorTitleLabel>
622
- }
623
- colorScheme={colorScheme}
624
- >
625
- <StateInspectorDisclosureRowInner>
626
- {outputTransforms?.map((transform: OutputTransform) => (
627
- <StateInspectorRow key={transform.id} colorScheme={colorScheme}>
628
- <ObjectInspector data={transform} theme={theme} />
629
- </StateInspectorRow>
630
- ))}
631
- {!outputTransforms?.length && (
632
- <div
633
- style={{
634
- padding: "12px",
635
- fontSize: "12px",
636
- display: "flex",
637
- flexDirection: "column",
638
- gap: "4px",
639
- }}
640
- >
641
- <span>No output transforms</span>
642
- </div>
643
- )}
644
- </StateInspectorDisclosureRowInner>
645
- </StateInspectorDisclosureSection>
646
- <StateInspectorDisclosureSection
647
- title="Tasks"
648
- colorScheme={colorScheme}
649
- open={showTasks}
650
- setOpen={setShowTasks}
651
- >
652
- <StateInspectorDisclosureRowInner>
653
- {tasks?.map((task) => (
654
- <StateInspectorRow
655
- key={task.id}
656
- colorScheme={colorScheme}
657
- style={{
658
- backgroundColor:
659
- task.status === "done"
660
- ? "rgba(0,255,0,0.2)"
661
- : task.status === "error"
662
- ? "rgba(255,0,0,0.2)"
663
- : undefined,
664
- }}
665
- >
666
- <ObjectInspector
667
- name={task.name}
668
- data={task.payload}
669
- theme={theme}
654
+ <span>No inputs</span>
655
+ </div>
656
+ )}
657
+ </StateInspectorDisclosureRowInner>
658
+ </StateInspectorDisclosureSection>
659
+ )}
660
+ {advanced && (
661
+ <StateInspectorDisclosureSection
662
+ open={showOutputTransforms}
663
+ setOpen={setShowOutputTransforms}
664
+ title={
665
+ <StateInspectorTitleLabel>
666
+ Output Transforms
667
+ <ColoredDot
668
+ type={outputTransformsInitialized ? "success" : "error"}
670
669
  />
671
- </StateInspectorRow>
672
- ))}
673
- </StateInspectorDisclosureRowInner>
674
- </StateInspectorDisclosureSection>
670
+ </StateInspectorTitleLabel>
671
+ }
672
+ colorScheme={colorScheme}
673
+ >
674
+ <StateInspectorDisclosureRowInner>
675
+ {outputTransforms?.map((transform: OutputTransform) => (
676
+ <StateInspectorRow key={transform.id} colorScheme={colorScheme}>
677
+ <ObjectInspector data={transform} theme={theme} />
678
+ </StateInspectorRow>
679
+ ))}
680
+ {!outputTransforms?.length && (
681
+ <div
682
+ style={{
683
+ padding: "12px",
684
+ fontSize: "12px",
685
+ display: "flex",
686
+ flexDirection: "column",
687
+ gap: "4px",
688
+ }}
689
+ >
690
+ <span>No output transforms</span>
691
+ </div>
692
+ )}
693
+ </StateInspectorDisclosureRowInner>
694
+ </StateInspectorDisclosureSection>
695
+ )}
696
+ {advanced && (
697
+ <StateInspectorDisclosureSection
698
+ title="Tasks"
699
+ colorScheme={colorScheme}
700
+ open={showTasks}
701
+ setOpen={setShowTasks}
702
+ >
703
+ <StateInspectorDisclosureRowInner>
704
+ {tasks?.map((task) => (
705
+ <StateInspectorRow
706
+ key={task.id}
707
+ colorScheme={colorScheme}
708
+ style={{
709
+ backgroundColor:
710
+ task.status === "done"
711
+ ? "rgba(0,255,0,0.2)"
712
+ : task.status === "error"
713
+ ? "rgba(255,0,0,0.2)"
714
+ : undefined,
715
+ }}
716
+ >
717
+ <ObjectInspector
718
+ name={task.name}
719
+ data={task.payload}
720
+ theme={theme}
721
+ />
722
+ </StateInspectorRow>
723
+ ))}
724
+ </StateInspectorDisclosureRowInner>
725
+ </StateInspectorDisclosureSection>
726
+ )}
675
727
  <StateInspectorDisclosureSection
676
- open={showEphemeral}
677
- setOpen={setShowEphemeral}
678
- title="Ephemeral User Data"
728
+ open={showShared}
729
+ setOpen={setShowShared}
730
+ title="Shared Connection Data"
679
731
  colorScheme={colorScheme}
680
732
  >
681
733
  <StateInspectorDisclosureRowInner>
682
- {Object.entries(ephemeral).map(([key, value]) => (
734
+ {Object.entries(shared).map(([key, value]) => (
683
735
  <StateInspectorRow key={key} colorScheme={colorScheme}>
684
736
  <ObjectInspector
685
737
  name={key}
@@ -691,13 +743,24 @@ export const StateInspector = memoGeneric(function StateInspector<
691
743
  ))}
692
744
  </StateInspectorDisclosureRowInner>
693
745
  </StateInspectorDisclosureSection>
694
- <ActivityEventsSection
746
+ <ServerScriptLogsSection
695
747
  colorScheme={colorScheme}
696
- activityEventsContainerRef={activityEventsContainerRef}
697
- showEvents={showActivityEvents}
698
- setShowEvents={setShowActivityEvents}
699
- activityEventsManager={activityEventsManager}
748
+ showLogs={showServerLogs}
749
+ setShowLogs={setShowServerLogs}
750
+ logs={visibleServerLogs}
751
+ paused={serverLogsPaused}
752
+ onTogglePaused={toggleServerLogsPaused}
753
+ containerRef={serverLogsContainerRef}
700
754
  />
755
+ {advanced && (
756
+ <ActivityEventsSection
757
+ colorScheme={colorScheme}
758
+ activityEventsContainerRef={activityEventsContainerRef}
759
+ showEvents={showActivityEvents}
760
+ setShowEvents={setShowActivityEvents}
761
+ activityEventsManager={activityEventsManager}
762
+ />
763
+ )}
701
764
  <EventsSection<S>
702
765
  connectionEvents={connectionEvents}
703
766
  colorScheme={colorScheme}