@noya-app/noya-multiplayer-react 0.1.45 → 0.1.46
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 +11 -11
- package/CHANGELOG.md +10 -0
- package/dist/index.bundle.js +10 -10
- package/dist/index.d.mts +81 -86
- package/dist/index.d.ts +81 -86
- package/dist/index.js +371 -418
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +268 -312
- package/dist/index.mjs.map +1 -1
- package/package.json +4 -4
- package/src/NoyaStateContext.tsx +96 -95
- package/src/WebSocketConnection.ts +153 -0
- package/src/components/ErrorOverlay.tsx +3 -3
- package/src/hooks.ts +74 -191
- package/src/index.ts +2 -0
- package/src/inspector/StateInspector.tsx +83 -45
- package/src/inspector/useStateInspector.tsx +11 -55
- package/src/noyaApp.ts +36 -95
- package/src/globals.ts +0 -4
package/src/hooks.ts
CHANGED
|
@@ -3,24 +3,12 @@
|
|
|
3
3
|
import { GetAtPath } from "@noya-app/observable";
|
|
4
4
|
import {
|
|
5
5
|
ActionHandler,
|
|
6
|
-
AIManager,
|
|
7
|
-
AssetManager,
|
|
8
|
-
ConnectedUsersManager,
|
|
9
|
-
ConnectionEvent,
|
|
10
|
-
EphemeralUserDataManager,
|
|
11
|
-
MultiplayerStateManager,
|
|
12
|
-
MultiplayerStateManagerError,
|
|
13
6
|
MultiplayerStateManagerOptions,
|
|
14
|
-
|
|
15
|
-
StandardRPCRequestPayload,
|
|
16
|
-
StandardRPCResponsePayload,
|
|
7
|
+
NoyaManager,
|
|
17
8
|
StateManager,
|
|
18
9
|
StateManagerOptions,
|
|
19
10
|
stubSync,
|
|
20
11
|
SyncAdapter,
|
|
21
|
-
SyncAdapterOptions,
|
|
22
|
-
Task,
|
|
23
|
-
WorkflowManager,
|
|
24
12
|
} from "@noya-app/state-manager";
|
|
25
13
|
import {
|
|
26
14
|
createElement,
|
|
@@ -33,9 +21,7 @@ import {
|
|
|
33
21
|
useSyncExternalStore,
|
|
34
22
|
} from "react";
|
|
35
23
|
import { createRoot } from "react-dom/client";
|
|
36
|
-
import { SecretManager } from "../../noya-state-manager/src/SecretManager";
|
|
37
24
|
import { ErrorOverlay } from "./components/ErrorOverlay";
|
|
38
|
-
import { shouldTrackEvents$, shouldTrackTasks$ } from "./globals";
|
|
39
25
|
import { StateInspectorOptions } from "./inspector/StateInspector";
|
|
40
26
|
import { useStateInspector } from "./inspector/useStateInspector";
|
|
41
27
|
import { useObservable } from "./useObservable";
|
|
@@ -134,190 +120,103 @@ export function useManagedHistory<S, M = void>(
|
|
|
134
120
|
);
|
|
135
121
|
}
|
|
136
122
|
|
|
137
|
-
export function useSyncMultiplayerStateManager<S, M = void>(
|
|
138
|
-
multiplayerStateManager: MultiplayerStateManager<S, M>
|
|
139
|
-
) {
|
|
140
|
-
return useSyncExternalStore(
|
|
141
|
-
multiplayerStateManager.addListener,
|
|
142
|
-
multiplayerStateManager.getOptimisticState,
|
|
143
|
-
multiplayerStateManager.getOptimisticState
|
|
144
|
-
);
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
const defaultStubSync = stubSync();
|
|
148
|
-
|
|
149
123
|
export type UseMultiplayerStateOptions<
|
|
150
124
|
S,
|
|
151
|
-
M =
|
|
125
|
+
M extends object = object,
|
|
152
126
|
E extends object = object,
|
|
127
|
+
MenuT extends string = string,
|
|
153
128
|
> = MultiplayerStateManagerOptions<S, M> & {
|
|
154
|
-
sync?:
|
|
155
|
-
options: Pick<
|
|
156
|
-
SyncAdapterOptions<S, M, E>,
|
|
157
|
-
"ms" | "ephemeralUserDataManager" | "assetManager"
|
|
158
|
-
>
|
|
159
|
-
) => void;
|
|
160
|
-
trackConnectionEvents?: boolean;
|
|
161
|
-
trackTasks?: boolean;
|
|
129
|
+
sync?: SyncAdapter<S, M, E, MenuT>;
|
|
162
130
|
inspector?: boolean | StateInspectorOptions;
|
|
163
131
|
};
|
|
164
132
|
|
|
165
|
-
|
|
133
|
+
const defaultStubSync = stubSync<unknown, object, object, string>();
|
|
134
|
+
|
|
135
|
+
export function useMultiplayerState<
|
|
136
|
+
S,
|
|
137
|
+
M extends object = object,
|
|
138
|
+
E extends object = object,
|
|
139
|
+
MenuT extends string = string,
|
|
140
|
+
>(
|
|
166
141
|
initialState: S | (() => S),
|
|
167
|
-
options?: UseMultiplayerStateOptions<S, M, E>
|
|
142
|
+
options?: UseMultiplayerStateOptions<S, M, E, MenuT>
|
|
168
143
|
) {
|
|
169
|
-
const {
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
);
|
|
178
|
-
const [multiplayerStateManager] = useState(
|
|
179
|
-
() => new MultiplayerStateManager<S, M>(initialState, rest)
|
|
144
|
+
const {
|
|
145
|
+
sync = defaultStubSync as unknown as SyncAdapter<S, M, E, MenuT>,
|
|
146
|
+
inspector,
|
|
147
|
+
...rest
|
|
148
|
+
} = options ?? {};
|
|
149
|
+
|
|
150
|
+
const [noyaManager] = useState(
|
|
151
|
+
() => new NoyaManager<S, M, E, MenuT>(initialState, rest)
|
|
180
152
|
);
|
|
181
|
-
const [assetManager] = useState(() => new AssetManager());
|
|
182
|
-
const [aiManager] = useState(() => new AIManager(rpcManager));
|
|
183
|
-
const [workflowManager] = useState(() => new WorkflowManager(rpcManager));
|
|
184
|
-
const [secretManager] = useState(() => new SecretManager(rpcManager));
|
|
185
|
-
const [connectedUsersManager] = useState(() => new ConnectedUsersManager());
|
|
186
|
-
const syncRef = useRef(sync);
|
|
187
153
|
|
|
188
|
-
const
|
|
189
|
-
|
|
190
|
-
>();
|
|
191
|
-
const [ephemeralUserDataManager] = useState<EphemeralUserDataManager<E>>(
|
|
192
|
-
() => new EphemeralUserDataManager()
|
|
154
|
+
const state = useObservable(
|
|
155
|
+
noyaManager.multiplayerStateManager.optimisticState$
|
|
193
156
|
);
|
|
194
|
-
const
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
157
|
+
const assets = useObservable(noyaManager.assetManager.assets$);
|
|
158
|
+
|
|
159
|
+
type ExtrasType = {
|
|
160
|
+
noyaManager: typeof noyaManager;
|
|
161
|
+
taskManager: typeof noyaManager.taskManager;
|
|
162
|
+
connectionEventManager: typeof noyaManager.connectionEventManager;
|
|
163
|
+
connectedUsersManager: typeof noyaManager.connectedUsersManager;
|
|
164
|
+
ephemeralUserDataManager: typeof noyaManager.ephemeralUserDataManager;
|
|
165
|
+
multiplayerStateManager: typeof noyaManager.multiplayerStateManager;
|
|
166
|
+
assetManager: typeof noyaManager.assetManager;
|
|
167
|
+
aiManager: typeof noyaManager.aiManager;
|
|
168
|
+
rpcManager: typeof noyaManager.rpcManager;
|
|
169
|
+
workflowManager: typeof noyaManager.workflowManager;
|
|
170
|
+
secretManager: typeof noyaManager.secretManager;
|
|
171
|
+
menuItemsManager: typeof noyaManager.menuManager;
|
|
172
|
+
createAsset: typeof noyaManager.assetManager.create;
|
|
173
|
+
deleteAsset: typeof noyaManager.assetManager.delete;
|
|
174
|
+
assets: typeof assets;
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
const extras = useMemo<ExtrasType>(() => {
|
|
201
178
|
return {
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
179
|
+
noyaManager,
|
|
180
|
+
taskManager: noyaManager.taskManager,
|
|
181
|
+
connectionEventManager: noyaManager.connectionEventManager,
|
|
182
|
+
connectedUsersManager: noyaManager.connectedUsersManager,
|
|
183
|
+
ephemeralUserDataManager: noyaManager.ephemeralUserDataManager,
|
|
184
|
+
multiplayerStateManager: noyaManager.multiplayerStateManager,
|
|
185
|
+
assetManager: noyaManager.assetManager,
|
|
186
|
+
aiManager: noyaManager.aiManager,
|
|
187
|
+
rpcManager: noyaManager.rpcManager,
|
|
188
|
+
workflowManager: noyaManager.workflowManager,
|
|
189
|
+
secretManager: noyaManager.secretManager,
|
|
190
|
+
menuItemsManager: noyaManager.menuManager,
|
|
191
|
+
createAsset: noyaManager.assetManager.create,
|
|
192
|
+
deleteAsset: noyaManager.assetManager.delete,
|
|
207
193
|
assets,
|
|
208
|
-
assetManager,
|
|
209
|
-
aiManager,
|
|
210
|
-
rpcManager,
|
|
211
|
-
workflowManager,
|
|
212
|
-
secretManager,
|
|
213
|
-
createAsset: assetManager.create,
|
|
214
|
-
deleteAsset: assetManager.delete,
|
|
215
|
-
// connectionStatus,
|
|
216
|
-
// isConnected: connectionStatus === "connected",
|
|
217
194
|
};
|
|
218
|
-
}, [
|
|
219
|
-
tasks,
|
|
220
|
-
connectionEvents,
|
|
221
|
-
connectedUsersManager,
|
|
222
|
-
ephemeralUserDataManager,
|
|
223
|
-
multiplayerStateManager,
|
|
224
|
-
assets,
|
|
225
|
-
assetManager,
|
|
226
|
-
aiManager,
|
|
227
|
-
rpcManager,
|
|
228
|
-
workflowManager,
|
|
229
|
-
secretManager,
|
|
230
|
-
]);
|
|
231
|
-
|
|
232
|
-
const globalTrackEvents = useObservable(shouldTrackEvents$);
|
|
233
|
-
const globalTrackTasks = useObservable(shouldTrackTasks$);
|
|
234
|
-
|
|
235
|
-
const shouldTrackEvents = options?.trackConnectionEvents ?? globalTrackEvents;
|
|
236
|
-
const shouldTrackTasks = options?.trackTasks ?? globalTrackTasks;
|
|
195
|
+
}, [noyaManager, assets]);
|
|
237
196
|
|
|
238
|
-
const
|
|
239
|
-
((e: ConnectionEvent<S>) => void) | undefined
|
|
240
|
-
>();
|
|
241
|
-
const trackTasksCallbackRef = useRef<((tasks: Task[]) => void) | undefined>();
|
|
242
|
-
|
|
243
|
-
useEffect(() => {
|
|
244
|
-
trackEventsCallbackRef.current = shouldTrackEvents
|
|
245
|
-
? (e) =>
|
|
246
|
-
setConnectionEvents((events) => {
|
|
247
|
-
const updated = events ? [...events, e] : [e];
|
|
248
|
-
return updated.length > 50 ? updated.slice(-50) : updated;
|
|
249
|
-
})
|
|
250
|
-
: undefined;
|
|
251
|
-
}, [shouldTrackEvents]);
|
|
252
|
-
|
|
253
|
-
useEffect(() => {
|
|
254
|
-
trackTasksCallbackRef.current = shouldTrackTasks
|
|
255
|
-
? (tasks) => {
|
|
256
|
-
setTasks((old) => {
|
|
257
|
-
const newIds = new Set(tasks.map((t) => t.id));
|
|
258
|
-
|
|
259
|
-
// merge old and new tasks, preferring new tasks
|
|
260
|
-
const unchanged = old.filter((t) => !newIds.has(t.id));
|
|
261
|
-
|
|
262
|
-
return [...unchanged, ...tasks];
|
|
263
|
-
});
|
|
264
|
-
}
|
|
265
|
-
: undefined;
|
|
266
|
-
}, [shouldTrackTasks]);
|
|
197
|
+
const syncRef = useRef(sync);
|
|
267
198
|
|
|
268
199
|
useEffect(() => {
|
|
269
200
|
if (syncRef.current) {
|
|
270
|
-
return syncRef.current({
|
|
271
|
-
ms: multiplayerStateManager,
|
|
272
|
-
assetManager,
|
|
273
|
-
workflowManager,
|
|
274
|
-
secretManager,
|
|
275
|
-
rpcManager,
|
|
276
|
-
aiManager,
|
|
277
|
-
ephemeralUserDataManager,
|
|
278
|
-
connectedUsersManager,
|
|
279
|
-
onConnectionEvent: (e) => trackEventsCallbackRef.current?.(e),
|
|
280
|
-
onChangeTasks: (tasks) => trackTasksCallbackRef.current?.(tasks),
|
|
281
|
-
});
|
|
201
|
+
return syncRef.current({ noyaManager });
|
|
282
202
|
}
|
|
283
|
-
}, [
|
|
284
|
-
assetManager,
|
|
285
|
-
ephemeralUserDataManager,
|
|
286
|
-
multiplayerStateManager,
|
|
287
|
-
workflowManager,
|
|
288
|
-
secretManager,
|
|
289
|
-
rpcManager,
|
|
290
|
-
aiManager,
|
|
291
|
-
connectedUsersManager,
|
|
292
|
-
]);
|
|
203
|
+
}, [noyaManager]);
|
|
293
204
|
|
|
294
|
-
|
|
205
|
+
useErrorOverlay<MenuT>(noyaManager);
|
|
295
206
|
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
multiplayerStateManager.sendInit({ force: true, state, schema });
|
|
302
|
-
}, [initialState, multiplayerStateManager, options?.schema]);
|
|
207
|
+
useStateInspector<S, M, E, MenuT>({
|
|
208
|
+
noyaManager,
|
|
209
|
+
disabled: !inspector,
|
|
210
|
+
...(typeof inspector === "object" && inspector),
|
|
211
|
+
});
|
|
303
212
|
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
>();
|
|
213
|
+
return [state, noyaManager.multiplayerStateManager.setState, extras] as const;
|
|
214
|
+
}
|
|
307
215
|
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
case "schemaMismatch":
|
|
313
|
-
case "outdatedSchema":
|
|
314
|
-
case "dataMigrationFailure":
|
|
315
|
-
case "schemaMigration":
|
|
316
|
-
setUnrecoverableError(error);
|
|
317
|
-
break;
|
|
318
|
-
}
|
|
319
|
-
});
|
|
320
|
-
}, [multiplayerStateManager]);
|
|
216
|
+
function useErrorOverlay<MenuT extends string = string>(
|
|
217
|
+
noyaManager: NoyaManager<any, any, any, MenuT>
|
|
218
|
+
) {
|
|
219
|
+
const unrecoverableError = useObservable(noyaManager.unrecoverableError);
|
|
321
220
|
|
|
322
221
|
useEffect(() => {
|
|
323
222
|
if (!unrecoverableError) return;
|
|
@@ -331,20 +230,4 @@ export function useMultiplayerState<S, M = void, E extends object = object>(
|
|
|
331
230
|
el.remove();
|
|
332
231
|
};
|
|
333
232
|
});
|
|
334
|
-
|
|
335
|
-
useStateInspector({
|
|
336
|
-
ephemeralUserDataManager,
|
|
337
|
-
multiplayerStateManager,
|
|
338
|
-
assetManager,
|
|
339
|
-
secretManager,
|
|
340
|
-
state,
|
|
341
|
-
connectionEvents,
|
|
342
|
-
connectedUsersManager,
|
|
343
|
-
tasks,
|
|
344
|
-
forceInit,
|
|
345
|
-
disabled: !options?.inspector,
|
|
346
|
-
...(typeof options?.inspector === "object" && options.inspector),
|
|
347
|
-
});
|
|
348
|
-
|
|
349
|
-
return [state, multiplayerStateManager.setState, extras] as const;
|
|
350
233
|
}
|
package/src/index.ts
CHANGED
|
@@ -5,3 +5,5 @@ export * from "./inspector/StateInspector";
|
|
|
5
5
|
export * from "./noyaApp";
|
|
6
6
|
export * from "./NoyaStateContext";
|
|
7
7
|
export * from "./useObservable";
|
|
8
|
+
export { WebSocketConnection } from "./WebSocketConnection";
|
|
9
|
+
export type { ConnectionEvent, ConnectionOptions } from "./WebSocketConnection";
|
|
@@ -1,16 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
3
|
import { memoGeneric } from "@noya-app/react-utils";
|
|
4
|
-
import {
|
|
5
|
-
AssetManager,
|
|
6
|
-
ConnectedUsersManager,
|
|
7
|
-
ConnectionEvent,
|
|
8
|
-
EphemeralUserDataManager,
|
|
9
|
-
ExtendedPathKey,
|
|
10
|
-
MultiplayerStateManager,
|
|
11
|
-
SecretManager,
|
|
12
|
-
Task,
|
|
13
|
-
} from "@noya-app/state-manager";
|
|
4
|
+
import { ExtendedPathKey, NoyaManager } from "@noya-app/state-manager";
|
|
14
5
|
import React, {
|
|
15
6
|
CSSProperties,
|
|
16
7
|
ComponentPropsWithoutRef,
|
|
@@ -26,7 +17,6 @@ import {
|
|
|
26
17
|
chromeDark,
|
|
27
18
|
chromeLight,
|
|
28
19
|
} from "react-inspector";
|
|
29
|
-
import { shouldTrackEvents$, shouldTrackTasks$ } from "../globals";
|
|
30
20
|
import { useManagedHistory } from "../hooks";
|
|
31
21
|
import { useObservable } from "../useObservable";
|
|
32
22
|
import { useLocalStorageState } from "./useLocalStorageState";
|
|
@@ -134,7 +124,7 @@ function DisclosureSection({
|
|
|
134
124
|
}: {
|
|
135
125
|
open: boolean;
|
|
136
126
|
setOpen?: (value: boolean) => void;
|
|
137
|
-
title:
|
|
127
|
+
title: React.ReactNode;
|
|
138
128
|
right?: React.ReactNode;
|
|
139
129
|
children: React.ReactNode;
|
|
140
130
|
colorScheme: "light" | "dark";
|
|
@@ -254,36 +244,32 @@ export type StateInspectorOptions = {
|
|
|
254
244
|
|
|
255
245
|
export const StateInspector = memoGeneric(function StateInspector<
|
|
256
246
|
S,
|
|
257
|
-
M,
|
|
247
|
+
M extends object,
|
|
258
248
|
E extends object,
|
|
249
|
+
MenuT extends string = string,
|
|
259
250
|
>({
|
|
260
|
-
|
|
261
|
-
connectionEvents,
|
|
262
|
-
tasks,
|
|
263
|
-
unstyled,
|
|
251
|
+
noyaManager,
|
|
264
252
|
colorScheme = "light",
|
|
265
|
-
multiplayerStateManager,
|
|
266
|
-
assetManager,
|
|
267
|
-
secretManager,
|
|
268
|
-
connectedUsersManager,
|
|
269
|
-
ephemeralUserDataManager,
|
|
270
253
|
anchor = "right",
|
|
271
|
-
|
|
254
|
+
unstyled = false,
|
|
272
255
|
...props
|
|
273
256
|
}: {
|
|
274
|
-
|
|
275
|
-
connectionEvents?: ConnectionEvent<S>[];
|
|
276
|
-
tasks?: Task[];
|
|
257
|
+
noyaManager: NoyaManager<S, M, E, MenuT>;
|
|
277
258
|
unstyled?: boolean;
|
|
278
|
-
multiplayerStateManager: MultiplayerStateManager<S, M>;
|
|
279
|
-
assetManager: AssetManager;
|
|
280
|
-
connectedUsersManager: ConnectedUsersManager;
|
|
281
|
-
secretManager: SecretManager;
|
|
282
|
-
ephemeralUserDataManager: EphemeralUserDataManager<E>;
|
|
283
|
-
forceInit: () => void;
|
|
284
259
|
} & StateInspectorOptions &
|
|
285
260
|
ComponentPropsWithoutRef<"div">) {
|
|
261
|
+
const {
|
|
262
|
+
multiplayerStateManager,
|
|
263
|
+
assetManager,
|
|
264
|
+
connectedUsersManager,
|
|
265
|
+
secretManager,
|
|
266
|
+
ephemeralUserDataManager,
|
|
267
|
+
connectionEventManager,
|
|
268
|
+
taskManager,
|
|
269
|
+
} = noyaManager;
|
|
270
|
+
|
|
286
271
|
const [didMount, setDidMount] = React.useState(false);
|
|
272
|
+
const tasks = useObservable(taskManager.tasks$);
|
|
287
273
|
|
|
288
274
|
useLayoutEffect(() => {
|
|
289
275
|
setDidMount(true);
|
|
@@ -329,13 +315,7 @@ export const StateInspector = memoGeneric(function StateInspector<
|
|
|
329
315
|
false
|
|
330
316
|
);
|
|
331
317
|
|
|
332
|
-
|
|
333
|
-
shouldTrackEvents$.set(showEvents);
|
|
334
|
-
}, [showEvents]);
|
|
335
|
-
|
|
336
|
-
useEffect(() => {
|
|
337
|
-
shouldTrackTasks$.set(showTasks);
|
|
338
|
-
}, [showTasks]);
|
|
318
|
+
const connectionEvents = useObservable(connectionEventManager.events$);
|
|
339
319
|
|
|
340
320
|
useEffect(() => {
|
|
341
321
|
if (eventsContainerRef.current) {
|
|
@@ -344,12 +324,18 @@ export const StateInspector = memoGeneric(function StateInspector<
|
|
|
344
324
|
}
|
|
345
325
|
}, [connectionEvents]);
|
|
346
326
|
|
|
327
|
+
const multipeerStateInitialized = useObservable(
|
|
328
|
+
multiplayerStateManager.isInitialized$
|
|
329
|
+
);
|
|
347
330
|
const ephemeral = useObservable(ephemeralUserDataManager.data$);
|
|
348
331
|
const historySnapshot = useManagedHistory(multiplayerStateManager.sm);
|
|
349
332
|
const assets = useObservable(assetManager.assets$);
|
|
333
|
+
const assetsInitialized = useObservable(assetManager.isInitialized$);
|
|
350
334
|
const secrets = useObservable(secretManager.secrets$);
|
|
335
|
+
const secretsInitialized = useObservable(secretManager.isInitialized$);
|
|
351
336
|
const connectedUsers = useObservable(connectedUsersManager.connectedUsers$);
|
|
352
337
|
const userId = useObservable(connectedUsersManager.currentUserId$);
|
|
338
|
+
const state = useObservable(multiplayerStateManager.optimisticState$);
|
|
353
339
|
|
|
354
340
|
useEffect(() => {
|
|
355
341
|
if (historyContainerRef.current) {
|
|
@@ -496,7 +482,14 @@ export const StateInspector = memoGeneric(function StateInspector<
|
|
|
496
482
|
</div>
|
|
497
483
|
</DisclosureSection>
|
|
498
484
|
<DisclosureSection
|
|
499
|
-
title=
|
|
485
|
+
title={
|
|
486
|
+
<TitleLabel>
|
|
487
|
+
Data
|
|
488
|
+
<ColoredDot
|
|
489
|
+
type={multipeerStateInitialized ? "success" : "error"}
|
|
490
|
+
/>
|
|
491
|
+
</TitleLabel>
|
|
492
|
+
}
|
|
500
493
|
colorScheme={colorScheme}
|
|
501
494
|
setOpen={setShowData}
|
|
502
495
|
open={showData}
|
|
@@ -511,7 +504,7 @@ export const StateInspector = memoGeneric(function StateInspector<
|
|
|
511
504
|
// disabled={!historySnapshot.canUndo}
|
|
512
505
|
theme={theme}
|
|
513
506
|
onClick={() => {
|
|
514
|
-
forceInit();
|
|
507
|
+
noyaManager.forceInit();
|
|
515
508
|
}}
|
|
516
509
|
>
|
|
517
510
|
Reset
|
|
@@ -706,10 +699,27 @@ export const StateInspector = memoGeneric(function StateInspector<
|
|
|
706
699
|
<DisclosureSection
|
|
707
700
|
open={showAssets}
|
|
708
701
|
setOpen={setShowAssets}
|
|
709
|
-
title=
|
|
702
|
+
title={
|
|
703
|
+
<TitleLabel>
|
|
704
|
+
Assets
|
|
705
|
+
<ColoredDot type={assetsInitialized ? "success" : "error"} />
|
|
706
|
+
</TitleLabel>
|
|
707
|
+
}
|
|
710
708
|
colorScheme={colorScheme}
|
|
711
709
|
right={
|
|
712
|
-
<span style={{ display: "flex", gap: "
|
|
710
|
+
<span style={{ display: "flex", gap: "10px" }}>
|
|
711
|
+
<SmallButton
|
|
712
|
+
theme={theme}
|
|
713
|
+
onClick={() => {
|
|
714
|
+
console.info(
|
|
715
|
+
"[StateInspector] Reloading assets",
|
|
716
|
+
noyaManager.id
|
|
717
|
+
);
|
|
718
|
+
assetManager.fetch();
|
|
719
|
+
}}
|
|
720
|
+
>
|
|
721
|
+
Reload
|
|
722
|
+
</SmallButton>
|
|
713
723
|
<SmallButton
|
|
714
724
|
theme={theme}
|
|
715
725
|
onClick={() => {
|
|
@@ -779,7 +789,12 @@ export const StateInspector = memoGeneric(function StateInspector<
|
|
|
779
789
|
</div>
|
|
780
790
|
</DisclosureSection>
|
|
781
791
|
<DisclosureSection
|
|
782
|
-
title=
|
|
792
|
+
title={
|
|
793
|
+
<TitleLabel>
|
|
794
|
+
Secrets
|
|
795
|
+
<ColoredDot type={secretsInitialized ? "success" : "error"} />
|
|
796
|
+
</TitleLabel>
|
|
797
|
+
}
|
|
783
798
|
colorScheme={colorScheme}
|
|
784
799
|
open={showSecrets}
|
|
785
800
|
setOpen={setShowSecrets}
|
|
@@ -863,7 +878,6 @@ export const StateInspector = memoGeneric(function StateInspector<
|
|
|
863
878
|
name,
|
|
864
879
|
data,
|
|
865
880
|
isNonenumerable,
|
|
866
|
-
expanded,
|
|
867
881
|
}: any) => {
|
|
868
882
|
const direction = event.type === "send" ? "up" : "down";
|
|
869
883
|
|
|
@@ -1051,3 +1065,27 @@ function SmallButton({
|
|
|
1051
1065
|
</button>
|
|
1052
1066
|
);
|
|
1053
1067
|
}
|
|
1068
|
+
|
|
1069
|
+
function ColoredDot({ type }: { type: "success" | "error" }) {
|
|
1070
|
+
return (
|
|
1071
|
+
<span
|
|
1072
|
+
style={{
|
|
1073
|
+
display: "inline-block",
|
|
1074
|
+
width: 6,
|
|
1075
|
+
height: 6,
|
|
1076
|
+
background:
|
|
1077
|
+
type === "success" ? "rgba(0,200,0,0.8)" : "rgba(200,0,0,0.8)",
|
|
1078
|
+
borderRadius: "50%",
|
|
1079
|
+
verticalAlign: "middle",
|
|
1080
|
+
}}
|
|
1081
|
+
/>
|
|
1082
|
+
);
|
|
1083
|
+
}
|
|
1084
|
+
|
|
1085
|
+
function TitleLabel({ children }: { children: React.ReactNode }) {
|
|
1086
|
+
return (
|
|
1087
|
+
<span style={{ display: "flex", alignItems: "center", gap: "4px" }}>
|
|
1088
|
+
{children}
|
|
1089
|
+
</span>
|
|
1090
|
+
);
|
|
1091
|
+
}
|
|
@@ -1,14 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
AssetManager,
|
|
5
|
-
ConnectedUsersManager,
|
|
6
|
-
ConnectionEvent,
|
|
7
|
-
EphemeralUserDataManager,
|
|
8
|
-
MultiplayerStateManager,
|
|
9
|
-
SecretManager,
|
|
10
|
-
Task,
|
|
11
|
-
} from "@noya-app/state-manager";
|
|
3
|
+
import { NoyaManager } from "@noya-app/state-manager";
|
|
12
4
|
import React, { ComponentProps, useLayoutEffect } from "react";
|
|
13
5
|
import { createRoot } from "react-dom/client";
|
|
14
6
|
|
|
@@ -24,30 +16,19 @@ function createShadowRootElement() {
|
|
|
24
16
|
return { host, container };
|
|
25
17
|
}
|
|
26
18
|
|
|
27
|
-
export function useStateInspector<
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
19
|
+
export function useStateInspector<
|
|
20
|
+
S,
|
|
21
|
+
M extends object,
|
|
22
|
+
E extends object,
|
|
23
|
+
MenuT extends string = string,
|
|
24
|
+
>({
|
|
25
|
+
noyaManager,
|
|
31
26
|
disabled = false,
|
|
32
27
|
colorScheme,
|
|
33
28
|
anchor,
|
|
34
|
-
multiplayerStateManager,
|
|
35
|
-
assetManager,
|
|
36
|
-
secretManager,
|
|
37
|
-
connectedUsersManager,
|
|
38
|
-
ephemeralUserDataManager,
|
|
39
|
-
forceInit,
|
|
40
29
|
}: {
|
|
41
|
-
|
|
42
|
-
connectionEvents?: ConnectionEvent<S>[];
|
|
43
|
-
tasks?: Task[];
|
|
30
|
+
noyaManager: NoyaManager<S, M, E, MenuT>;
|
|
44
31
|
disabled: boolean;
|
|
45
|
-
multiplayerStateManager: MultiplayerStateManager<S, M>;
|
|
46
|
-
assetManager: AssetManager;
|
|
47
|
-
secretManager: SecretManager;
|
|
48
|
-
connectedUsersManager: ConnectedUsersManager;
|
|
49
|
-
ephemeralUserDataManager: EphemeralUserDataManager<E>;
|
|
50
|
-
forceInit: () => void;
|
|
51
32
|
} & Pick<ComponentProps<typeof StateInspector>, "colorScheme" | "anchor">) {
|
|
52
33
|
const [root, setRoot] = React.useState<Root | null>(null);
|
|
53
34
|
|
|
@@ -71,35 +52,10 @@ export function useStateInspector<S, M, E extends object>({
|
|
|
71
52
|
|
|
72
53
|
root.render(
|
|
73
54
|
<StateInspector
|
|
74
|
-
state={state}
|
|
75
|
-
connectionEvents={connectionEvents}
|
|
76
|
-
tasks={tasks}
|
|
77
55
|
colorScheme={colorScheme}
|
|
78
56
|
anchor={anchor}
|
|
79
|
-
|
|
80
|
-
multiplayerStateManager={
|
|
81
|
-
multiplayerStateManager as MultiplayerStateManager<any, any>
|
|
82
|
-
}
|
|
83
|
-
assetManager={assetManager}
|
|
84
|
-
secretManager={secretManager}
|
|
85
|
-
ephemeralUserDataManager={
|
|
86
|
-
ephemeralUserDataManager as EphemeralUserDataManager<any>
|
|
87
|
-
}
|
|
88
|
-
connectedUsersManager={connectedUsersManager}
|
|
57
|
+
noyaManager={noyaManager}
|
|
89
58
|
/>
|
|
90
59
|
);
|
|
91
|
-
}, [
|
|
92
|
-
anchor,
|
|
93
|
-
colorScheme,
|
|
94
|
-
connectionEvents,
|
|
95
|
-
root,
|
|
96
|
-
state,
|
|
97
|
-
multiplayerStateManager,
|
|
98
|
-
tasks,
|
|
99
|
-
forceInit,
|
|
100
|
-
ephemeralUserDataManager,
|
|
101
|
-
assetManager,
|
|
102
|
-
secretManager,
|
|
103
|
-
connectedUsersManager,
|
|
104
|
-
]);
|
|
60
|
+
}, [noyaManager, anchor, colorScheme, root]);
|
|
105
61
|
}
|