@noya-app/noya-multiplayer-react 0.1.12 → 0.1.14
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/.turbo/turbo-build.log +9 -9
- package/CHANGELOG.md +16 -0
- package/dist/index.d.mts +9 -5
- package/dist/index.d.ts +9 -5
- package/dist/index.js +228 -36
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +228 -36
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
- package/src/globals.ts +2 -1
- package/src/hooks.ts +60 -16
- package/src/inspector/StateInspector.tsx +185 -18
- package/src/inspector/useLocalStorageState.tsx +2 -0
- package/src/inspector/useStateInspector.tsx +18 -5
|
@@ -1,8 +1,11 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
1
3
|
import {
|
|
2
4
|
ConnectionEvent,
|
|
3
5
|
ExtendedPathKey,
|
|
6
|
+
MultiplayerStateManager,
|
|
4
7
|
MultiplayerUser,
|
|
5
|
-
|
|
8
|
+
Task,
|
|
6
9
|
} from "@noya-app/state-manager";
|
|
7
10
|
import React, {
|
|
8
11
|
CSSProperties,
|
|
@@ -20,7 +23,7 @@ import {
|
|
|
20
23
|
chromeDark,
|
|
21
24
|
chromeLight,
|
|
22
25
|
} from "react-inspector";
|
|
23
|
-
import {
|
|
26
|
+
import { shouldTrackEvents$, shouldTrackTasks$ } from "../globals";
|
|
24
27
|
import { useManagedHistory } from "../hooks";
|
|
25
28
|
import { useLocalStorageState } from "./useLocalStorageState";
|
|
26
29
|
|
|
@@ -170,7 +173,7 @@ function DisclosureSection({
|
|
|
170
173
|
}}
|
|
171
174
|
/>
|
|
172
175
|
)}
|
|
173
|
-
<span style={{ flex: "1 1 0" }}>{title}</span>
|
|
176
|
+
<span style={{ flex: "1 1 0", userSelect: "none" }}>{title}</span>
|
|
174
177
|
{right}
|
|
175
178
|
</div>
|
|
176
179
|
{open && children}
|
|
@@ -234,7 +237,7 @@ function InspectorRow({
|
|
|
234
237
|
|
|
235
238
|
const HISTORY_ELEMENT_PREFIX = "noya-multiplayer-history-";
|
|
236
239
|
|
|
237
|
-
type Anchor = "left" | "right";
|
|
240
|
+
type Anchor = "left" | "right" | "bottom left" | "bottom right";
|
|
238
241
|
|
|
239
242
|
export const StateInspector = memo(function StateInspector<
|
|
240
243
|
S extends object,
|
|
@@ -243,21 +246,25 @@ export const StateInspector = memo(function StateInspector<
|
|
|
243
246
|
state,
|
|
244
247
|
connectionEvents,
|
|
245
248
|
connectedUsers,
|
|
249
|
+
tasks,
|
|
246
250
|
userId,
|
|
247
251
|
unstyled,
|
|
248
252
|
colorScheme = "light",
|
|
249
|
-
|
|
253
|
+
multiplayerStateManager,
|
|
250
254
|
anchor = "right",
|
|
255
|
+
forceInit,
|
|
251
256
|
...props
|
|
252
257
|
}: {
|
|
253
258
|
state: S;
|
|
254
259
|
connectionEvents?: ConnectionEvent<S>[];
|
|
255
260
|
connectedUsers?: MultiplayerUser[];
|
|
261
|
+
tasks?: Task[];
|
|
256
262
|
userId?: string;
|
|
257
263
|
unstyled?: boolean;
|
|
258
264
|
colorScheme?: "light" | "dark";
|
|
259
|
-
|
|
265
|
+
multiplayerStateManager: MultiplayerStateManager<S, M>;
|
|
260
266
|
anchor?: Anchor;
|
|
267
|
+
forceInit: () => void;
|
|
261
268
|
} & ComponentPropsWithoutRef<"div">) {
|
|
262
269
|
const [didMount, setDidMount] = React.useState(false);
|
|
263
270
|
|
|
@@ -288,11 +295,19 @@ export const StateInspector = memo(function StateInspector<
|
|
|
288
295
|
"noya-multiplayer-react-show-data",
|
|
289
296
|
true
|
|
290
297
|
);
|
|
298
|
+
const [showTasks, setShowTasks] = useLocalStorageState(
|
|
299
|
+
"noya-multiplayer-react-show-tasks",
|
|
300
|
+
false
|
|
301
|
+
);
|
|
291
302
|
|
|
292
303
|
useEffect(() => {
|
|
293
|
-
|
|
304
|
+
shouldTrackEvents$.set(showEvents);
|
|
294
305
|
}, [showEvents]);
|
|
295
306
|
|
|
307
|
+
useEffect(() => {
|
|
308
|
+
shouldTrackTasks$.set(showTasks);
|
|
309
|
+
}, [showTasks]);
|
|
310
|
+
|
|
296
311
|
useEffect(() => {
|
|
297
312
|
if (eventsContainerRef.current) {
|
|
298
313
|
eventsContainerRef.current.scrollTop =
|
|
@@ -300,7 +315,7 @@ export const StateInspector = memo(function StateInspector<
|
|
|
300
315
|
}
|
|
301
316
|
}, [connectionEvents]);
|
|
302
317
|
|
|
303
|
-
const historySnapshot = useManagedHistory(
|
|
318
|
+
const historySnapshot = useManagedHistory(multiplayerStateManager.sm);
|
|
304
319
|
|
|
305
320
|
useEffect(() => {
|
|
306
321
|
if (historyContainerRef.current) {
|
|
@@ -334,7 +349,7 @@ export const StateInspector = memo(function StateInspector<
|
|
|
334
349
|
const baseStyle: CSSProperties = {
|
|
335
350
|
position: "fixed",
|
|
336
351
|
top: 12,
|
|
337
|
-
...(anchor
|
|
352
|
+
...(anchor.includes("right") ? { right: 12 } : { left: 12 }),
|
|
338
353
|
bottom: 12,
|
|
339
354
|
width: 400,
|
|
340
355
|
background:
|
|
@@ -361,8 +376,10 @@ export const StateInspector = memo(function StateInspector<
|
|
|
361
376
|
style={{
|
|
362
377
|
...baseStyle,
|
|
363
378
|
padding: "4px 10px",
|
|
364
|
-
bottom: undefined,
|
|
365
379
|
width: undefined,
|
|
380
|
+
...(anchor.includes("bottom")
|
|
381
|
+
? { bottom: 12, top: undefined }
|
|
382
|
+
: { bottom: undefined }),
|
|
366
383
|
}}
|
|
367
384
|
onClick={() => setShowInspector(true)}
|
|
368
385
|
>
|
|
@@ -433,15 +450,42 @@ export const StateInspector = memo(function StateInspector<
|
|
|
433
450
|
colorScheme={colorScheme}
|
|
434
451
|
setOpen={setShowData}
|
|
435
452
|
open={showData}
|
|
453
|
+
right={
|
|
454
|
+
<span
|
|
455
|
+
style={{
|
|
456
|
+
display: "flex",
|
|
457
|
+
gap: "4px",
|
|
458
|
+
}}
|
|
459
|
+
>
|
|
460
|
+
<SmallButton
|
|
461
|
+
// disabled={!historySnapshot.canUndo}
|
|
462
|
+
theme={theme}
|
|
463
|
+
onClick={() => {
|
|
464
|
+
forceInit();
|
|
465
|
+
}}
|
|
466
|
+
>
|
|
467
|
+
Reset
|
|
468
|
+
</SmallButton>
|
|
469
|
+
</span>
|
|
470
|
+
}
|
|
436
471
|
>
|
|
437
|
-
<div
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
472
|
+
<div style={styles.sectionInner}>
|
|
473
|
+
<InspectorRow colorScheme={colorScheme}>
|
|
474
|
+
<ObjectInspector
|
|
475
|
+
name={multiplayerStateManager.sm.schema ? "state" : undefined}
|
|
476
|
+
data={state}
|
|
477
|
+
theme={theme}
|
|
478
|
+
/>
|
|
479
|
+
</InspectorRow>
|
|
480
|
+
{multiplayerStateManager.sm.schema && (
|
|
481
|
+
<InspectorRow colorScheme={colorScheme}>
|
|
482
|
+
<ObjectInspector
|
|
483
|
+
name="schema"
|
|
484
|
+
data={multiplayerStateManager.sm.schema}
|
|
485
|
+
theme={theme}
|
|
486
|
+
/>
|
|
487
|
+
</InspectorRow>
|
|
488
|
+
)}
|
|
445
489
|
</div>
|
|
446
490
|
</DisclosureSection>
|
|
447
491
|
<DisclosureSection
|
|
@@ -449,6 +493,57 @@ export const StateInspector = memo(function StateInspector<
|
|
|
449
493
|
setOpen={setShowHistory}
|
|
450
494
|
title="History"
|
|
451
495
|
colorScheme={colorScheme}
|
|
496
|
+
right={
|
|
497
|
+
<span
|
|
498
|
+
style={{
|
|
499
|
+
display: "flex",
|
|
500
|
+
gap: "4px",
|
|
501
|
+
}}
|
|
502
|
+
>
|
|
503
|
+
<SmallButton
|
|
504
|
+
disabled={!historySnapshot.canUndo}
|
|
505
|
+
theme={theme}
|
|
506
|
+
onClick={() => {
|
|
507
|
+
multiplayerStateManager.sm.undo();
|
|
508
|
+
}}
|
|
509
|
+
>
|
|
510
|
+
<span style={{ width: "12px", height: "12px" }}>
|
|
511
|
+
<svg
|
|
512
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
513
|
+
width="1em"
|
|
514
|
+
height="1em"
|
|
515
|
+
viewBox="0 0 20 20"
|
|
516
|
+
>
|
|
517
|
+
<path
|
|
518
|
+
fill="currentColor"
|
|
519
|
+
d="M12 5H7V2L1 6l6 4V7h5c2.2 0 4 1.8 4 4s-1.8 4-4 4H7v2h5c3.3 0 6-2.7 6-6s-2.7-6-6-6"
|
|
520
|
+
/>
|
|
521
|
+
</svg>
|
|
522
|
+
</span>
|
|
523
|
+
</SmallButton>
|
|
524
|
+
<SmallButton
|
|
525
|
+
disabled={!historySnapshot.canRedo}
|
|
526
|
+
theme={theme}
|
|
527
|
+
onClick={() => {
|
|
528
|
+
multiplayerStateManager.sm.redo();
|
|
529
|
+
}}
|
|
530
|
+
>
|
|
531
|
+
<span style={{ width: "12px", height: "12px" }}>
|
|
532
|
+
<svg
|
|
533
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
534
|
+
width="1em"
|
|
535
|
+
height="1em"
|
|
536
|
+
viewBox="0 0 20 20"
|
|
537
|
+
>
|
|
538
|
+
<path
|
|
539
|
+
fill="currentColor"
|
|
540
|
+
d="M8 5h5V2l6 4l-6 4V7H8c-2.2 0-4 1.8-4 4s1.8 4 4 4h5v2H8c-3.3 0-6-2.7-6-6s2.7-6 6-6"
|
|
541
|
+
/>
|
|
542
|
+
</svg>
|
|
543
|
+
</span>
|
|
544
|
+
</SmallButton>
|
|
545
|
+
</span>
|
|
546
|
+
}
|
|
452
547
|
>
|
|
453
548
|
<div ref={historyContainerRef} style={styles.sectionInner}>
|
|
454
549
|
<div
|
|
@@ -533,6 +628,35 @@ export const StateInspector = memo(function StateInspector<
|
|
|
533
628
|
))}
|
|
534
629
|
</div>
|
|
535
630
|
</DisclosureSection>
|
|
631
|
+
<DisclosureSection
|
|
632
|
+
title="Tasks"
|
|
633
|
+
colorScheme={colorScheme}
|
|
634
|
+
open={showTasks}
|
|
635
|
+
setOpen={setShowTasks}
|
|
636
|
+
>
|
|
637
|
+
<div style={styles.sectionInner}>
|
|
638
|
+
{tasks?.map((task) => (
|
|
639
|
+
<InspectorRow
|
|
640
|
+
key={task.id}
|
|
641
|
+
colorScheme={colorScheme}
|
|
642
|
+
style={{
|
|
643
|
+
backgroundColor:
|
|
644
|
+
task.status === "done"
|
|
645
|
+
? "rgba(0,255,0,0.2)"
|
|
646
|
+
: task.status === "error"
|
|
647
|
+
? "rgba(255,0,0,0.2)"
|
|
648
|
+
: undefined,
|
|
649
|
+
}}
|
|
650
|
+
>
|
|
651
|
+
<ObjectInspector
|
|
652
|
+
name={task.name}
|
|
653
|
+
data={task.payload}
|
|
654
|
+
theme={theme}
|
|
655
|
+
/>
|
|
656
|
+
</InspectorRow>
|
|
657
|
+
))}
|
|
658
|
+
</div>
|
|
659
|
+
</DisclosureSection>
|
|
536
660
|
<DisclosureSection
|
|
537
661
|
open={showEvents}
|
|
538
662
|
setOpen={setShowEvents}
|
|
@@ -710,3 +834,46 @@ function ellipsis(
|
|
|
710
834
|
return `${str.slice(0, maxLength)}…`;
|
|
711
835
|
}
|
|
712
836
|
}
|
|
837
|
+
|
|
838
|
+
function SmallButton({
|
|
839
|
+
children,
|
|
840
|
+
onClick,
|
|
841
|
+
style,
|
|
842
|
+
theme,
|
|
843
|
+
disabled,
|
|
844
|
+
}: {
|
|
845
|
+
children: React.ReactNode;
|
|
846
|
+
onClick: () => void;
|
|
847
|
+
style?: CSSProperties;
|
|
848
|
+
theme: typeof chromeLight;
|
|
849
|
+
disabled?: boolean;
|
|
850
|
+
}) {
|
|
851
|
+
return (
|
|
852
|
+
<button
|
|
853
|
+
type="button"
|
|
854
|
+
disabled={disabled}
|
|
855
|
+
onClick={(event) => {
|
|
856
|
+
event.stopPropagation();
|
|
857
|
+
onClick();
|
|
858
|
+
}}
|
|
859
|
+
style={{
|
|
860
|
+
flex: "0",
|
|
861
|
+
appearance: "none",
|
|
862
|
+
background: "none",
|
|
863
|
+
color: theme.BASE_COLOR,
|
|
864
|
+
opacity: disabled ? 0.25 : 1,
|
|
865
|
+
border: "none",
|
|
866
|
+
fontSize: "12px",
|
|
867
|
+
whiteSpace: "nowrap",
|
|
868
|
+
display: "inline-flex",
|
|
869
|
+
alignItems: "center",
|
|
870
|
+
justifyContent: "center",
|
|
871
|
+
padding: "2px 0",
|
|
872
|
+
cursor: "pointer",
|
|
873
|
+
...style,
|
|
874
|
+
}}
|
|
875
|
+
>
|
|
876
|
+
{children}
|
|
877
|
+
</button>
|
|
878
|
+
);
|
|
879
|
+
}
|
|
@@ -1,7 +1,10 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
1
3
|
import {
|
|
2
4
|
ConnectionEvent,
|
|
5
|
+
MultiplayerStateManager,
|
|
3
6
|
MultiplayerUser,
|
|
4
|
-
|
|
7
|
+
Task,
|
|
5
8
|
} from "@noya-app/state-manager";
|
|
6
9
|
import React, { ComponentProps, useLayoutEffect } from "react";
|
|
7
10
|
import { createRoot } from "react-dom/client";
|
|
@@ -19,18 +22,22 @@ export function useStateInspector<S extends object, M>({
|
|
|
19
22
|
state,
|
|
20
23
|
connectionEvents,
|
|
21
24
|
connectedUsers,
|
|
25
|
+
tasks,
|
|
22
26
|
userId,
|
|
23
27
|
disabled = false,
|
|
24
28
|
colorScheme,
|
|
25
29
|
anchor,
|
|
26
|
-
|
|
30
|
+
multiplayerStateManager,
|
|
31
|
+
forceInit,
|
|
27
32
|
}: {
|
|
28
33
|
state: S;
|
|
29
34
|
connectionEvents?: ConnectionEvent<S>[];
|
|
30
35
|
connectedUsers?: MultiplayerUser[];
|
|
36
|
+
tasks?: Task[];
|
|
31
37
|
userId?: string;
|
|
32
38
|
disabled: boolean;
|
|
33
|
-
|
|
39
|
+
multiplayerStateManager: MultiplayerStateManager<S, M>;
|
|
40
|
+
forceInit: () => void;
|
|
34
41
|
} & Pick<ComponentProps<typeof StateInspector>, "colorScheme" | "anchor">) {
|
|
35
42
|
const [root, setRoot] = React.useState<Root | null>(null);
|
|
36
43
|
|
|
@@ -58,10 +65,14 @@ export function useStateInspector<S extends object, M>({
|
|
|
58
65
|
state={state}
|
|
59
66
|
connectionEvents={connectionEvents}
|
|
60
67
|
connectedUsers={connectedUsers}
|
|
68
|
+
tasks={tasks}
|
|
61
69
|
userId={userId}
|
|
62
70
|
colorScheme={colorScheme}
|
|
63
71
|
anchor={anchor}
|
|
64
|
-
|
|
72
|
+
forceInit={forceInit}
|
|
73
|
+
multiplayerStateManager={
|
|
74
|
+
multiplayerStateManager as MultiplayerStateManager<any, any>
|
|
75
|
+
}
|
|
65
76
|
/>
|
|
66
77
|
);
|
|
67
78
|
}, [
|
|
@@ -71,7 +82,9 @@ export function useStateInspector<S extends object, M>({
|
|
|
71
82
|
connectionEvents,
|
|
72
83
|
root,
|
|
73
84
|
state,
|
|
74
|
-
|
|
85
|
+
multiplayerStateManager,
|
|
75
86
|
userId,
|
|
87
|
+
tasks,
|
|
88
|
+
forceInit,
|
|
76
89
|
]);
|
|
77
90
|
}
|