@noya-app/noya-multiplayer-react 0.1.17 → 0.1.18

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.
@@ -5,14 +5,14 @@
5
5
  CLI Target: esnext
6
6
  CJS Build start
7
7
  ESM Build start
8
- ESM dist/index.mjs 29.75 KB
9
- ESM dist/index.mjs.map 53.84 KB
10
- ESM ⚡️ Build success in 50ms
11
- CJS dist/index.js 33.83 KB
12
- CJS dist/index.js.map 53.63 KB
13
- CJS ⚡️ Build success in 70ms
8
+ ESM dist/index.mjs 30.94 KB
9
+ ESM dist/index.mjs.map 56.01 KB
10
+ ESM ⚡️ Build success in 35ms
11
+ CJS dist/index.js 35.19 KB
12
+ CJS dist/index.js.map 55.79 KB
13
+ CJS ⚡️ Build success in 37ms
14
14
  DTS Build start
15
15
  "ComponentPropsWithoutRef" is imported from external module "react" but never used in "src/inspector/StateInspector.tsx" and "src/hooks.ts".
16
- DTS ⚡️ Build success in 2630ms
17
- DTS dist/index.d.ts 2.69 KB
18
- DTS dist/index.d.mts 2.69 KB
16
+ DTS ⚡️ Build success in 2094ms
17
+ DTS dist/index.d.ts 3.00 KB
18
+ DTS dist/index.d.mts 3.00 KB
package/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # @noya-app/state-manager
2
2
 
3
+ ## 0.1.18
4
+
5
+ ### Patch Changes
6
+
7
+ - 536ef31: Add ephemeral user data
8
+ - Updated dependencies [536ef31]
9
+ - @noya-app/state-manager@0.1.18
10
+
3
11
  ## 0.1.17
4
12
 
5
13
  ### Patch Changes
package/dist/index.d.mts CHANGED
@@ -1,10 +1,10 @@
1
1
  import * as _noya_app_state_manager from '@noya-app/state-manager';
2
- import { ConnectionEvent, MultiplayerUser, Task, MultiplayerStateManager, StateManager, Get, StateManagerOptions, MultiplayerStateManagerOptions } from '@noya-app/state-manager';
2
+ import { ConnectionEvent, MultiplayerUser, Task, MultiplayerStateManager, EphemeralUserData, StateManager, Get, StateManagerOptions, MultiplayerStateManagerOptions } from '@noya-app/state-manager';
3
3
  export * from '@noya-app/state-manager';
4
4
  import React, { SetStateAction, ComponentProps } from 'react';
5
5
 
6
6
  type Anchor = "left" | "right" | "bottom left" | "bottom right";
7
- declare const StateInspector: React.MemoExoticComponent<(<S extends object, M>({ state, connectionEvents, connectedUsers, tasks, userId, unstyled, colorScheme, multiplayerStateManager, anchor, forceInit, ...props }: {
7
+ declare const StateInspector: React.MemoExoticComponent<(<S extends object, M, E>({ state, connectionEvents, connectedUsers, tasks, userId, unstyled, colorScheme, multiplayerStateManager, ephemeralUserData, anchor, forceInit, ...props }: {
8
8
  state: S;
9
9
  connectionEvents?: ConnectionEvent<S>[] | undefined;
10
10
  connectedUsers?: MultiplayerUser[] | undefined;
@@ -13,6 +13,7 @@ declare const StateInspector: React.MemoExoticComponent<(<S extends object, M>({
13
13
  unstyled?: boolean | undefined;
14
14
  colorScheme?: "light" | "dark" | undefined;
15
15
  multiplayerStateManager: MultiplayerStateManager<S, M>;
16
+ ephemeralUserData: EphemeralUserData<E>;
16
17
  anchor?: Anchor | undefined;
17
18
  forceInit: () => void;
18
19
  } & Omit<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref">) => React.JSX.Element | null)>;
@@ -22,9 +23,11 @@ declare function useSyncStateManager<S extends object, M, P extends string>(stat
22
23
  declare function useManagedState<S extends object, M = void>(createInitialState: () => S, options?: StateManagerOptions<S, M>): readonly [S, (...args: M extends {} ? [metadata: M, action: SetStateAction<S>] : [action: SetStateAction<S>]) => void, StateManager<S, M>];
23
24
  declare function useManagedHistory<S extends object, M = void>(stateManager: StateManager<S, M>): _noya_app_state_manager.HistorySnapshot<S, M>;
24
25
  declare function useSyncMultiplayerStateManager<S extends object, M = void>(multiplayerStateManager: MultiplayerStateManager<S, M>): S;
25
- declare function useMultiplayerState<S extends object, M = void>(initialState: S | (() => S), options?: MultiplayerStateManagerOptions<S, M> & {
26
+ declare function useEphemeralUserData<E>(ephemeralUserData: EphemeralUserData<E>): Record<string, E>;
27
+ declare function useMultiplayerState<S extends object, M = void, E = void>(initialState: S | (() => S), options?: MultiplayerStateManagerOptions<S, M> & {
26
28
  sync?: (options: {
27
29
  ms: MultiplayerStateManager<S, M>;
30
+ ephemeralUserData: EphemeralUserData<E>;
28
31
  }) => void;
29
32
  trackConnectionEvents?: boolean;
30
33
  trackTasks?: boolean;
@@ -33,6 +36,7 @@ declare function useMultiplayerState<S extends object, M = void>(initialState: S
33
36
  tasks: Task[];
34
37
  connectionEvents: ConnectionEvent<S>[] | undefined;
35
38
  connectedUsers: MultiplayerUser[];
39
+ ephemeralUserData: EphemeralUserData<E>;
36
40
  }];
37
41
 
38
- export { StateInspector, useManagedHistory, useManagedState, useMultiplayerState, useSyncMultiplayerStateManager, useSyncStateManager };
42
+ export { StateInspector, useEphemeralUserData, useManagedHistory, useManagedState, useMultiplayerState, useSyncMultiplayerStateManager, useSyncStateManager };
package/dist/index.d.ts CHANGED
@@ -1,10 +1,10 @@
1
1
  import * as _noya_app_state_manager from '@noya-app/state-manager';
2
- import { ConnectionEvent, MultiplayerUser, Task, MultiplayerStateManager, StateManager, Get, StateManagerOptions, MultiplayerStateManagerOptions } from '@noya-app/state-manager';
2
+ import { ConnectionEvent, MultiplayerUser, Task, MultiplayerStateManager, EphemeralUserData, StateManager, Get, StateManagerOptions, MultiplayerStateManagerOptions } from '@noya-app/state-manager';
3
3
  export * from '@noya-app/state-manager';
4
4
  import React, { SetStateAction, ComponentProps } from 'react';
5
5
 
6
6
  type Anchor = "left" | "right" | "bottom left" | "bottom right";
7
- declare const StateInspector: React.MemoExoticComponent<(<S extends object, M>({ state, connectionEvents, connectedUsers, tasks, userId, unstyled, colorScheme, multiplayerStateManager, anchor, forceInit, ...props }: {
7
+ declare const StateInspector: React.MemoExoticComponent<(<S extends object, M, E>({ state, connectionEvents, connectedUsers, tasks, userId, unstyled, colorScheme, multiplayerStateManager, ephemeralUserData, anchor, forceInit, ...props }: {
8
8
  state: S;
9
9
  connectionEvents?: ConnectionEvent<S>[] | undefined;
10
10
  connectedUsers?: MultiplayerUser[] | undefined;
@@ -13,6 +13,7 @@ declare const StateInspector: React.MemoExoticComponent<(<S extends object, M>({
13
13
  unstyled?: boolean | undefined;
14
14
  colorScheme?: "light" | "dark" | undefined;
15
15
  multiplayerStateManager: MultiplayerStateManager<S, M>;
16
+ ephemeralUserData: EphemeralUserData<E>;
16
17
  anchor?: Anchor | undefined;
17
18
  forceInit: () => void;
18
19
  } & Omit<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref">) => React.JSX.Element | null)>;
@@ -22,9 +23,11 @@ declare function useSyncStateManager<S extends object, M, P extends string>(stat
22
23
  declare function useManagedState<S extends object, M = void>(createInitialState: () => S, options?: StateManagerOptions<S, M>): readonly [S, (...args: M extends {} ? [metadata: M, action: SetStateAction<S>] : [action: SetStateAction<S>]) => void, StateManager<S, M>];
23
24
  declare function useManagedHistory<S extends object, M = void>(stateManager: StateManager<S, M>): _noya_app_state_manager.HistorySnapshot<S, M>;
24
25
  declare function useSyncMultiplayerStateManager<S extends object, M = void>(multiplayerStateManager: MultiplayerStateManager<S, M>): S;
25
- declare function useMultiplayerState<S extends object, M = void>(initialState: S | (() => S), options?: MultiplayerStateManagerOptions<S, M> & {
26
+ declare function useEphemeralUserData<E>(ephemeralUserData: EphemeralUserData<E>): Record<string, E>;
27
+ declare function useMultiplayerState<S extends object, M = void, E = void>(initialState: S | (() => S), options?: MultiplayerStateManagerOptions<S, M> & {
26
28
  sync?: (options: {
27
29
  ms: MultiplayerStateManager<S, M>;
30
+ ephemeralUserData: EphemeralUserData<E>;
28
31
  }) => void;
29
32
  trackConnectionEvents?: boolean;
30
33
  trackTasks?: boolean;
@@ -33,6 +36,7 @@ declare function useMultiplayerState<S extends object, M = void>(initialState: S
33
36
  tasks: Task[];
34
37
  connectionEvents: ConnectionEvent<S>[] | undefined;
35
38
  connectedUsers: MultiplayerUser[];
39
+ ephemeralUserData: EphemeralUserData<E>;
36
40
  }];
37
41
 
38
- export { StateInspector, useManagedHistory, useManagedState, useMultiplayerState, useSyncMultiplayerStateManager, useSyncStateManager };
42
+ export { StateInspector, useEphemeralUserData, useManagedHistory, useManagedState, useMultiplayerState, useSyncMultiplayerStateManager, useSyncStateManager };
package/dist/index.js CHANGED
@@ -32,6 +32,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
32
32
  var src_exports = {};
33
33
  __export(src_exports, {
34
34
  StateInspector: () => StateInspector,
35
+ useEphemeralUserData: () => useEphemeralUserData,
35
36
  useManagedHistory: () => useManagedHistory,
36
37
  useManagedState: () => useManagedState,
37
38
  useMultiplayerState: () => useMultiplayerState,
@@ -298,6 +299,7 @@ var StateInspector = (0, import_react3.memo)(function StateInspector2({
298
299
  unstyled,
299
300
  colorScheme = "light",
300
301
  multiplayerStateManager,
302
+ ephemeralUserData,
301
303
  anchor = "right",
302
304
  forceInit,
303
305
  ...props
@@ -332,6 +334,10 @@ var StateInspector = (0, import_react3.memo)(function StateInspector2({
332
334
  "noya-multiplayer-react-show-tasks",
333
335
  false
334
336
  );
337
+ const [showEphemeral, setShowEphemeral] = useLocalStorageState(
338
+ "noya-multiplayer-react-show-ephemeral",
339
+ false
340
+ );
335
341
  (0, import_react3.useEffect)(() => {
336
342
  shouldTrackEvents$.set(showEvents);
337
343
  }, [showEvents]);
@@ -343,6 +349,7 @@ var StateInspector = (0, import_react3.memo)(function StateInspector2({
343
349
  eventsContainerRef.current.scrollTop = eventsContainerRef.current.scrollHeight;
344
350
  }
345
351
  }, [connectionEvents]);
352
+ const ephemeral = useEphemeralUserData(ephemeralUserData);
346
353
  const historySnapshot = useManagedHistory(multiplayerStateManager.sm);
347
354
  (0, import_react3.useEffect)(() => {
348
355
  if (historyContainerRef.current) {
@@ -658,6 +665,16 @@ var StateInspector = (0, import_react3.memo)(function StateInspector2({
658
665
  )
659
666
  )))
660
667
  ),
668
+ /* @__PURE__ */ import_react3.default.createElement(
669
+ DisclosureSection,
670
+ {
671
+ open: showEphemeral,
672
+ setOpen: setShowEphemeral,
673
+ title: "Ephemeral User Data",
674
+ colorScheme
675
+ },
676
+ /* @__PURE__ */ import_react3.default.createElement("div", { style: styles.sectionInner }, Object.entries(ephemeral).map(([key, value]) => /* @__PURE__ */ import_react3.default.createElement(InspectorRow, { key, colorScheme }, /* @__PURE__ */ import_react3.default.createElement(import_react_inspector.ObjectInspector, { name: key, data: value, theme }))))
677
+ ),
661
678
  /* @__PURE__ */ import_react3.default.createElement(
662
679
  DisclosureSection,
663
680
  {
@@ -841,6 +858,7 @@ function useStateInspector({
841
858
  colorScheme,
842
859
  anchor,
843
860
  multiplayerStateManager,
861
+ ephemeralUserData,
844
862
  forceInit
845
863
  }) {
846
864
  const [root, setRoot] = import_react4.default.useState(null);
@@ -870,7 +888,8 @@ function useStateInspector({
870
888
  colorScheme,
871
889
  anchor,
872
890
  forceInit,
873
- multiplayerStateManager
891
+ multiplayerStateManager,
892
+ ephemeralUserData
874
893
  }
875
894
  )
876
895
  );
@@ -884,7 +903,8 @@ function useStateInspector({
884
903
  multiplayerStateManager,
885
904
  userId,
886
905
  tasks,
887
- forceInit
906
+ forceInit,
907
+ ephemeralUserData
888
908
  ]);
889
909
  }
890
910
 
@@ -954,6 +974,13 @@ function useSyncMultiplayerStateManager(multiplayerStateManager) {
954
974
  multiplayerStateManager.getOptimisticState
955
975
  );
956
976
  }
977
+ function useEphemeralUserData(ephemeralUserData) {
978
+ return (0, import_react5.useSyncExternalStore)(
979
+ ephemeralUserData.addListener,
980
+ ephemeralUserData.getSnapshot,
981
+ ephemeralUserData.getSnapshot
982
+ );
983
+ }
957
984
  var defaultStubSync = (0, import_state_manager.stubSync)();
958
985
  function useMultiplayerState(initialState, options) {
959
986
  const { sync = defaultStubSync, ...rest } = options ?? {};
@@ -963,13 +990,17 @@ function useMultiplayerState(initialState, options) {
963
990
  const syncRef = (0, import_react5.useRef)(sync);
964
991
  const [connectionEvents, setConnectionEvents] = (0, import_react5.useState)();
965
992
  const [connectedUsers, setConnectedUsers] = (0, import_react5.useState)([]);
993
+ const [ephemeralUserData] = (0, import_react5.useState)(
994
+ () => new import_state_manager.EphemeralUserData()
995
+ );
966
996
  const [tasks, setTasks] = (0, import_react5.useState)([]);
967
997
  const [userId, setUserId] = (0, import_react5.useState)();
968
998
  const extras = (0, import_react5.useMemo)(() => {
969
999
  return {
970
1000
  tasks,
971
1001
  connectionEvents,
972
- connectedUsers
1002
+ connectedUsers,
1003
+ ephemeralUserData
973
1004
  // evaluate: async (code: string) => {
974
1005
  // const payload = await rpcManager.request({ type: "eval", code });
975
1006
  // if (payload.type !== "eval") {
@@ -977,7 +1008,7 @@ function useMultiplayerState(initialState, options) {
977
1008
  // }
978
1009
  // },
979
1010
  };
980
- }, [tasks, connectionEvents, connectedUsers]);
1011
+ }, [tasks, connectionEvents, connectedUsers, ephemeralUserData]);
981
1012
  const globalTrackEvents = useObservable(shouldTrackEvents$);
982
1013
  const globalTrackTasks = useObservable(shouldTrackTasks$);
983
1014
  const shouldTrackEvents = options?.trackConnectionEvents ?? globalTrackEvents;
@@ -1010,12 +1041,13 @@ function useMultiplayerState(initialState, options) {
1010
1041
  if (syncRef.current) {
1011
1042
  return syncRef.current({
1012
1043
  ms: multiplayerStateManager,
1044
+ ephemeralUserData,
1013
1045
  onConnectionEvent: (e) => trackEventsCallbackRef.current?.(e),
1014
1046
  onChangeConnectedUsers: (users, userId2) => trackConnectedUsersCallbackRef.current?.(users, userId2),
1015
1047
  onChangeTasks: (tasks2) => trackTasksCallbackRef.current?.(tasks2)
1016
1048
  });
1017
1049
  }
1018
- }, [multiplayerStateManager]);
1050
+ }, [ephemeralUserData, multiplayerStateManager]);
1019
1051
  const state = useSyncMultiplayerStateManager(multiplayerStateManager);
1020
1052
  const forceInit = (0, import_react5.useCallback)(() => {
1021
1053
  const state2 = typeof initialState === "function" ? initialState() : initialState;
@@ -1024,6 +1056,7 @@ function useMultiplayerState(initialState, options) {
1024
1056
  multiplayerStateManager.sendInit({ force: true, state: state2, schema });
1025
1057
  }, [initialState, multiplayerStateManager, options?.schema]);
1026
1058
  useStateInspector({
1059
+ ephemeralUserData,
1027
1060
  multiplayerStateManager,
1028
1061
  state,
1029
1062
  connectionEvents,
@@ -1044,6 +1077,7 @@ function useMultiplayerState(initialState, options) {
1044
1077
  // Annotate the CommonJS export names for ESM import in node:
1045
1078
  0 && (module.exports = {
1046
1079
  StateInspector,
1080
+ useEphemeralUserData,
1047
1081
  useManagedHistory,
1048
1082
  useManagedState,
1049
1083
  useMultiplayerState,
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/hooks.ts","../src/globals.ts","../src/inspector/useStateInspector.tsx","../src/inspector/StateInspector.tsx","../src/inspector/useLocalStorageState.tsx"],"sourcesContent":["export * from \"@noya-app/state-manager\";\nexport * from \"./hooks\";\nexport * from \"./inspector/StateInspector\";\n","\"use client\";\n\nimport {\n ActionHandler,\n ConnectionEvent,\n Get,\n MultiplayerStateManager,\n MultiplayerStateManagerOptions,\n MultiplayerUser,\n StateManager,\n StateManagerOptions,\n SyncAdapter,\n Task,\n stubSync,\n} from \"@noya-app/state-manager\";\nimport {\n ComponentProps,\n SetStateAction,\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n useSyncExternalStore,\n} from \"react\";\nimport {\n shouldTrackEvents$,\n shouldTrackTasks$,\n useObservable,\n} from \"./globals\";\nimport { StateInspector } from \"./inspector/StateInspector\";\nimport { useStateInspector } from \"./inspector/useStateInspector\";\n\nexport function useSyncStateManager<S extends object, M>(\n stateManager: StateManager<S, M>\n): S;\nexport function useSyncStateManager<S extends object, M, P extends string>(\n stateManager: StateManager<S, M>,\n path: P\n): Get<S, P, unknown>;\nexport function useSyncStateManager<S extends object, M>(\n stateManager: StateManager<S, M>,\n path?: string\n) {\n const getSnapshot = useCallback(\n () =>\n typeof path === \"string\"\n ? stateManager.getState(path)\n : stateManager.getState(),\n [stateManager, path]\n );\n\n return useSyncExternalStore(\n stateManager.addListener,\n getSnapshot,\n getSnapshot\n );\n}\n\nexport function useManagedState<S extends object, M = void>(\n createInitialState: () => S,\n options?: StateManagerOptions<S, M>\n) {\n const [stateManager] = useState(\n () => new StateManager<S, M>(createInitialState(), options)\n );\n\n const state = useSyncStateManager(stateManager);\n\n const setState = useCallback(\n (\n ...args: M extends {}\n ? [metadata: M, action: SetStateAction<S>]\n : [action: SetStateAction<S>]\n ) => {\n const [metadata, action] =\n args.length === 1 ? [undefined, args[0]] : args;\n\n if (metadata === undefined) {\n const performActionNoMetadata = stateManager.performAction as (\n handler: ActionHandler<S>\n ) => void;\n\n if (typeof action === \"function\") {\n performActionNoMetadata({\n run: action,\n undo: () => state,\n });\n } else {\n performActionNoMetadata({\n run: () => action,\n undo: () => state,\n });\n }\n } else {\n const performActionWithMetadata = stateManager.performAction as (\n metadata: M,\n handler: ActionHandler<S>\n ) => void;\n\n if (typeof action === \"function\") {\n performActionWithMetadata(metadata, {\n run: action,\n undo: () => state,\n });\n } else {\n performActionWithMetadata(metadata, {\n run: () => action,\n undo: () => state,\n });\n }\n }\n },\n [state, stateManager]\n );\n\n return [state, setState, stateManager] as const;\n}\n\nexport function useManagedHistory<S extends object, M = void>(\n stateManager: StateManager<S, M>\n) {\n return useSyncExternalStore(\n stateManager.historyEmittor.addListener,\n stateManager.getHistorySnapshot,\n stateManager.getHistorySnapshot\n );\n}\n\nexport function useSyncMultiplayerStateManager<S extends object, M = void>(\n multiplayerStateManager: MultiplayerStateManager<S, M>\n) {\n return useSyncExternalStore(\n multiplayerStateManager.addListener,\n multiplayerStateManager.getOptimisticState,\n multiplayerStateManager.getOptimisticState\n );\n}\n\nconst defaultStubSync = stubSync();\n\nexport function useMultiplayerState<S extends object, M = void>(\n initialState: S | (() => S),\n options?: MultiplayerStateManagerOptions<S, M> & {\n sync?: (options: { ms: MultiplayerStateManager<S, M> }) => void;\n trackConnectionEvents?: boolean;\n trackTasks?: boolean;\n inspector?:\n | boolean\n | Pick<ComponentProps<typeof StateInspector>, \"colorScheme\" | \"anchor\">;\n }\n) {\n const { sync = defaultStubSync as unknown as SyncAdapter<S, M>, ...rest } =\n options ?? {};\n\n const [multiplayerStateManager] = useState(\n () => new MultiplayerStateManager<S, M>(initialState, rest)\n );\n const syncRef = useRef(sync);\n\n const [connectionEvents, setConnectionEvents] = useState<\n ConnectionEvent<S>[] | undefined\n >();\n\n const [connectedUsers, setConnectedUsers] = useState<MultiplayerUser[]>([]);\n const [tasks, setTasks] = useState<Task[]>([]);\n const [userId, setUserId] = useState<string | undefined>();\n\n const extras = useMemo(() => {\n return {\n tasks,\n connectionEvents,\n connectedUsers,\n // evaluate: async (code: string) => {\n // const payload = await rpcManager.request({ type: \"eval\", code });\n\n // if (payload.type !== \"eval\") {\n // throw new Error(`Unexpected rpc response type: ${payload.type}`);\n // }\n // },\n };\n }, [tasks, connectionEvents, connectedUsers]);\n\n const globalTrackEvents = useObservable(shouldTrackEvents$);\n const globalTrackTasks = useObservable(shouldTrackTasks$);\n\n const shouldTrackEvents = options?.trackConnectionEvents ?? globalTrackEvents;\n const shouldTrackTasks = options?.trackTasks ?? globalTrackTasks;\n\n const trackEventsCallbackRef = useRef<\n ((e: ConnectionEvent<S>) => void) | undefined\n >();\n const trackConnectedUsersCallbackRef = useRef<\n ((users: MultiplayerUser[], userId: string) => void) | undefined\n >();\n const trackTasksCallbackRef = useRef<((tasks: Task[]) => void) | undefined>();\n\n useEffect(() => {\n trackEventsCallbackRef.current = shouldTrackEvents\n ? (e) =>\n setConnectionEvents((events) => {\n const updated = events ? [...events, e] : [e];\n return updated.length > 50 ? updated.slice(-50) : updated;\n })\n : undefined;\n }, [shouldTrackEvents]);\n\n useEffect(() => {\n trackConnectedUsersCallbackRef.current = (users, userId) => {\n setConnectedUsers(users);\n setUserId(userId);\n };\n }, []);\n\n useEffect(() => {\n trackTasksCallbackRef.current = shouldTrackTasks\n ? (tasks) => {\n setTasks((old) => {\n const newIds = new Set(tasks.map((t) => t.id));\n\n // merge old and new tasks, preferring new tasks\n const unchanged = old.filter((t) => !newIds.has(t.id));\n\n return [...unchanged, ...tasks];\n });\n }\n : undefined;\n }, [shouldTrackTasks]);\n\n useEffect(() => {\n if (syncRef.current) {\n return syncRef.current({\n ms: multiplayerStateManager,\n onConnectionEvent: (e) => trackEventsCallbackRef.current?.(e),\n onChangeConnectedUsers: (users, userId) =>\n trackConnectedUsersCallbackRef.current?.(users, userId),\n onChangeTasks: (tasks) => trackTasksCallbackRef.current?.(tasks),\n });\n }\n }, [multiplayerStateManager]);\n\n const state = useSyncMultiplayerStateManager(multiplayerStateManager);\n\n const forceInit = useCallback(() => {\n const state =\n typeof initialState === \"function\" ? initialState() : initialState;\n const schema = options?.schema;\n multiplayerStateManager.sm.clearHistory();\n multiplayerStateManager.sendInit({ force: true, state, schema });\n }, [initialState, multiplayerStateManager, options?.schema]);\n\n useStateInspector({\n multiplayerStateManager,\n state,\n connectionEvents,\n connectedUsers,\n tasks,\n userId,\n forceInit,\n disabled: !options?.inspector,\n ...(typeof options?.inspector === \"object\" && options.inspector),\n });\n\n return [\n state,\n multiplayerStateManager.setState,\n multiplayerStateManager,\n extras,\n ] as const;\n}\n","import { Emitter } from \"@noya-app/emitter\";\nimport { useEffect, useState } from \"react\";\n\nclass Observable<T> {\n private value: T;\n private emitter = new Emitter<[T]>();\n\n constructor(value: T) {\n this.value = value;\n }\n\n get() {\n return this.value;\n }\n\n set(value: T) {\n if (this.value === value) return;\n\n this.value = value;\n\n this.emitter.emit(value);\n }\n\n listen(callback: (value: T) => void) {\n callback(this.value);\n\n return this.emitter.addListener(callback);\n }\n}\n\nexport function useObservable<T>(observable: Observable<T>) {\n const [value, setValue] = useState(observable.get());\n\n useEffect(() => {\n return observable.listen(setValue);\n }, [observable]);\n\n return value;\n}\n\nexport const shouldTrackEvents$ = new Observable(false);\nexport const shouldTrackTasks$ = new Observable(false);\n","\"use client\";\n\nimport {\n ConnectionEvent,\n MultiplayerStateManager,\n MultiplayerUser,\n Task,\n} from \"@noya-app/state-manager\";\nimport React, { ComponentProps, useLayoutEffect } from \"react\";\nimport { createRoot } from \"react-dom/client\";\n\nimport { Root } from \"react-dom/client\";\nimport { StateInspector } from \"./StateInspector\";\n\nfunction createPortalElement() {\n const el = document.createElement(\"div\");\n el.id = \"noya-multiplayer-react-inspector-portal\";\n return el;\n}\n\nexport function useStateInspector<S extends object, M>({\n state,\n connectionEvents,\n connectedUsers,\n tasks,\n userId,\n disabled = false,\n colorScheme,\n anchor,\n multiplayerStateManager,\n forceInit,\n}: {\n state: S;\n connectionEvents?: ConnectionEvent<S>[];\n connectedUsers?: MultiplayerUser[];\n tasks?: Task[];\n userId?: string;\n disabled: boolean;\n multiplayerStateManager: MultiplayerStateManager<S, M>;\n forceInit: () => void;\n} & Pick<ComponentProps<typeof StateInspector>, \"colorScheme\" | \"anchor\">) {\n const [root, setRoot] = React.useState<Root | null>(null);\n\n useLayoutEffect(() => {\n if (disabled) return;\n\n const portalElement = createPortalElement();\n\n const root = createRoot(portalElement);\n\n document.body.appendChild(portalElement);\n\n setRoot(root);\n\n return () => {\n document.body.removeChild(portalElement);\n };\n }, [disabled]);\n\n useLayoutEffect(() => {\n if (!root) return;\n\n root.render(\n <StateInspector\n state={state}\n connectionEvents={connectionEvents}\n connectedUsers={connectedUsers}\n tasks={tasks}\n userId={userId}\n colorScheme={colorScheme}\n anchor={anchor}\n forceInit={forceInit}\n multiplayerStateManager={\n multiplayerStateManager as MultiplayerStateManager<any, any>\n }\n />\n );\n }, [\n anchor,\n colorScheme,\n connectedUsers,\n connectionEvents,\n root,\n state,\n multiplayerStateManager,\n userId,\n tasks,\n forceInit,\n ]);\n}\n","\"use client\";\n\nimport {\n ConnectionEvent,\n ExtendedPathKey,\n MultiplayerStateManager,\n MultiplayerUser,\n Task,\n} from \"@noya-app/state-manager\";\nimport React, {\n CSSProperties,\n ComponentPropsWithoutRef,\n FC,\n memo,\n useEffect,\n useLayoutEffect,\n} from \"react\";\nimport {\n ObjectInspector,\n ObjectLabel,\n ObjectName,\n ObjectPreview,\n chromeDark,\n chromeLight,\n} from \"react-inspector\";\nimport { shouldTrackEvents$, shouldTrackTasks$ } from \"../globals\";\nimport { useManagedHistory } from \"../hooks\";\nimport { useLocalStorageState } from \"./useLocalStorageState\";\n\nconst lightTheme = {\n ...chromeLight,\n BASE_BACKGROUND_COLOR: \"transparent\",\n OBJECT_NAME_COLOR: \"rgba(0,0,0,0.7)\",\n} as any;\n\nconst darkTheme = {\n ...chromeDark,\n BASE_BACKGROUND_COLOR: \"transparent\",\n OBJECT_NAME_COLOR: \"rgba(255,255,255,0.7)\",\n} as any;\n\nconst styles = {\n sectionInner: {\n flex: \"1 1 0\",\n overflowY: \"auto\",\n overflowX: \"hidden\",\n display: \"flex\",\n flexDirection: \"column\",\n },\n} as const;\n\nfunction ToggleButton({\n showInspector,\n setShowInspector,\n theme,\n anchor,\n}: {\n showInspector: boolean;\n setShowInspector: (value: boolean) => void;\n theme: typeof chromeLight;\n anchor: Anchor;\n}) {\n return (\n <span\n role=\"button\"\n style={{\n flex: \"0\",\n appearance: \"none\",\n color: theme.BASE_COLOR,\n border: \"none\",\n fontSize: \"12px\",\n whiteSpace: \"nowrap\",\n display: \"inline-flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n padding: \"2px 0\",\n }}\n onClick={(event) => {\n event.stopPropagation();\n\n setShowInspector(!showInspector);\n }}\n >\n <span style={{ width: \"12px\", height: \"12px\" }}>\n {showInspector === (anchor === \"right\") ? (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n strokeWidth={1.5}\n stroke=\"currentColor\"\n className=\"size-6\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n d=\"m5.25 4.5 7.5 7.5-7.5 7.5m6-15 7.5 7.5-7.5 7.5\"\n />\n </svg>\n ) : (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n strokeWidth={1.5}\n stroke=\"currentColor\"\n className=\"size-6\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n d=\"m18.75 4.5-7.5 7.5 7.5 7.5m-6-15L5.25 12l7.5 7.5\"\n />\n </svg>\n )}\n </span>\n </span>\n );\n}\n\nfunction DisclosureSection({\n open,\n setOpen,\n title,\n right,\n children,\n colorScheme,\n isFirst,\n style,\n}: {\n open: boolean;\n setOpen?: (value: boolean) => void;\n title: string;\n right?: React.ReactNode;\n children: React.ReactNode;\n colorScheme: \"light\" | \"dark\";\n isFirst?: boolean;\n style?: CSSProperties;\n}) {\n const theme = colorScheme === \"light\" ? lightTheme : darkTheme;\n\n const borderColor =\n colorScheme === \"light\" ? \"rgba(0,0,0,0.1)\" : \"rgba(255,255,255,0.1)\";\n\n return (\n <div\n style={{\n flex: open ? \"1 1 0\" : \"0\",\n display: \"flex\",\n flexDirection: \"column\",\n ...style,\n }}\n >\n <div\n onClick={() => setOpen?.(!open)}\n style={{\n cursor: \"default\",\n fontSize: \"12px\",\n padding: \"4px 10px\",\n display: \"flex\",\n alignItems: \"center\",\n ...(!isFirst && { borderTop: `1px solid ${borderColor}` }),\n ...(open && { borderBottom: `1px solid ${borderColor}` }),\n }}\n >\n {setOpen && (\n <Arrow\n expanded={open}\n style={{\n fontSize: theme.ARROW_FONT_SIZE,\n marginRight: theme.ARROW_MARGIN_RIGHT + 1,\n color: theme.ARROW_COLOR,\n }}\n />\n )}\n <span style={{ flex: \"1 1 0\", userSelect: \"none\" }}>{title}</span>\n {right}\n </div>\n {open && children}\n </div>\n );\n}\n\nfunction InspectorRow({\n children,\n colorScheme,\n selected,\n style,\n variant,\n}: {\n children: React.ReactNode;\n colorScheme: \"light\" | \"dark\";\n selected?: boolean;\n style?: CSSProperties;\n variant?: \"up\" | \"down\";\n}) {\n const solidBorderColor =\n colorScheme === \"light\" ? \"rgb(223 223 223)\" : \"rgb(29 29 29)\";\n\n return (\n <div\n style={{\n borderBottom: `1px solid ${solidBorderColor}`,\n fontSize: \"12px\",\n fontFamily: \"Menlo, monospace\",\n padding: \"2px 12px 1px\",\n display: \"flex\",\n alignItems: \"center\",\n background:\n variant === \"up\"\n ? \"rgba(0,255,0,0.1)\"\n : variant === \"down\"\n ? \"rgba(255,0,0,0.1)\"\n : selected\n ? \"rgb(59 130 246 / 15%)\"\n : undefined,\n // background:\n // colorScheme === \"light\"\n // ? \"rgba(0,0,0,0.05)\"\n // : \"rgba(255,255,255,0.05)\",\n ...style,\n }}\n >\n <span\n style={{\n fontFamily: \"Menlo, monospace\",\n fontSize: \"11px\",\n borderRadius: 4,\n display: \"inline-block\",\n }}\n >\n {children}\n </span>\n </div>\n );\n}\n\nconst HISTORY_ELEMENT_PREFIX = \"noya-multiplayer-history-\";\n\ntype Anchor = \"left\" | \"right\" | \"bottom left\" | \"bottom right\";\n\nexport const StateInspector = memo(function StateInspector<\n S extends object,\n M,\n>({\n state,\n connectionEvents,\n connectedUsers,\n tasks,\n userId,\n unstyled,\n colorScheme = \"light\",\n multiplayerStateManager,\n anchor = \"right\",\n forceInit,\n ...props\n}: {\n state: S;\n connectionEvents?: ConnectionEvent<S>[];\n connectedUsers?: MultiplayerUser[];\n tasks?: Task[];\n userId?: string;\n unstyled?: boolean;\n colorScheme?: \"light\" | \"dark\";\n multiplayerStateManager: MultiplayerStateManager<S, M>;\n anchor?: Anchor;\n forceInit: () => void;\n} & ComponentPropsWithoutRef<\"div\">) {\n const [didMount, setDidMount] = React.useState(false);\n\n useLayoutEffect(() => {\n setDidMount(true);\n }, []);\n\n const eventsContainerRef = React.useRef<HTMLDivElement>(null);\n const historyContainerRef = React.useRef<HTMLDivElement>(null);\n\n const [showInspector, setShowInspector] = useLocalStorageState(\n \"noya-multiplayer-react-show-inspector\",\n true\n );\n const [showEvents, setShowEvents] = useLocalStorageState(\n \"noya-multiplayer-react-show-events\",\n false\n );\n const [showHistory, setShowHistory] = useLocalStorageState(\n \"noya-multiplayer-react-show-history\",\n false\n );\n const [showUsers, setShowUsers] = useLocalStorageState(\n \"noya-multiplayer-react-show-users\",\n true\n );\n const [showData, setShowData] = useLocalStorageState(\n \"noya-multiplayer-react-show-data\",\n true\n );\n const [showTasks, setShowTasks] = useLocalStorageState(\n \"noya-multiplayer-react-show-tasks\",\n false\n );\n\n useEffect(() => {\n shouldTrackEvents$.set(showEvents);\n }, [showEvents]);\n\n useEffect(() => {\n shouldTrackTasks$.set(showTasks);\n }, [showTasks]);\n\n useEffect(() => {\n if (eventsContainerRef.current) {\n eventsContainerRef.current.scrollTop =\n eventsContainerRef.current.scrollHeight;\n }\n }, [connectionEvents]);\n\n const historySnapshot = useManagedHistory(multiplayerStateManager.sm);\n\n useEffect(() => {\n if (historyContainerRef.current) {\n historyContainerRef.current.scrollTop =\n historyContainerRef.current.scrollHeight;\n }\n }, [historySnapshot]);\n\n // If history index changes, scroll to the new history entry into view\n useEffect(() => {\n if (!historyContainerRef.current) return;\n\n const historyEntry = historyContainerRef.current.querySelector(\n `#${HISTORY_ELEMENT_PREFIX}${historySnapshot.historyIndex}`\n );\n\n if (historyEntry) {\n historyEntry.scrollIntoView({\n block: \"nearest\",\n inline: \"nearest\",\n });\n }\n }, [historySnapshot.historyIndex]);\n\n const theme = colorScheme === \"light\" ? lightTheme : darkTheme;\n // const borderColor =\n // colorScheme === \"light\" ? \"rgba(0,0,0,0.1)\" : \"rgba(255,255,255,0.1)\";\n const solidBorderColor =\n colorScheme === \"light\" ? \"rgb(223 223 223)\" : \"rgb(29 29 29)\";\n\n const baseStyle: CSSProperties = {\n position: \"fixed\",\n top: 12,\n ...(anchor.includes(\"right\") ? { right: 12 } : { left: 12 }),\n bottom: 12,\n width: 400,\n background:\n colorScheme === \"light\" ? \"rgba(255,255,255,0.95)\" : \"rgba(0,0,0,0.95)\",\n backdropFilter: \"blur(48px)\",\n border: `1px solid ${solidBorderColor}`,\n // outlineOffset: -1,\n borderRadius: 4,\n display: \"flex\",\n flexDirection: \"column\",\n // gap: 12,\n color: theme.BASE_COLOR,\n overflow: \"hidden\",\n zIndex: 9999,\n lineHeight: \"13px\",\n };\n\n if (!didMount) return null;\n\n if (!showInspector) {\n return (\n <div\n {...props}\n style={{\n ...baseStyle,\n padding: \"4px 10px\",\n width: undefined,\n ...(anchor.includes(\"bottom\")\n ? { bottom: 12, top: undefined }\n : { bottom: undefined }),\n }}\n onClick={() => setShowInspector(true)}\n >\n <ToggleButton\n showInspector={showInspector}\n setShowInspector={setShowInspector}\n theme={theme}\n anchor={anchor}\n />\n </div>\n );\n }\n\n return (\n <div\n {...props}\n style={{\n ...(!unstyled && baseStyle),\n ...props.style,\n }}\n >\n <DisclosureSection\n isFirst\n open={showUsers}\n setOpen={setShowUsers}\n title=\"Users\"\n colorScheme={colorScheme}\n style={{\n flex: \"0 0 auto\",\n maxHeight: \"200px\",\n }}\n right={\n <ToggleButton\n showInspector={showInspector}\n setShowInspector={setShowInspector}\n theme={theme}\n anchor={anchor}\n />\n }\n >\n <div style={{ ...styles.sectionInner, flex: \"0 0 auto\" }}>\n {connectedUsers?.map((user) => (\n <InspectorRow\n key={user.id}\n colorScheme={colorScheme}\n variant={user.id === userId ? \"up\" : undefined}\n >\n {user.name} ({ellipsis(user.id.toString(), 8, \"middle\")})\n </InspectorRow>\n ))}\n {!connectedUsers && (\n <div\n style={{\n padding: \"12px\",\n fontSize: \"12px\",\n display: \"flex\",\n flexDirection: \"column\",\n gap: \"4px\",\n }}\n >\n <span>No connected users</span>\n </div>\n )}\n </div>\n </DisclosureSection>\n <DisclosureSection\n title=\"Data\"\n colorScheme={colorScheme}\n setOpen={setShowData}\n open={showData}\n right={\n <span\n style={{\n display: \"flex\",\n gap: \"4px\",\n }}\n >\n <SmallButton\n // disabled={!historySnapshot.canUndo}\n theme={theme}\n onClick={() => {\n forceInit();\n }}\n >\n Reset\n </SmallButton>\n </span>\n }\n >\n <div style={styles.sectionInner}>\n <InspectorRow colorScheme={colorScheme}>\n <ObjectInspector\n name={multiplayerStateManager.schema ? \"state\" : undefined}\n data={state}\n theme={theme}\n />\n </InspectorRow>\n {multiplayerStateManager.schema && (\n <InspectorRow colorScheme={colorScheme}>\n <ObjectInspector\n name=\"schema\"\n data={multiplayerStateManager.schema}\n theme={theme}\n />\n </InspectorRow>\n )}\n </div>\n </DisclosureSection>\n <DisclosureSection\n open={showHistory}\n setOpen={setShowHistory}\n title=\"History\"\n colorScheme={colorScheme}\n right={\n <span\n style={{\n display: \"flex\",\n gap: \"4px\",\n }}\n >\n <SmallButton\n disabled={!historySnapshot.canUndo}\n theme={theme}\n onClick={() => {\n multiplayerStateManager.undo();\n }}\n >\n <span style={{ width: \"12px\", height: \"12px\" }}>\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"1em\"\n height=\"1em\"\n viewBox=\"0 0 20 20\"\n >\n <path\n fill=\"currentColor\"\n 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\"\n />\n </svg>\n </span>\n </SmallButton>\n <SmallButton\n disabled={!historySnapshot.canRedo}\n theme={theme}\n onClick={() => {\n multiplayerStateManager.redo();\n }}\n >\n <span style={{ width: \"12px\", height: \"12px\" }}>\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"1em\"\n height=\"1em\"\n viewBox=\"0 0 20 20\"\n >\n <path\n fill=\"currentColor\"\n 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\"\n />\n </svg>\n </span>\n </SmallButton>\n </span>\n }\n >\n <div ref={historyContainerRef} style={styles.sectionInner}>\n <div\n id={`${HISTORY_ELEMENT_PREFIX}0`}\n style={{\n borderBottom: `1px solid ${solidBorderColor}`,\n fontSize: \"12px\",\n fontFamily: \"Menlo, monospace\",\n padding: \"2px 12px 1px\",\n display: \"flex\",\n alignItems: \"center\",\n background:\n historySnapshot.historyIndex === 0\n ? \"rgb(59 130 246 / 15%)\"\n : undefined,\n }}\n >\n <span\n style={{\n fontFamily: \"Menlo, monospace\",\n fontSize: \"11px\",\n borderRadius: 4,\n display: \"inline-block\",\n }}\n >\n Initial state\n </span>\n </div>\n {historySnapshot.history.map((entry, index) => (\n <div\n id={`${HISTORY_ELEMENT_PREFIX}${index + 1}`}\n key={index}\n style={{\n padding: \"0px 12px 1px\",\n borderBottom: `1px solid ${solidBorderColor}`,\n background:\n index + 1 === historySnapshot.historyIndex\n ? \"rgb(59 130 246 / 15%)\"\n : undefined,\n }}\n >\n {entry.redoPatches?.map((patch, j) => (\n <pre\n key={j}\n style={{\n fontSize: \"11px\",\n display: \"flex\",\n flexWrap: \"wrap\",\n }}\n >\n {patch.op === \"add\" && (\n <>\n <span style={{ fontStyle: \"italic\" }}>\n {pathToString(patch.path)}\n </span>\n {\" = \"}\n <ObjectInspector data={patch.value} theme={theme} />\n </>\n )}\n {patch.op === \"replace\" && (\n <>\n <span style={{ fontStyle: \"italic\" }}>\n {pathToString(patch.path)}\n </span>\n {\" = \"}\n <ObjectInspector data={patch.value} theme={theme} />\n </>\n )}\n {patch.op === \"remove\" && (\n <>\n <span style={{ color: theme.OBJECT_VALUE_STRING_COLOR }}>\n delete{\" \"}\n </span>\n <span style={{ fontStyle: \"italic\" }}>\n {pathToString(patch.path)}\n </span>\n </>\n )}\n {patch.op === \"move\" && (\n <>\n <span style={{ fontStyle: \"italic\" }}>\n {pathToString(patch.from!)}\n </span>\n {\" -> \"}\n <span style={{ fontStyle: \"italic\" }}>\n {pathToString(patch.path)}\n </span>\n </>\n )}\n </pre>\n ))}\n </div>\n ))}\n </div>\n </DisclosureSection>\n <DisclosureSection\n title=\"Tasks\"\n colorScheme={colorScheme}\n open={showTasks}\n setOpen={setShowTasks}\n >\n <div style={styles.sectionInner}>\n {tasks?.map((task) => (\n <InspectorRow\n key={task.id}\n colorScheme={colorScheme}\n style={{\n backgroundColor:\n task.status === \"done\"\n ? \"rgba(0,255,0,0.2)\"\n : task.status === \"error\"\n ? \"rgba(255,0,0,0.2)\"\n : undefined,\n }}\n >\n <ObjectInspector\n name={task.name}\n data={task.payload}\n theme={theme}\n />\n </InspectorRow>\n ))}\n </div>\n </DisclosureSection>\n <DisclosureSection\n open={showEvents}\n setOpen={setShowEvents}\n title=\"Events\"\n colorScheme={colorScheme}\n >\n <div ref={eventsContainerRef} style={styles.sectionInner}>\n {connectionEvents?.map((event, index) =>\n event.type === \"stateChange\" ? (\n <InspectorRow key={index} colorScheme={colorScheme}>\n connection:{\" \"}\n <span style={{ fontStyle: \"italic\" }}>\n {event.state.toLowerCase()}\n </span>\n </InspectorRow>\n ) : (\n <InspectorRow\n key={index}\n colorScheme={colorScheme}\n variant={event.type === \"send\" ? \"up\" : \"down\"}\n style={{\n padding: \"0px 12px 1px\",\n }}\n >\n <ObjectInspector\n data={event.message}\n theme={theme}\n nodeRenderer={({\n depth,\n name,\n data,\n isNonenumerable,\n expanded,\n }: any) => {\n const direction = event.type === \"send\" ? \"up\" : \"down\";\n\n return depth === 0 ? (\n <ObjectRootLabel direction={direction} data={data} />\n ) : (\n <ObjectLabel\n direction={direction}\n name={name}\n data={data}\n isNonenumerable={isNonenumerable}\n />\n );\n }}\n />\n </InspectorRow>\n )\n )}\n {!connectionEvents && (\n <div\n style={{\n padding: \"12px\",\n fontSize: \"12px\",\n display: \"flex\",\n flexDirection: \"column\",\n gap: \"4px\",\n }}\n >\n <span>No recorded events</span>\n </div>\n )}\n </div>\n </DisclosureSection>\n </div>\n );\n});\n\ntype MessageDirection = \"up\" | \"down\";\n\nconst ObjectRootLabel: FC<{\n name?: string;\n data: any;\n direction?: MessageDirection;\n}> = ({ name, data, direction }) => {\n if (typeof name === \"string\") {\n return (\n <span>\n <ObjectName name={name} />\n <span>: </span>\n <ObjectPreview data={data} />\n </span>\n );\n }\n if (direction === \"up\" || direction === \"down\") {\n const arrow = direction === \"up\" ? \"↑\" : \"↓\";\n\n return (\n <span>\n <span\n style={{\n background:\n direction === \"up\" ? \"rgba(0,255,0,0.2)\" : \"rgba(255,0,0,0.2)\",\n // color: \"white\",\n width: 12,\n height: 12,\n borderRadius: 2,\n display: \"inline-flex\",\n justifyContent: \"center\",\n alignItems: \"center\",\n marginRight: 4,\n lineHeight: \"12px\",\n }}\n >\n {arrow}\n </span>\n <ObjectPreview data={data} />\n </span>\n );\n } else {\n return <ObjectPreview data={data} />;\n }\n};\n\n// Rotates ▶️ or ▼ based on expanded state\nfunction Arrow({\n expanded,\n onClick,\n style,\n}: {\n expanded: boolean;\n onClick?: () => void;\n style?: CSSProperties;\n}) {\n return (\n <span\n role=\"button\"\n onClick={onClick}\n style={{\n display: \"inline-block\",\n textAlign: \"center\",\n transform: expanded ? \"rotate(0deg)\" : \"rotate(-90deg)\",\n fontFamily: \"Menlo, monospace\",\n ...style,\n }}\n >\n {\"▼\"}\n </span>\n );\n}\n\nfunction pathToString(extendedPath: ExtendedPathKey[]) {\n return extendedPath\n .map((key) =>\n typeof key === \"object\"\n ? `:${ellipsis(key.id.toString(), 8, \"middle\")}`\n : key\n )\n .map((key, index) =>\n index === 0 || (typeof key === \"string\" && key.startsWith(\":\"))\n ? key\n : `.${key}`\n )\n .join(\"\");\n}\n\ntype EllipsisPosition = \"start\" | \"middle\" | \"end\";\n\nfunction ellipsis(\n str: string,\n maxLength: number,\n position: EllipsisPosition = \"end\"\n) {\n if (str.length <= maxLength) return str;\n\n switch (position) {\n case \"start\":\n return `…${str.slice(str.length - maxLength)}`;\n case \"middle\":\n const halfLength = Math.floor(maxLength / 2);\n return `${str.slice(0, halfLength)}…${str.slice(str.length - halfLength)}`;\n case \"end\":\n return `${str.slice(0, maxLength)}…`;\n }\n}\n\nfunction SmallButton({\n children,\n onClick,\n style,\n theme,\n disabled,\n}: {\n children: React.ReactNode;\n onClick: () => void;\n style?: CSSProperties;\n theme: typeof chromeLight;\n disabled?: boolean;\n}) {\n return (\n <button\n type=\"button\"\n disabled={disabled}\n onClick={(event) => {\n event.stopPropagation();\n onClick();\n }}\n style={{\n flex: \"0\",\n appearance: \"none\",\n background: \"none\",\n color: theme.BASE_COLOR,\n opacity: disabled ? 0.25 : 1,\n border: \"none\",\n fontSize: \"12px\",\n whiteSpace: \"nowrap\",\n display: \"inline-flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n padding: \"2px 0\",\n cursor: \"pointer\",\n ...style,\n }}\n >\n {children}\n </button>\n );\n}\n","\"use client\";\n\nimport React from \"react\";\n\nconst localStorage = typeof window !== \"undefined\" ? window.localStorage : null;\n\nexport function useLocalStorageState<S>(\n key: string,\n defaultValue: S\n): [S, (value: S) => void] {\n const [state, setState] = React.useState<S>(() => {\n const value = localStorage?.getItem(key);\n let result = defaultValue;\n if (value) {\n try {\n result = JSON.parse(value);\n } catch (error) {\n console.error(\"Error parsing localStorage value\", error);\n }\n }\n return result;\n });\n\n const setLocalStorageState = (value: S) => {\n localStorage?.setItem(key, JSON.stringify(value));\n setState(value);\n };\n\n return [state, setLocalStorageState];\n}\n"],"mappings":"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","names":["import_react","import_react","import_react","import_react","React","React","StateInspector","React","root","userId","tasks","state"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/hooks.ts","../src/globals.ts","../src/inspector/useStateInspector.tsx","../src/inspector/StateInspector.tsx","../src/inspector/useLocalStorageState.tsx"],"sourcesContent":["export * from \"@noya-app/state-manager\";\nexport * from \"./hooks\";\nexport * from \"./inspector/StateInspector\";\n","\"use client\";\n\nimport {\n ActionHandler,\n ConnectionEvent,\n EphemeralUserData,\n Get,\n MultiplayerStateManager,\n MultiplayerStateManagerOptions,\n MultiplayerUser,\n StateManager,\n StateManagerOptions,\n SyncAdapter,\n Task,\n stubSync,\n} from \"@noya-app/state-manager\";\nimport {\n ComponentProps,\n SetStateAction,\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n useSyncExternalStore,\n} from \"react\";\nimport {\n shouldTrackEvents$,\n shouldTrackTasks$,\n useObservable,\n} from \"./globals\";\nimport { StateInspector } from \"./inspector/StateInspector\";\nimport { useStateInspector } from \"./inspector/useStateInspector\";\n\nexport function useSyncStateManager<S extends object, M>(\n stateManager: StateManager<S, M>\n): S;\nexport function useSyncStateManager<S extends object, M, P extends string>(\n stateManager: StateManager<S, M>,\n path: P\n): Get<S, P, unknown>;\nexport function useSyncStateManager<S extends object, M>(\n stateManager: StateManager<S, M>,\n path?: string\n) {\n const getSnapshot = useCallback(\n () =>\n typeof path === \"string\"\n ? stateManager.getState(path)\n : stateManager.getState(),\n [stateManager, path]\n );\n\n return useSyncExternalStore(\n stateManager.addListener,\n getSnapshot,\n getSnapshot\n );\n}\n\nexport function useManagedState<S extends object, M = void>(\n createInitialState: () => S,\n options?: StateManagerOptions<S, M>\n) {\n const [stateManager] = useState(\n () => new StateManager<S, M>(createInitialState(), options)\n );\n\n const state = useSyncStateManager(stateManager);\n\n const setState = useCallback(\n (\n ...args: M extends {}\n ? [metadata: M, action: SetStateAction<S>]\n : [action: SetStateAction<S>]\n ) => {\n const [metadata, action] =\n args.length === 1 ? [undefined, args[0]] : args;\n\n if (metadata === undefined) {\n const performActionNoMetadata = stateManager.performAction as (\n handler: ActionHandler<S>\n ) => void;\n\n if (typeof action === \"function\") {\n performActionNoMetadata({\n run: action,\n undo: () => state,\n });\n } else {\n performActionNoMetadata({\n run: () => action,\n undo: () => state,\n });\n }\n } else {\n const performActionWithMetadata = stateManager.performAction as (\n metadata: M,\n handler: ActionHandler<S>\n ) => void;\n\n if (typeof action === \"function\") {\n performActionWithMetadata(metadata, {\n run: action,\n undo: () => state,\n });\n } else {\n performActionWithMetadata(metadata, {\n run: () => action,\n undo: () => state,\n });\n }\n }\n },\n [state, stateManager]\n );\n\n return [state, setState, stateManager] as const;\n}\n\nexport function useManagedHistory<S extends object, M = void>(\n stateManager: StateManager<S, M>\n) {\n return useSyncExternalStore(\n stateManager.historyEmittor.addListener,\n stateManager.getHistorySnapshot,\n stateManager.getHistorySnapshot\n );\n}\n\nexport function useSyncMultiplayerStateManager<S extends object, M = void>(\n multiplayerStateManager: MultiplayerStateManager<S, M>\n) {\n return useSyncExternalStore(\n multiplayerStateManager.addListener,\n multiplayerStateManager.getOptimisticState,\n multiplayerStateManager.getOptimisticState\n );\n}\n\nexport function useEphemeralUserData<E>(\n ephemeralUserData: EphemeralUserData<E>\n) {\n return useSyncExternalStore(\n ephemeralUserData.addListener,\n ephemeralUserData.getSnapshot,\n ephemeralUserData.getSnapshot\n );\n}\n\nconst defaultStubSync = stubSync();\n\nexport function useMultiplayerState<S extends object, M = void, E = void>(\n initialState: S | (() => S),\n options?: MultiplayerStateManagerOptions<S, M> & {\n sync?: (options: {\n ms: MultiplayerStateManager<S, M>;\n ephemeralUserData: EphemeralUserData<E>;\n }) => void;\n trackConnectionEvents?: boolean;\n trackTasks?: boolean;\n inspector?:\n | boolean\n | Pick<ComponentProps<typeof StateInspector>, \"colorScheme\" | \"anchor\">;\n }\n) {\n const { sync = defaultStubSync as unknown as SyncAdapter<S, M, E>, ...rest } =\n options ?? {};\n\n const [multiplayerStateManager] = useState(\n () => new MultiplayerStateManager<S, M>(initialState, rest)\n );\n const syncRef = useRef(sync);\n\n const [connectionEvents, setConnectionEvents] = useState<\n ConnectionEvent<S>[] | undefined\n >();\n\n const [connectedUsers, setConnectedUsers] = useState<MultiplayerUser[]>([]);\n const [ephemeralUserData] = useState<EphemeralUserData<E>>(\n () => new EphemeralUserData()\n );\n const [tasks, setTasks] = useState<Task[]>([]);\n const [userId, setUserId] = useState<string | undefined>();\n\n const extras = useMemo(() => {\n return {\n tasks,\n connectionEvents,\n connectedUsers,\n ephemeralUserData,\n // evaluate: async (code: string) => {\n // const payload = await rpcManager.request({ type: \"eval\", code });\n\n // if (payload.type !== \"eval\") {\n // throw new Error(`Unexpected rpc response type: ${payload.type}`);\n // }\n // },\n };\n }, [tasks, connectionEvents, connectedUsers, ephemeralUserData]);\n\n const globalTrackEvents = useObservable(shouldTrackEvents$);\n const globalTrackTasks = useObservable(shouldTrackTasks$);\n\n const shouldTrackEvents = options?.trackConnectionEvents ?? globalTrackEvents;\n const shouldTrackTasks = options?.trackTasks ?? globalTrackTasks;\n\n const trackEventsCallbackRef = useRef<\n ((e: ConnectionEvent<S>) => void) | undefined\n >();\n const trackConnectedUsersCallbackRef = useRef<\n ((users: MultiplayerUser[], userId: string) => void) | undefined\n >();\n const trackTasksCallbackRef = useRef<((tasks: Task[]) => void) | undefined>();\n\n useEffect(() => {\n trackEventsCallbackRef.current = shouldTrackEvents\n ? (e) =>\n setConnectionEvents((events) => {\n const updated = events ? [...events, e] : [e];\n return updated.length > 50 ? updated.slice(-50) : updated;\n })\n : undefined;\n }, [shouldTrackEvents]);\n\n useEffect(() => {\n trackConnectedUsersCallbackRef.current = (users, userId) => {\n setConnectedUsers(users);\n setUserId(userId);\n };\n }, []);\n\n useEffect(() => {\n trackTasksCallbackRef.current = shouldTrackTasks\n ? (tasks) => {\n setTasks((old) => {\n const newIds = new Set(tasks.map((t) => t.id));\n\n // merge old and new tasks, preferring new tasks\n const unchanged = old.filter((t) => !newIds.has(t.id));\n\n return [...unchanged, ...tasks];\n });\n }\n : undefined;\n }, [shouldTrackTasks]);\n\n useEffect(() => {\n if (syncRef.current) {\n return syncRef.current({\n ms: multiplayerStateManager,\n ephemeralUserData,\n onConnectionEvent: (e) => trackEventsCallbackRef.current?.(e),\n onChangeConnectedUsers: (users, userId) =>\n trackConnectedUsersCallbackRef.current?.(users, userId),\n onChangeTasks: (tasks) => trackTasksCallbackRef.current?.(tasks),\n });\n }\n }, [ephemeralUserData, multiplayerStateManager]);\n\n const state = useSyncMultiplayerStateManager(multiplayerStateManager);\n\n const forceInit = useCallback(() => {\n const state =\n typeof initialState === \"function\" ? initialState() : initialState;\n const schema = options?.schema;\n multiplayerStateManager.sm.clearHistory();\n multiplayerStateManager.sendInit({ force: true, state, schema });\n }, [initialState, multiplayerStateManager, options?.schema]);\n\n useStateInspector({\n ephemeralUserData,\n multiplayerStateManager,\n state,\n connectionEvents,\n connectedUsers,\n tasks,\n userId,\n forceInit,\n disabled: !options?.inspector,\n ...(typeof options?.inspector === \"object\" && options.inspector),\n });\n\n return [\n state,\n multiplayerStateManager.setState,\n multiplayerStateManager,\n extras,\n ] as const;\n}\n","import { Emitter } from \"@noya-app/emitter\";\nimport { useEffect, useState } from \"react\";\n\nclass Observable<T> {\n private value: T;\n private emitter = new Emitter<[T]>();\n\n constructor(value: T) {\n this.value = value;\n }\n\n get() {\n return this.value;\n }\n\n set(value: T) {\n if (this.value === value) return;\n\n this.value = value;\n\n this.emitter.emit(value);\n }\n\n listen(callback: (value: T) => void) {\n callback(this.value);\n\n return this.emitter.addListener(callback);\n }\n}\n\nexport function useObservable<T>(observable: Observable<T>) {\n const [value, setValue] = useState(observable.get());\n\n useEffect(() => {\n return observable.listen(setValue);\n }, [observable]);\n\n return value;\n}\n\nexport const shouldTrackEvents$ = new Observable(false);\nexport const shouldTrackTasks$ = new Observable(false);\n","\"use client\";\n\nimport {\n ConnectionEvent,\n EphemeralUserData,\n MultiplayerStateManager,\n MultiplayerUser,\n Task,\n} from \"@noya-app/state-manager\";\nimport React, { ComponentProps, useLayoutEffect } from \"react\";\nimport { createRoot } from \"react-dom/client\";\n\nimport { Root } from \"react-dom/client\";\nimport { StateInspector } from \"./StateInspector\";\n\nfunction createPortalElement() {\n const el = document.createElement(\"div\");\n el.id = \"noya-multiplayer-react-inspector-portal\";\n return el;\n}\n\nexport function useStateInspector<S extends object, M, E>({\n state,\n connectionEvents,\n connectedUsers,\n tasks,\n userId,\n disabled = false,\n colorScheme,\n anchor,\n multiplayerStateManager,\n ephemeralUserData,\n forceInit,\n}: {\n state: S;\n connectionEvents?: ConnectionEvent<S>[];\n connectedUsers?: MultiplayerUser[];\n tasks?: Task[];\n userId?: string;\n disabled: boolean;\n multiplayerStateManager: MultiplayerStateManager<S, M>;\n ephemeralUserData: EphemeralUserData<E>;\n forceInit: () => void;\n} & Pick<ComponentProps<typeof StateInspector>, \"colorScheme\" | \"anchor\">) {\n const [root, setRoot] = React.useState<Root | null>(null);\n\n useLayoutEffect(() => {\n if (disabled) return;\n\n const portalElement = createPortalElement();\n\n const root = createRoot(portalElement);\n\n document.body.appendChild(portalElement);\n\n setRoot(root);\n\n return () => {\n document.body.removeChild(portalElement);\n };\n }, [disabled]);\n\n useLayoutEffect(() => {\n if (!root) return;\n\n root.render(\n <StateInspector\n state={state}\n connectionEvents={connectionEvents}\n connectedUsers={connectedUsers}\n tasks={tasks}\n userId={userId}\n colorScheme={colorScheme}\n anchor={anchor}\n forceInit={forceInit}\n multiplayerStateManager={\n multiplayerStateManager as MultiplayerStateManager<any, any>\n }\n ephemeralUserData={ephemeralUserData as EphemeralUserData<any>}\n />\n );\n }, [\n anchor,\n colorScheme,\n connectedUsers,\n connectionEvents,\n root,\n state,\n multiplayerStateManager,\n userId,\n tasks,\n forceInit,\n ephemeralUserData,\n ]);\n}\n","\"use client\";\n\nimport {\n ConnectionEvent,\n EphemeralUserData,\n ExtendedPathKey,\n MultiplayerStateManager,\n MultiplayerUser,\n Task,\n} from \"@noya-app/state-manager\";\nimport React, {\n CSSProperties,\n ComponentPropsWithoutRef,\n FC,\n memo,\n useEffect,\n useLayoutEffect,\n} from \"react\";\nimport {\n ObjectInspector,\n ObjectLabel,\n ObjectName,\n ObjectPreview,\n chromeDark,\n chromeLight,\n} from \"react-inspector\";\nimport { shouldTrackEvents$, shouldTrackTasks$ } from \"../globals\";\nimport { useEphemeralUserData, useManagedHistory } from \"../hooks\";\nimport { useLocalStorageState } from \"./useLocalStorageState\";\n\nconst lightTheme = {\n ...chromeLight,\n BASE_BACKGROUND_COLOR: \"transparent\",\n OBJECT_NAME_COLOR: \"rgba(0,0,0,0.7)\",\n} as any;\n\nconst darkTheme = {\n ...chromeDark,\n BASE_BACKGROUND_COLOR: \"transparent\",\n OBJECT_NAME_COLOR: \"rgba(255,255,255,0.7)\",\n} as any;\n\nconst styles = {\n sectionInner: {\n flex: \"1 1 0\",\n overflowY: \"auto\",\n overflowX: \"hidden\",\n display: \"flex\",\n flexDirection: \"column\",\n },\n} as const;\n\nfunction ToggleButton({\n showInspector,\n setShowInspector,\n theme,\n anchor,\n}: {\n showInspector: boolean;\n setShowInspector: (value: boolean) => void;\n theme: typeof chromeLight;\n anchor: Anchor;\n}) {\n return (\n <span\n role=\"button\"\n style={{\n flex: \"0\",\n appearance: \"none\",\n color: theme.BASE_COLOR,\n border: \"none\",\n fontSize: \"12px\",\n whiteSpace: \"nowrap\",\n display: \"inline-flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n padding: \"2px 0\",\n }}\n onClick={(event) => {\n event.stopPropagation();\n\n setShowInspector(!showInspector);\n }}\n >\n <span style={{ width: \"12px\", height: \"12px\" }}>\n {showInspector === (anchor === \"right\") ? (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n strokeWidth={1.5}\n stroke=\"currentColor\"\n className=\"size-6\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n d=\"m5.25 4.5 7.5 7.5-7.5 7.5m6-15 7.5 7.5-7.5 7.5\"\n />\n </svg>\n ) : (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n strokeWidth={1.5}\n stroke=\"currentColor\"\n className=\"size-6\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n d=\"m18.75 4.5-7.5 7.5 7.5 7.5m-6-15L5.25 12l7.5 7.5\"\n />\n </svg>\n )}\n </span>\n </span>\n );\n}\n\nfunction DisclosureSection({\n open,\n setOpen,\n title,\n right,\n children,\n colorScheme,\n isFirst,\n style,\n}: {\n open: boolean;\n setOpen?: (value: boolean) => void;\n title: string;\n right?: React.ReactNode;\n children: React.ReactNode;\n colorScheme: \"light\" | \"dark\";\n isFirst?: boolean;\n style?: CSSProperties;\n}) {\n const theme = colorScheme === \"light\" ? lightTheme : darkTheme;\n\n const borderColor =\n colorScheme === \"light\" ? \"rgba(0,0,0,0.1)\" : \"rgba(255,255,255,0.1)\";\n\n return (\n <div\n style={{\n flex: open ? \"1 1 0\" : \"0\",\n display: \"flex\",\n flexDirection: \"column\",\n ...style,\n }}\n >\n <div\n onClick={() => setOpen?.(!open)}\n style={{\n cursor: \"default\",\n fontSize: \"12px\",\n padding: \"4px 10px\",\n display: \"flex\",\n alignItems: \"center\",\n ...(!isFirst && { borderTop: `1px solid ${borderColor}` }),\n ...(open && { borderBottom: `1px solid ${borderColor}` }),\n }}\n >\n {setOpen && (\n <Arrow\n expanded={open}\n style={{\n fontSize: theme.ARROW_FONT_SIZE,\n marginRight: theme.ARROW_MARGIN_RIGHT + 1,\n color: theme.ARROW_COLOR,\n }}\n />\n )}\n <span style={{ flex: \"1 1 0\", userSelect: \"none\" }}>{title}</span>\n {right}\n </div>\n {open && children}\n </div>\n );\n}\n\nfunction InspectorRow({\n children,\n colorScheme,\n selected,\n style,\n variant,\n}: {\n children: React.ReactNode;\n colorScheme: \"light\" | \"dark\";\n selected?: boolean;\n style?: CSSProperties;\n variant?: \"up\" | \"down\";\n}) {\n const solidBorderColor =\n colorScheme === \"light\" ? \"rgb(223 223 223)\" : \"rgb(29 29 29)\";\n\n return (\n <div\n style={{\n borderBottom: `1px solid ${solidBorderColor}`,\n fontSize: \"12px\",\n fontFamily: \"Menlo, monospace\",\n padding: \"2px 12px 1px\",\n display: \"flex\",\n alignItems: \"center\",\n background:\n variant === \"up\"\n ? \"rgba(0,255,0,0.1)\"\n : variant === \"down\"\n ? \"rgba(255,0,0,0.1)\"\n : selected\n ? \"rgb(59 130 246 / 15%)\"\n : undefined,\n // background:\n // colorScheme === \"light\"\n // ? \"rgba(0,0,0,0.05)\"\n // : \"rgba(255,255,255,0.05)\",\n ...style,\n }}\n >\n <span\n style={{\n fontFamily: \"Menlo, monospace\",\n fontSize: \"11px\",\n borderRadius: 4,\n display: \"inline-block\",\n }}\n >\n {children}\n </span>\n </div>\n );\n}\n\nconst HISTORY_ELEMENT_PREFIX = \"noya-multiplayer-history-\";\n\ntype Anchor = \"left\" | \"right\" | \"bottom left\" | \"bottom right\";\n\nexport const StateInspector = memo(function StateInspector<\n S extends object,\n M,\n E,\n>({\n state,\n connectionEvents,\n connectedUsers,\n tasks,\n userId,\n unstyled,\n colorScheme = \"light\",\n multiplayerStateManager,\n ephemeralUserData,\n anchor = \"right\",\n forceInit,\n ...props\n}: {\n state: S;\n connectionEvents?: ConnectionEvent<S>[];\n connectedUsers?: MultiplayerUser[];\n tasks?: Task[];\n userId?: string;\n unstyled?: boolean;\n colorScheme?: \"light\" | \"dark\";\n multiplayerStateManager: MultiplayerStateManager<S, M>;\n ephemeralUserData: EphemeralUserData<E>;\n anchor?: Anchor;\n forceInit: () => void;\n} & ComponentPropsWithoutRef<\"div\">) {\n const [didMount, setDidMount] = React.useState(false);\n\n useLayoutEffect(() => {\n setDidMount(true);\n }, []);\n\n const eventsContainerRef = React.useRef<HTMLDivElement>(null);\n const historyContainerRef = React.useRef<HTMLDivElement>(null);\n\n const [showInspector, setShowInspector] = useLocalStorageState(\n \"noya-multiplayer-react-show-inspector\",\n true\n );\n const [showEvents, setShowEvents] = useLocalStorageState(\n \"noya-multiplayer-react-show-events\",\n false\n );\n const [showHistory, setShowHistory] = useLocalStorageState(\n \"noya-multiplayer-react-show-history\",\n false\n );\n const [showUsers, setShowUsers] = useLocalStorageState(\n \"noya-multiplayer-react-show-users\",\n true\n );\n const [showData, setShowData] = useLocalStorageState(\n \"noya-multiplayer-react-show-data\",\n true\n );\n const [showTasks, setShowTasks] = useLocalStorageState(\n \"noya-multiplayer-react-show-tasks\",\n false\n );\n const [showEphemeral, setShowEphemeral] = useLocalStorageState(\n \"noya-multiplayer-react-show-ephemeral\",\n false\n );\n\n useEffect(() => {\n shouldTrackEvents$.set(showEvents);\n }, [showEvents]);\n\n useEffect(() => {\n shouldTrackTasks$.set(showTasks);\n }, [showTasks]);\n\n useEffect(() => {\n if (eventsContainerRef.current) {\n eventsContainerRef.current.scrollTop =\n eventsContainerRef.current.scrollHeight;\n }\n }, [connectionEvents]);\n\n const ephemeral = useEphemeralUserData(ephemeralUserData);\n const historySnapshot = useManagedHistory(multiplayerStateManager.sm);\n\n useEffect(() => {\n if (historyContainerRef.current) {\n historyContainerRef.current.scrollTop =\n historyContainerRef.current.scrollHeight;\n }\n }, [historySnapshot]);\n\n // If history index changes, scroll to the new history entry into view\n useEffect(() => {\n if (!historyContainerRef.current) return;\n\n const historyEntry = historyContainerRef.current.querySelector(\n `#${HISTORY_ELEMENT_PREFIX}${historySnapshot.historyIndex}`\n );\n\n if (historyEntry) {\n historyEntry.scrollIntoView({\n block: \"nearest\",\n inline: \"nearest\",\n });\n }\n }, [historySnapshot.historyIndex]);\n\n const theme = colorScheme === \"light\" ? lightTheme : darkTheme;\n // const borderColor =\n // colorScheme === \"light\" ? \"rgba(0,0,0,0.1)\" : \"rgba(255,255,255,0.1)\";\n const solidBorderColor =\n colorScheme === \"light\" ? \"rgb(223 223 223)\" : \"rgb(29 29 29)\";\n\n const baseStyle: CSSProperties = {\n position: \"fixed\",\n top: 12,\n ...(anchor.includes(\"right\") ? { right: 12 } : { left: 12 }),\n bottom: 12,\n width: 400,\n background:\n colorScheme === \"light\" ? \"rgba(255,255,255,0.95)\" : \"rgba(0,0,0,0.95)\",\n backdropFilter: \"blur(48px)\",\n border: `1px solid ${solidBorderColor}`,\n // outlineOffset: -1,\n borderRadius: 4,\n display: \"flex\",\n flexDirection: \"column\",\n // gap: 12,\n color: theme.BASE_COLOR,\n overflow: \"hidden\",\n zIndex: 9999,\n lineHeight: \"13px\",\n };\n\n if (!didMount) return null;\n\n if (!showInspector) {\n return (\n <div\n {...props}\n style={{\n ...baseStyle,\n padding: \"4px 10px\",\n width: undefined,\n ...(anchor.includes(\"bottom\")\n ? { bottom: 12, top: undefined }\n : { bottom: undefined }),\n }}\n onClick={() => setShowInspector(true)}\n >\n <ToggleButton\n showInspector={showInspector}\n setShowInspector={setShowInspector}\n theme={theme}\n anchor={anchor}\n />\n </div>\n );\n }\n\n return (\n <div\n {...props}\n style={{\n ...(!unstyled && baseStyle),\n ...props.style,\n }}\n >\n <DisclosureSection\n isFirst\n open={showUsers}\n setOpen={setShowUsers}\n title=\"Users\"\n colorScheme={colorScheme}\n style={{\n flex: \"0 0 auto\",\n maxHeight: \"200px\",\n }}\n right={\n <ToggleButton\n showInspector={showInspector}\n setShowInspector={setShowInspector}\n theme={theme}\n anchor={anchor}\n />\n }\n >\n <div style={{ ...styles.sectionInner, flex: \"0 0 auto\" }}>\n {connectedUsers?.map((user) => (\n <InspectorRow\n key={user.id}\n colorScheme={colorScheme}\n variant={user.id === userId ? \"up\" : undefined}\n >\n {user.name} ({ellipsis(user.id.toString(), 8, \"middle\")})\n </InspectorRow>\n ))}\n {!connectedUsers && (\n <div\n style={{\n padding: \"12px\",\n fontSize: \"12px\",\n display: \"flex\",\n flexDirection: \"column\",\n gap: \"4px\",\n }}\n >\n <span>No connected users</span>\n </div>\n )}\n </div>\n </DisclosureSection>\n <DisclosureSection\n title=\"Data\"\n colorScheme={colorScheme}\n setOpen={setShowData}\n open={showData}\n right={\n <span\n style={{\n display: \"flex\",\n gap: \"4px\",\n }}\n >\n <SmallButton\n // disabled={!historySnapshot.canUndo}\n theme={theme}\n onClick={() => {\n forceInit();\n }}\n >\n Reset\n </SmallButton>\n </span>\n }\n >\n <div style={styles.sectionInner}>\n <InspectorRow colorScheme={colorScheme}>\n <ObjectInspector\n name={multiplayerStateManager.schema ? \"state\" : undefined}\n data={state}\n theme={theme}\n />\n </InspectorRow>\n {multiplayerStateManager.schema && (\n <InspectorRow colorScheme={colorScheme}>\n <ObjectInspector\n name=\"schema\"\n data={multiplayerStateManager.schema}\n theme={theme}\n />\n </InspectorRow>\n )}\n </div>\n </DisclosureSection>\n <DisclosureSection\n open={showHistory}\n setOpen={setShowHistory}\n title=\"History\"\n colorScheme={colorScheme}\n right={\n <span\n style={{\n display: \"flex\",\n gap: \"4px\",\n }}\n >\n <SmallButton\n disabled={!historySnapshot.canUndo}\n theme={theme}\n onClick={() => {\n multiplayerStateManager.undo();\n }}\n >\n <span style={{ width: \"12px\", height: \"12px\" }}>\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"1em\"\n height=\"1em\"\n viewBox=\"0 0 20 20\"\n >\n <path\n fill=\"currentColor\"\n 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\"\n />\n </svg>\n </span>\n </SmallButton>\n <SmallButton\n disabled={!historySnapshot.canRedo}\n theme={theme}\n onClick={() => {\n multiplayerStateManager.redo();\n }}\n >\n <span style={{ width: \"12px\", height: \"12px\" }}>\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"1em\"\n height=\"1em\"\n viewBox=\"0 0 20 20\"\n >\n <path\n fill=\"currentColor\"\n 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\"\n />\n </svg>\n </span>\n </SmallButton>\n </span>\n }\n >\n <div ref={historyContainerRef} style={styles.sectionInner}>\n <div\n id={`${HISTORY_ELEMENT_PREFIX}0`}\n style={{\n borderBottom: `1px solid ${solidBorderColor}`,\n fontSize: \"12px\",\n fontFamily: \"Menlo, monospace\",\n padding: \"2px 12px 1px\",\n display: \"flex\",\n alignItems: \"center\",\n background:\n historySnapshot.historyIndex === 0\n ? \"rgb(59 130 246 / 15%)\"\n : undefined,\n }}\n >\n <span\n style={{\n fontFamily: \"Menlo, monospace\",\n fontSize: \"11px\",\n borderRadius: 4,\n display: \"inline-block\",\n }}\n >\n Initial state\n </span>\n </div>\n {historySnapshot.history.map((entry, index) => (\n <div\n id={`${HISTORY_ELEMENT_PREFIX}${index + 1}`}\n key={index}\n style={{\n padding: \"0px 12px 1px\",\n borderBottom: `1px solid ${solidBorderColor}`,\n background:\n index + 1 === historySnapshot.historyIndex\n ? \"rgb(59 130 246 / 15%)\"\n : undefined,\n }}\n >\n {entry.redoPatches?.map((patch, j) => (\n <pre\n key={j}\n style={{\n fontSize: \"11px\",\n display: \"flex\",\n flexWrap: \"wrap\",\n }}\n >\n {patch.op === \"add\" && (\n <>\n <span style={{ fontStyle: \"italic\" }}>\n {pathToString(patch.path)}\n </span>\n {\" = \"}\n <ObjectInspector data={patch.value} theme={theme} />\n </>\n )}\n {patch.op === \"replace\" && (\n <>\n <span style={{ fontStyle: \"italic\" }}>\n {pathToString(patch.path)}\n </span>\n {\" = \"}\n <ObjectInspector data={patch.value} theme={theme} />\n </>\n )}\n {patch.op === \"remove\" && (\n <>\n <span style={{ color: theme.OBJECT_VALUE_STRING_COLOR }}>\n delete{\" \"}\n </span>\n <span style={{ fontStyle: \"italic\" }}>\n {pathToString(patch.path)}\n </span>\n </>\n )}\n {patch.op === \"move\" && (\n <>\n <span style={{ fontStyle: \"italic\" }}>\n {pathToString(patch.from!)}\n </span>\n {\" -> \"}\n <span style={{ fontStyle: \"italic\" }}>\n {pathToString(patch.path)}\n </span>\n </>\n )}\n </pre>\n ))}\n </div>\n ))}\n </div>\n </DisclosureSection>\n <DisclosureSection\n title=\"Tasks\"\n colorScheme={colorScheme}\n open={showTasks}\n setOpen={setShowTasks}\n >\n <div style={styles.sectionInner}>\n {tasks?.map((task) => (\n <InspectorRow\n key={task.id}\n colorScheme={colorScheme}\n style={{\n backgroundColor:\n task.status === \"done\"\n ? \"rgba(0,255,0,0.2)\"\n : task.status === \"error\"\n ? \"rgba(255,0,0,0.2)\"\n : undefined,\n }}\n >\n <ObjectInspector\n name={task.name}\n data={task.payload}\n theme={theme}\n />\n </InspectorRow>\n ))}\n </div>\n </DisclosureSection>\n <DisclosureSection\n open={showEphemeral}\n setOpen={setShowEphemeral}\n title=\"Ephemeral User Data\"\n colorScheme={colorScheme}\n >\n <div style={styles.sectionInner}>\n {Object.entries(ephemeral).map(([key, value]) => (\n <InspectorRow key={key} colorScheme={colorScheme}>\n <ObjectInspector name={key} data={value} theme={theme} />\n </InspectorRow>\n ))}\n </div>\n </DisclosureSection>\n <DisclosureSection\n open={showEvents}\n setOpen={setShowEvents}\n title=\"Events\"\n colorScheme={colorScheme}\n >\n <div ref={eventsContainerRef} style={styles.sectionInner}>\n {connectionEvents?.map((event, index) =>\n event.type === \"stateChange\" ? (\n <InspectorRow key={index} colorScheme={colorScheme}>\n connection:{\" \"}\n <span style={{ fontStyle: \"italic\" }}>\n {event.state.toLowerCase()}\n </span>\n </InspectorRow>\n ) : (\n <InspectorRow\n key={index}\n colorScheme={colorScheme}\n variant={event.type === \"send\" ? \"up\" : \"down\"}\n style={{\n padding: \"0px 12px 1px\",\n }}\n >\n <ObjectInspector\n data={event.message}\n theme={theme}\n nodeRenderer={({\n depth,\n name,\n data,\n isNonenumerable,\n expanded,\n }: any) => {\n const direction = event.type === \"send\" ? \"up\" : \"down\";\n\n return depth === 0 ? (\n <ObjectRootLabel direction={direction} data={data} />\n ) : (\n <ObjectLabel\n direction={direction}\n name={name}\n data={data}\n isNonenumerable={isNonenumerable}\n />\n );\n }}\n />\n </InspectorRow>\n )\n )}\n {!connectionEvents && (\n <div\n style={{\n padding: \"12px\",\n fontSize: \"12px\",\n display: \"flex\",\n flexDirection: \"column\",\n gap: \"4px\",\n }}\n >\n <span>No recorded events</span>\n </div>\n )}\n </div>\n </DisclosureSection>\n </div>\n );\n});\n\ntype MessageDirection = \"up\" | \"down\";\n\nconst ObjectRootLabel: FC<{\n name?: string;\n data: any;\n direction?: MessageDirection;\n}> = ({ name, data, direction }) => {\n if (typeof name === \"string\") {\n return (\n <span>\n <ObjectName name={name} />\n <span>: </span>\n <ObjectPreview data={data} />\n </span>\n );\n }\n if (direction === \"up\" || direction === \"down\") {\n const arrow = direction === \"up\" ? \"↑\" : \"↓\";\n\n return (\n <span>\n <span\n style={{\n background:\n direction === \"up\" ? \"rgba(0,255,0,0.2)\" : \"rgba(255,0,0,0.2)\",\n // color: \"white\",\n width: 12,\n height: 12,\n borderRadius: 2,\n display: \"inline-flex\",\n justifyContent: \"center\",\n alignItems: \"center\",\n marginRight: 4,\n lineHeight: \"12px\",\n }}\n >\n {arrow}\n </span>\n <ObjectPreview data={data} />\n </span>\n );\n } else {\n return <ObjectPreview data={data} />;\n }\n};\n\n// Rotates ▶️ or ▼ based on expanded state\nfunction Arrow({\n expanded,\n onClick,\n style,\n}: {\n expanded: boolean;\n onClick?: () => void;\n style?: CSSProperties;\n}) {\n return (\n <span\n role=\"button\"\n onClick={onClick}\n style={{\n display: \"inline-block\",\n textAlign: \"center\",\n transform: expanded ? \"rotate(0deg)\" : \"rotate(-90deg)\",\n fontFamily: \"Menlo, monospace\",\n ...style,\n }}\n >\n {\"▼\"}\n </span>\n );\n}\n\nfunction pathToString(extendedPath: ExtendedPathKey[]) {\n return extendedPath\n .map((key) =>\n typeof key === \"object\"\n ? `:${ellipsis(key.id.toString(), 8, \"middle\")}`\n : key\n )\n .map((key, index) =>\n index === 0 || (typeof key === \"string\" && key.startsWith(\":\"))\n ? key\n : `.${key}`\n )\n .join(\"\");\n}\n\ntype EllipsisPosition = \"start\" | \"middle\" | \"end\";\n\nfunction ellipsis(\n str: string,\n maxLength: number,\n position: EllipsisPosition = \"end\"\n) {\n if (str.length <= maxLength) return str;\n\n switch (position) {\n case \"start\":\n return `…${str.slice(str.length - maxLength)}`;\n case \"middle\":\n const halfLength = Math.floor(maxLength / 2);\n return `${str.slice(0, halfLength)}…${str.slice(str.length - halfLength)}`;\n case \"end\":\n return `${str.slice(0, maxLength)}…`;\n }\n}\n\nfunction SmallButton({\n children,\n onClick,\n style,\n theme,\n disabled,\n}: {\n children: React.ReactNode;\n onClick: () => void;\n style?: CSSProperties;\n theme: typeof chromeLight;\n disabled?: boolean;\n}) {\n return (\n <button\n type=\"button\"\n disabled={disabled}\n onClick={(event) => {\n event.stopPropagation();\n onClick();\n }}\n style={{\n flex: \"0\",\n appearance: \"none\",\n background: \"none\",\n color: theme.BASE_COLOR,\n opacity: disabled ? 0.25 : 1,\n border: \"none\",\n fontSize: \"12px\",\n whiteSpace: \"nowrap\",\n display: \"inline-flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n padding: \"2px 0\",\n cursor: \"pointer\",\n ...style,\n }}\n >\n {children}\n </button>\n );\n}\n","\"use client\";\n\nimport React from \"react\";\n\nconst localStorage = typeof window !== \"undefined\" ? window.localStorage : null;\n\nexport function useLocalStorageState<S>(\n key: string,\n defaultValue: S\n): [S, (value: S) => void] {\n const [state, setState] = React.useState<S>(() => {\n const value = localStorage?.getItem(key);\n let result = defaultValue;\n if (value) {\n try {\n result = JSON.parse(value);\n } catch (error) {\n console.error(\"Error parsing localStorage value\", error);\n }\n }\n return result;\n });\n\n const setLocalStorageState = (value: S) => {\n localStorage?.setItem(key, JSON.stringify(value));\n setState(value);\n };\n\n return [state, setLocalStorageState];\n}\n"],"mappings":"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","names":["import_react","import_react","import_react","import_react","React","React","StateInspector","React","root","userId","tasks","state"]}
package/dist/index.mjs CHANGED
@@ -3,6 +3,7 @@ export * from "@noya-app/state-manager";
3
3
 
4
4
  // src/hooks.ts
5
5
  import {
6
+ EphemeralUserData,
6
7
  MultiplayerStateManager,
7
8
  StateManager,
8
9
  stubSync
@@ -280,6 +281,7 @@ var StateInspector = memo(function StateInspector2({
280
281
  unstyled,
281
282
  colorScheme = "light",
282
283
  multiplayerStateManager,
284
+ ephemeralUserData,
283
285
  anchor = "right",
284
286
  forceInit,
285
287
  ...props
@@ -314,6 +316,10 @@ var StateInspector = memo(function StateInspector2({
314
316
  "noya-multiplayer-react-show-tasks",
315
317
  false
316
318
  );
319
+ const [showEphemeral, setShowEphemeral] = useLocalStorageState(
320
+ "noya-multiplayer-react-show-ephemeral",
321
+ false
322
+ );
317
323
  useEffect2(() => {
318
324
  shouldTrackEvents$.set(showEvents);
319
325
  }, [showEvents]);
@@ -325,6 +331,7 @@ var StateInspector = memo(function StateInspector2({
325
331
  eventsContainerRef.current.scrollTop = eventsContainerRef.current.scrollHeight;
326
332
  }
327
333
  }, [connectionEvents]);
334
+ const ephemeral = useEphemeralUserData(ephemeralUserData);
328
335
  const historySnapshot = useManagedHistory(multiplayerStateManager.sm);
329
336
  useEffect2(() => {
330
337
  if (historyContainerRef.current) {
@@ -640,6 +647,16 @@ var StateInspector = memo(function StateInspector2({
640
647
  )
641
648
  )))
642
649
  ),
650
+ /* @__PURE__ */ React2.createElement(
651
+ DisclosureSection,
652
+ {
653
+ open: showEphemeral,
654
+ setOpen: setShowEphemeral,
655
+ title: "Ephemeral User Data",
656
+ colorScheme
657
+ },
658
+ /* @__PURE__ */ React2.createElement("div", { style: styles.sectionInner }, Object.entries(ephemeral).map(([key, value]) => /* @__PURE__ */ React2.createElement(InspectorRow, { key, colorScheme }, /* @__PURE__ */ React2.createElement(ObjectInspector, { name: key, data: value, theme }))))
659
+ ),
643
660
  /* @__PURE__ */ React2.createElement(
644
661
  DisclosureSection,
645
662
  {
@@ -823,6 +840,7 @@ function useStateInspector({
823
840
  colorScheme,
824
841
  anchor,
825
842
  multiplayerStateManager,
843
+ ephemeralUserData,
826
844
  forceInit
827
845
  }) {
828
846
  const [root, setRoot] = React3.useState(null);
@@ -852,7 +870,8 @@ function useStateInspector({
852
870
  colorScheme,
853
871
  anchor,
854
872
  forceInit,
855
- multiplayerStateManager
873
+ multiplayerStateManager,
874
+ ephemeralUserData
856
875
  }
857
876
  )
858
877
  );
@@ -866,7 +885,8 @@ function useStateInspector({
866
885
  multiplayerStateManager,
867
886
  userId,
868
887
  tasks,
869
- forceInit
888
+ forceInit,
889
+ ephemeralUserData
870
890
  ]);
871
891
  }
872
892
 
@@ -936,6 +956,13 @@ function useSyncMultiplayerStateManager(multiplayerStateManager) {
936
956
  multiplayerStateManager.getOptimisticState
937
957
  );
938
958
  }
959
+ function useEphemeralUserData(ephemeralUserData) {
960
+ return useSyncExternalStore(
961
+ ephemeralUserData.addListener,
962
+ ephemeralUserData.getSnapshot,
963
+ ephemeralUserData.getSnapshot
964
+ );
965
+ }
939
966
  var defaultStubSync = stubSync();
940
967
  function useMultiplayerState(initialState, options) {
941
968
  const { sync = defaultStubSync, ...rest } = options ?? {};
@@ -945,13 +972,17 @@ function useMultiplayerState(initialState, options) {
945
972
  const syncRef = useRef(sync);
946
973
  const [connectionEvents, setConnectionEvents] = useState2();
947
974
  const [connectedUsers, setConnectedUsers] = useState2([]);
975
+ const [ephemeralUserData] = useState2(
976
+ () => new EphemeralUserData()
977
+ );
948
978
  const [tasks, setTasks] = useState2([]);
949
979
  const [userId, setUserId] = useState2();
950
980
  const extras = useMemo(() => {
951
981
  return {
952
982
  tasks,
953
983
  connectionEvents,
954
- connectedUsers
984
+ connectedUsers,
985
+ ephemeralUserData
955
986
  // evaluate: async (code: string) => {
956
987
  // const payload = await rpcManager.request({ type: "eval", code });
957
988
  // if (payload.type !== "eval") {
@@ -959,7 +990,7 @@ function useMultiplayerState(initialState, options) {
959
990
  // }
960
991
  // },
961
992
  };
962
- }, [tasks, connectionEvents, connectedUsers]);
993
+ }, [tasks, connectionEvents, connectedUsers, ephemeralUserData]);
963
994
  const globalTrackEvents = useObservable(shouldTrackEvents$);
964
995
  const globalTrackTasks = useObservable(shouldTrackTasks$);
965
996
  const shouldTrackEvents = options?.trackConnectionEvents ?? globalTrackEvents;
@@ -992,12 +1023,13 @@ function useMultiplayerState(initialState, options) {
992
1023
  if (syncRef.current) {
993
1024
  return syncRef.current({
994
1025
  ms: multiplayerStateManager,
1026
+ ephemeralUserData,
995
1027
  onConnectionEvent: (e) => trackEventsCallbackRef.current?.(e),
996
1028
  onChangeConnectedUsers: (users, userId2) => trackConnectedUsersCallbackRef.current?.(users, userId2),
997
1029
  onChangeTasks: (tasks2) => trackTasksCallbackRef.current?.(tasks2)
998
1030
  });
999
1031
  }
1000
- }, [multiplayerStateManager]);
1032
+ }, [ephemeralUserData, multiplayerStateManager]);
1001
1033
  const state = useSyncMultiplayerStateManager(multiplayerStateManager);
1002
1034
  const forceInit = useCallback(() => {
1003
1035
  const state2 = typeof initialState === "function" ? initialState() : initialState;
@@ -1006,6 +1038,7 @@ function useMultiplayerState(initialState, options) {
1006
1038
  multiplayerStateManager.sendInit({ force: true, state: state2, schema });
1007
1039
  }, [initialState, multiplayerStateManager, options?.schema]);
1008
1040
  useStateInspector({
1041
+ ephemeralUserData,
1009
1042
  multiplayerStateManager,
1010
1043
  state,
1011
1044
  connectionEvents,
@@ -1025,6 +1058,7 @@ function useMultiplayerState(initialState, options) {
1025
1058
  }
1026
1059
  export {
1027
1060
  StateInspector,
1061
+ useEphemeralUserData,
1028
1062
  useManagedHistory,
1029
1063
  useManagedState,
1030
1064
  useMultiplayerState,
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/hooks.ts","../src/globals.ts","../src/inspector/useStateInspector.tsx","../src/inspector/StateInspector.tsx","../src/inspector/useLocalStorageState.tsx"],"sourcesContent":["export * from \"@noya-app/state-manager\";\nexport * from \"./hooks\";\nexport * from \"./inspector/StateInspector\";\n","\"use client\";\n\nimport {\n ActionHandler,\n ConnectionEvent,\n Get,\n MultiplayerStateManager,\n MultiplayerStateManagerOptions,\n MultiplayerUser,\n StateManager,\n StateManagerOptions,\n SyncAdapter,\n Task,\n stubSync,\n} from \"@noya-app/state-manager\";\nimport {\n ComponentProps,\n SetStateAction,\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n useSyncExternalStore,\n} from \"react\";\nimport {\n shouldTrackEvents$,\n shouldTrackTasks$,\n useObservable,\n} from \"./globals\";\nimport { StateInspector } from \"./inspector/StateInspector\";\nimport { useStateInspector } from \"./inspector/useStateInspector\";\n\nexport function useSyncStateManager<S extends object, M>(\n stateManager: StateManager<S, M>\n): S;\nexport function useSyncStateManager<S extends object, M, P extends string>(\n stateManager: StateManager<S, M>,\n path: P\n): Get<S, P, unknown>;\nexport function useSyncStateManager<S extends object, M>(\n stateManager: StateManager<S, M>,\n path?: string\n) {\n const getSnapshot = useCallback(\n () =>\n typeof path === \"string\"\n ? stateManager.getState(path)\n : stateManager.getState(),\n [stateManager, path]\n );\n\n return useSyncExternalStore(\n stateManager.addListener,\n getSnapshot,\n getSnapshot\n );\n}\n\nexport function useManagedState<S extends object, M = void>(\n createInitialState: () => S,\n options?: StateManagerOptions<S, M>\n) {\n const [stateManager] = useState(\n () => new StateManager<S, M>(createInitialState(), options)\n );\n\n const state = useSyncStateManager(stateManager);\n\n const setState = useCallback(\n (\n ...args: M extends {}\n ? [metadata: M, action: SetStateAction<S>]\n : [action: SetStateAction<S>]\n ) => {\n const [metadata, action] =\n args.length === 1 ? [undefined, args[0]] : args;\n\n if (metadata === undefined) {\n const performActionNoMetadata = stateManager.performAction as (\n handler: ActionHandler<S>\n ) => void;\n\n if (typeof action === \"function\") {\n performActionNoMetadata({\n run: action,\n undo: () => state,\n });\n } else {\n performActionNoMetadata({\n run: () => action,\n undo: () => state,\n });\n }\n } else {\n const performActionWithMetadata = stateManager.performAction as (\n metadata: M,\n handler: ActionHandler<S>\n ) => void;\n\n if (typeof action === \"function\") {\n performActionWithMetadata(metadata, {\n run: action,\n undo: () => state,\n });\n } else {\n performActionWithMetadata(metadata, {\n run: () => action,\n undo: () => state,\n });\n }\n }\n },\n [state, stateManager]\n );\n\n return [state, setState, stateManager] as const;\n}\n\nexport function useManagedHistory<S extends object, M = void>(\n stateManager: StateManager<S, M>\n) {\n return useSyncExternalStore(\n stateManager.historyEmittor.addListener,\n stateManager.getHistorySnapshot,\n stateManager.getHistorySnapshot\n );\n}\n\nexport function useSyncMultiplayerStateManager<S extends object, M = void>(\n multiplayerStateManager: MultiplayerStateManager<S, M>\n) {\n return useSyncExternalStore(\n multiplayerStateManager.addListener,\n multiplayerStateManager.getOptimisticState,\n multiplayerStateManager.getOptimisticState\n );\n}\n\nconst defaultStubSync = stubSync();\n\nexport function useMultiplayerState<S extends object, M = void>(\n initialState: S | (() => S),\n options?: MultiplayerStateManagerOptions<S, M> & {\n sync?: (options: { ms: MultiplayerStateManager<S, M> }) => void;\n trackConnectionEvents?: boolean;\n trackTasks?: boolean;\n inspector?:\n | boolean\n | Pick<ComponentProps<typeof StateInspector>, \"colorScheme\" | \"anchor\">;\n }\n) {\n const { sync = defaultStubSync as unknown as SyncAdapter<S, M>, ...rest } =\n options ?? {};\n\n const [multiplayerStateManager] = useState(\n () => new MultiplayerStateManager<S, M>(initialState, rest)\n );\n const syncRef = useRef(sync);\n\n const [connectionEvents, setConnectionEvents] = useState<\n ConnectionEvent<S>[] | undefined\n >();\n\n const [connectedUsers, setConnectedUsers] = useState<MultiplayerUser[]>([]);\n const [tasks, setTasks] = useState<Task[]>([]);\n const [userId, setUserId] = useState<string | undefined>();\n\n const extras = useMemo(() => {\n return {\n tasks,\n connectionEvents,\n connectedUsers,\n // evaluate: async (code: string) => {\n // const payload = await rpcManager.request({ type: \"eval\", code });\n\n // if (payload.type !== \"eval\") {\n // throw new Error(`Unexpected rpc response type: ${payload.type}`);\n // }\n // },\n };\n }, [tasks, connectionEvents, connectedUsers]);\n\n const globalTrackEvents = useObservable(shouldTrackEvents$);\n const globalTrackTasks = useObservable(shouldTrackTasks$);\n\n const shouldTrackEvents = options?.trackConnectionEvents ?? globalTrackEvents;\n const shouldTrackTasks = options?.trackTasks ?? globalTrackTasks;\n\n const trackEventsCallbackRef = useRef<\n ((e: ConnectionEvent<S>) => void) | undefined\n >();\n const trackConnectedUsersCallbackRef = useRef<\n ((users: MultiplayerUser[], userId: string) => void) | undefined\n >();\n const trackTasksCallbackRef = useRef<((tasks: Task[]) => void) | undefined>();\n\n useEffect(() => {\n trackEventsCallbackRef.current = shouldTrackEvents\n ? (e) =>\n setConnectionEvents((events) => {\n const updated = events ? [...events, e] : [e];\n return updated.length > 50 ? updated.slice(-50) : updated;\n })\n : undefined;\n }, [shouldTrackEvents]);\n\n useEffect(() => {\n trackConnectedUsersCallbackRef.current = (users, userId) => {\n setConnectedUsers(users);\n setUserId(userId);\n };\n }, []);\n\n useEffect(() => {\n trackTasksCallbackRef.current = shouldTrackTasks\n ? (tasks) => {\n setTasks((old) => {\n const newIds = new Set(tasks.map((t) => t.id));\n\n // merge old and new tasks, preferring new tasks\n const unchanged = old.filter((t) => !newIds.has(t.id));\n\n return [...unchanged, ...tasks];\n });\n }\n : undefined;\n }, [shouldTrackTasks]);\n\n useEffect(() => {\n if (syncRef.current) {\n return syncRef.current({\n ms: multiplayerStateManager,\n onConnectionEvent: (e) => trackEventsCallbackRef.current?.(e),\n onChangeConnectedUsers: (users, userId) =>\n trackConnectedUsersCallbackRef.current?.(users, userId),\n onChangeTasks: (tasks) => trackTasksCallbackRef.current?.(tasks),\n });\n }\n }, [multiplayerStateManager]);\n\n const state = useSyncMultiplayerStateManager(multiplayerStateManager);\n\n const forceInit = useCallback(() => {\n const state =\n typeof initialState === \"function\" ? initialState() : initialState;\n const schema = options?.schema;\n multiplayerStateManager.sm.clearHistory();\n multiplayerStateManager.sendInit({ force: true, state, schema });\n }, [initialState, multiplayerStateManager, options?.schema]);\n\n useStateInspector({\n multiplayerStateManager,\n state,\n connectionEvents,\n connectedUsers,\n tasks,\n userId,\n forceInit,\n disabled: !options?.inspector,\n ...(typeof options?.inspector === \"object\" && options.inspector),\n });\n\n return [\n state,\n multiplayerStateManager.setState,\n multiplayerStateManager,\n extras,\n ] as const;\n}\n","import { Emitter } from \"@noya-app/emitter\";\nimport { useEffect, useState } from \"react\";\n\nclass Observable<T> {\n private value: T;\n private emitter = new Emitter<[T]>();\n\n constructor(value: T) {\n this.value = value;\n }\n\n get() {\n return this.value;\n }\n\n set(value: T) {\n if (this.value === value) return;\n\n this.value = value;\n\n this.emitter.emit(value);\n }\n\n listen(callback: (value: T) => void) {\n callback(this.value);\n\n return this.emitter.addListener(callback);\n }\n}\n\nexport function useObservable<T>(observable: Observable<T>) {\n const [value, setValue] = useState(observable.get());\n\n useEffect(() => {\n return observable.listen(setValue);\n }, [observable]);\n\n return value;\n}\n\nexport const shouldTrackEvents$ = new Observable(false);\nexport const shouldTrackTasks$ = new Observable(false);\n","\"use client\";\n\nimport {\n ConnectionEvent,\n MultiplayerStateManager,\n MultiplayerUser,\n Task,\n} from \"@noya-app/state-manager\";\nimport React, { ComponentProps, useLayoutEffect } from \"react\";\nimport { createRoot } from \"react-dom/client\";\n\nimport { Root } from \"react-dom/client\";\nimport { StateInspector } from \"./StateInspector\";\n\nfunction createPortalElement() {\n const el = document.createElement(\"div\");\n el.id = \"noya-multiplayer-react-inspector-portal\";\n return el;\n}\n\nexport function useStateInspector<S extends object, M>({\n state,\n connectionEvents,\n connectedUsers,\n tasks,\n userId,\n disabled = false,\n colorScheme,\n anchor,\n multiplayerStateManager,\n forceInit,\n}: {\n state: S;\n connectionEvents?: ConnectionEvent<S>[];\n connectedUsers?: MultiplayerUser[];\n tasks?: Task[];\n userId?: string;\n disabled: boolean;\n multiplayerStateManager: MultiplayerStateManager<S, M>;\n forceInit: () => void;\n} & Pick<ComponentProps<typeof StateInspector>, \"colorScheme\" | \"anchor\">) {\n const [root, setRoot] = React.useState<Root | null>(null);\n\n useLayoutEffect(() => {\n if (disabled) return;\n\n const portalElement = createPortalElement();\n\n const root = createRoot(portalElement);\n\n document.body.appendChild(portalElement);\n\n setRoot(root);\n\n return () => {\n document.body.removeChild(portalElement);\n };\n }, [disabled]);\n\n useLayoutEffect(() => {\n if (!root) return;\n\n root.render(\n <StateInspector\n state={state}\n connectionEvents={connectionEvents}\n connectedUsers={connectedUsers}\n tasks={tasks}\n userId={userId}\n colorScheme={colorScheme}\n anchor={anchor}\n forceInit={forceInit}\n multiplayerStateManager={\n multiplayerStateManager as MultiplayerStateManager<any, any>\n }\n />\n );\n }, [\n anchor,\n colorScheme,\n connectedUsers,\n connectionEvents,\n root,\n state,\n multiplayerStateManager,\n userId,\n tasks,\n forceInit,\n ]);\n}\n","\"use client\";\n\nimport {\n ConnectionEvent,\n ExtendedPathKey,\n MultiplayerStateManager,\n MultiplayerUser,\n Task,\n} from \"@noya-app/state-manager\";\nimport React, {\n CSSProperties,\n ComponentPropsWithoutRef,\n FC,\n memo,\n useEffect,\n useLayoutEffect,\n} from \"react\";\nimport {\n ObjectInspector,\n ObjectLabel,\n ObjectName,\n ObjectPreview,\n chromeDark,\n chromeLight,\n} from \"react-inspector\";\nimport { shouldTrackEvents$, shouldTrackTasks$ } from \"../globals\";\nimport { useManagedHistory } from \"../hooks\";\nimport { useLocalStorageState } from \"./useLocalStorageState\";\n\nconst lightTheme = {\n ...chromeLight,\n BASE_BACKGROUND_COLOR: \"transparent\",\n OBJECT_NAME_COLOR: \"rgba(0,0,0,0.7)\",\n} as any;\n\nconst darkTheme = {\n ...chromeDark,\n BASE_BACKGROUND_COLOR: \"transparent\",\n OBJECT_NAME_COLOR: \"rgba(255,255,255,0.7)\",\n} as any;\n\nconst styles = {\n sectionInner: {\n flex: \"1 1 0\",\n overflowY: \"auto\",\n overflowX: \"hidden\",\n display: \"flex\",\n flexDirection: \"column\",\n },\n} as const;\n\nfunction ToggleButton({\n showInspector,\n setShowInspector,\n theme,\n anchor,\n}: {\n showInspector: boolean;\n setShowInspector: (value: boolean) => void;\n theme: typeof chromeLight;\n anchor: Anchor;\n}) {\n return (\n <span\n role=\"button\"\n style={{\n flex: \"0\",\n appearance: \"none\",\n color: theme.BASE_COLOR,\n border: \"none\",\n fontSize: \"12px\",\n whiteSpace: \"nowrap\",\n display: \"inline-flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n padding: \"2px 0\",\n }}\n onClick={(event) => {\n event.stopPropagation();\n\n setShowInspector(!showInspector);\n }}\n >\n <span style={{ width: \"12px\", height: \"12px\" }}>\n {showInspector === (anchor === \"right\") ? (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n strokeWidth={1.5}\n stroke=\"currentColor\"\n className=\"size-6\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n d=\"m5.25 4.5 7.5 7.5-7.5 7.5m6-15 7.5 7.5-7.5 7.5\"\n />\n </svg>\n ) : (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n strokeWidth={1.5}\n stroke=\"currentColor\"\n className=\"size-6\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n d=\"m18.75 4.5-7.5 7.5 7.5 7.5m-6-15L5.25 12l7.5 7.5\"\n />\n </svg>\n )}\n </span>\n </span>\n );\n}\n\nfunction DisclosureSection({\n open,\n setOpen,\n title,\n right,\n children,\n colorScheme,\n isFirst,\n style,\n}: {\n open: boolean;\n setOpen?: (value: boolean) => void;\n title: string;\n right?: React.ReactNode;\n children: React.ReactNode;\n colorScheme: \"light\" | \"dark\";\n isFirst?: boolean;\n style?: CSSProperties;\n}) {\n const theme = colorScheme === \"light\" ? lightTheme : darkTheme;\n\n const borderColor =\n colorScheme === \"light\" ? \"rgba(0,0,0,0.1)\" : \"rgba(255,255,255,0.1)\";\n\n return (\n <div\n style={{\n flex: open ? \"1 1 0\" : \"0\",\n display: \"flex\",\n flexDirection: \"column\",\n ...style,\n }}\n >\n <div\n onClick={() => setOpen?.(!open)}\n style={{\n cursor: \"default\",\n fontSize: \"12px\",\n padding: \"4px 10px\",\n display: \"flex\",\n alignItems: \"center\",\n ...(!isFirst && { borderTop: `1px solid ${borderColor}` }),\n ...(open && { borderBottom: `1px solid ${borderColor}` }),\n }}\n >\n {setOpen && (\n <Arrow\n expanded={open}\n style={{\n fontSize: theme.ARROW_FONT_SIZE,\n marginRight: theme.ARROW_MARGIN_RIGHT + 1,\n color: theme.ARROW_COLOR,\n }}\n />\n )}\n <span style={{ flex: \"1 1 0\", userSelect: \"none\" }}>{title}</span>\n {right}\n </div>\n {open && children}\n </div>\n );\n}\n\nfunction InspectorRow({\n children,\n colorScheme,\n selected,\n style,\n variant,\n}: {\n children: React.ReactNode;\n colorScheme: \"light\" | \"dark\";\n selected?: boolean;\n style?: CSSProperties;\n variant?: \"up\" | \"down\";\n}) {\n const solidBorderColor =\n colorScheme === \"light\" ? \"rgb(223 223 223)\" : \"rgb(29 29 29)\";\n\n return (\n <div\n style={{\n borderBottom: `1px solid ${solidBorderColor}`,\n fontSize: \"12px\",\n fontFamily: \"Menlo, monospace\",\n padding: \"2px 12px 1px\",\n display: \"flex\",\n alignItems: \"center\",\n background:\n variant === \"up\"\n ? \"rgba(0,255,0,0.1)\"\n : variant === \"down\"\n ? \"rgba(255,0,0,0.1)\"\n : selected\n ? \"rgb(59 130 246 / 15%)\"\n : undefined,\n // background:\n // colorScheme === \"light\"\n // ? \"rgba(0,0,0,0.05)\"\n // : \"rgba(255,255,255,0.05)\",\n ...style,\n }}\n >\n <span\n style={{\n fontFamily: \"Menlo, monospace\",\n fontSize: \"11px\",\n borderRadius: 4,\n display: \"inline-block\",\n }}\n >\n {children}\n </span>\n </div>\n );\n}\n\nconst HISTORY_ELEMENT_PREFIX = \"noya-multiplayer-history-\";\n\ntype Anchor = \"left\" | \"right\" | \"bottom left\" | \"bottom right\";\n\nexport const StateInspector = memo(function StateInspector<\n S extends object,\n M,\n>({\n state,\n connectionEvents,\n connectedUsers,\n tasks,\n userId,\n unstyled,\n colorScheme = \"light\",\n multiplayerStateManager,\n anchor = \"right\",\n forceInit,\n ...props\n}: {\n state: S;\n connectionEvents?: ConnectionEvent<S>[];\n connectedUsers?: MultiplayerUser[];\n tasks?: Task[];\n userId?: string;\n unstyled?: boolean;\n colorScheme?: \"light\" | \"dark\";\n multiplayerStateManager: MultiplayerStateManager<S, M>;\n anchor?: Anchor;\n forceInit: () => void;\n} & ComponentPropsWithoutRef<\"div\">) {\n const [didMount, setDidMount] = React.useState(false);\n\n useLayoutEffect(() => {\n setDidMount(true);\n }, []);\n\n const eventsContainerRef = React.useRef<HTMLDivElement>(null);\n const historyContainerRef = React.useRef<HTMLDivElement>(null);\n\n const [showInspector, setShowInspector] = useLocalStorageState(\n \"noya-multiplayer-react-show-inspector\",\n true\n );\n const [showEvents, setShowEvents] = useLocalStorageState(\n \"noya-multiplayer-react-show-events\",\n false\n );\n const [showHistory, setShowHistory] = useLocalStorageState(\n \"noya-multiplayer-react-show-history\",\n false\n );\n const [showUsers, setShowUsers] = useLocalStorageState(\n \"noya-multiplayer-react-show-users\",\n true\n );\n const [showData, setShowData] = useLocalStorageState(\n \"noya-multiplayer-react-show-data\",\n true\n );\n const [showTasks, setShowTasks] = useLocalStorageState(\n \"noya-multiplayer-react-show-tasks\",\n false\n );\n\n useEffect(() => {\n shouldTrackEvents$.set(showEvents);\n }, [showEvents]);\n\n useEffect(() => {\n shouldTrackTasks$.set(showTasks);\n }, [showTasks]);\n\n useEffect(() => {\n if (eventsContainerRef.current) {\n eventsContainerRef.current.scrollTop =\n eventsContainerRef.current.scrollHeight;\n }\n }, [connectionEvents]);\n\n const historySnapshot = useManagedHistory(multiplayerStateManager.sm);\n\n useEffect(() => {\n if (historyContainerRef.current) {\n historyContainerRef.current.scrollTop =\n historyContainerRef.current.scrollHeight;\n }\n }, [historySnapshot]);\n\n // If history index changes, scroll to the new history entry into view\n useEffect(() => {\n if (!historyContainerRef.current) return;\n\n const historyEntry = historyContainerRef.current.querySelector(\n `#${HISTORY_ELEMENT_PREFIX}${historySnapshot.historyIndex}`\n );\n\n if (historyEntry) {\n historyEntry.scrollIntoView({\n block: \"nearest\",\n inline: \"nearest\",\n });\n }\n }, [historySnapshot.historyIndex]);\n\n const theme = colorScheme === \"light\" ? lightTheme : darkTheme;\n // const borderColor =\n // colorScheme === \"light\" ? \"rgba(0,0,0,0.1)\" : \"rgba(255,255,255,0.1)\";\n const solidBorderColor =\n colorScheme === \"light\" ? \"rgb(223 223 223)\" : \"rgb(29 29 29)\";\n\n const baseStyle: CSSProperties = {\n position: \"fixed\",\n top: 12,\n ...(anchor.includes(\"right\") ? { right: 12 } : { left: 12 }),\n bottom: 12,\n width: 400,\n background:\n colorScheme === \"light\" ? \"rgba(255,255,255,0.95)\" : \"rgba(0,0,0,0.95)\",\n backdropFilter: \"blur(48px)\",\n border: `1px solid ${solidBorderColor}`,\n // outlineOffset: -1,\n borderRadius: 4,\n display: \"flex\",\n flexDirection: \"column\",\n // gap: 12,\n color: theme.BASE_COLOR,\n overflow: \"hidden\",\n zIndex: 9999,\n lineHeight: \"13px\",\n };\n\n if (!didMount) return null;\n\n if (!showInspector) {\n return (\n <div\n {...props}\n style={{\n ...baseStyle,\n padding: \"4px 10px\",\n width: undefined,\n ...(anchor.includes(\"bottom\")\n ? { bottom: 12, top: undefined }\n : { bottom: undefined }),\n }}\n onClick={() => setShowInspector(true)}\n >\n <ToggleButton\n showInspector={showInspector}\n setShowInspector={setShowInspector}\n theme={theme}\n anchor={anchor}\n />\n </div>\n );\n }\n\n return (\n <div\n {...props}\n style={{\n ...(!unstyled && baseStyle),\n ...props.style,\n }}\n >\n <DisclosureSection\n isFirst\n open={showUsers}\n setOpen={setShowUsers}\n title=\"Users\"\n colorScheme={colorScheme}\n style={{\n flex: \"0 0 auto\",\n maxHeight: \"200px\",\n }}\n right={\n <ToggleButton\n showInspector={showInspector}\n setShowInspector={setShowInspector}\n theme={theme}\n anchor={anchor}\n />\n }\n >\n <div style={{ ...styles.sectionInner, flex: \"0 0 auto\" }}>\n {connectedUsers?.map((user) => (\n <InspectorRow\n key={user.id}\n colorScheme={colorScheme}\n variant={user.id === userId ? \"up\" : undefined}\n >\n {user.name} ({ellipsis(user.id.toString(), 8, \"middle\")})\n </InspectorRow>\n ))}\n {!connectedUsers && (\n <div\n style={{\n padding: \"12px\",\n fontSize: \"12px\",\n display: \"flex\",\n flexDirection: \"column\",\n gap: \"4px\",\n }}\n >\n <span>No connected users</span>\n </div>\n )}\n </div>\n </DisclosureSection>\n <DisclosureSection\n title=\"Data\"\n colorScheme={colorScheme}\n setOpen={setShowData}\n open={showData}\n right={\n <span\n style={{\n display: \"flex\",\n gap: \"4px\",\n }}\n >\n <SmallButton\n // disabled={!historySnapshot.canUndo}\n theme={theme}\n onClick={() => {\n forceInit();\n }}\n >\n Reset\n </SmallButton>\n </span>\n }\n >\n <div style={styles.sectionInner}>\n <InspectorRow colorScheme={colorScheme}>\n <ObjectInspector\n name={multiplayerStateManager.schema ? \"state\" : undefined}\n data={state}\n theme={theme}\n />\n </InspectorRow>\n {multiplayerStateManager.schema && (\n <InspectorRow colorScheme={colorScheme}>\n <ObjectInspector\n name=\"schema\"\n data={multiplayerStateManager.schema}\n theme={theme}\n />\n </InspectorRow>\n )}\n </div>\n </DisclosureSection>\n <DisclosureSection\n open={showHistory}\n setOpen={setShowHistory}\n title=\"History\"\n colorScheme={colorScheme}\n right={\n <span\n style={{\n display: \"flex\",\n gap: \"4px\",\n }}\n >\n <SmallButton\n disabled={!historySnapshot.canUndo}\n theme={theme}\n onClick={() => {\n multiplayerStateManager.undo();\n }}\n >\n <span style={{ width: \"12px\", height: \"12px\" }}>\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"1em\"\n height=\"1em\"\n viewBox=\"0 0 20 20\"\n >\n <path\n fill=\"currentColor\"\n 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\"\n />\n </svg>\n </span>\n </SmallButton>\n <SmallButton\n disabled={!historySnapshot.canRedo}\n theme={theme}\n onClick={() => {\n multiplayerStateManager.redo();\n }}\n >\n <span style={{ width: \"12px\", height: \"12px\" }}>\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"1em\"\n height=\"1em\"\n viewBox=\"0 0 20 20\"\n >\n <path\n fill=\"currentColor\"\n 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\"\n />\n </svg>\n </span>\n </SmallButton>\n </span>\n }\n >\n <div ref={historyContainerRef} style={styles.sectionInner}>\n <div\n id={`${HISTORY_ELEMENT_PREFIX}0`}\n style={{\n borderBottom: `1px solid ${solidBorderColor}`,\n fontSize: \"12px\",\n fontFamily: \"Menlo, monospace\",\n padding: \"2px 12px 1px\",\n display: \"flex\",\n alignItems: \"center\",\n background:\n historySnapshot.historyIndex === 0\n ? \"rgb(59 130 246 / 15%)\"\n : undefined,\n }}\n >\n <span\n style={{\n fontFamily: \"Menlo, monospace\",\n fontSize: \"11px\",\n borderRadius: 4,\n display: \"inline-block\",\n }}\n >\n Initial state\n </span>\n </div>\n {historySnapshot.history.map((entry, index) => (\n <div\n id={`${HISTORY_ELEMENT_PREFIX}${index + 1}`}\n key={index}\n style={{\n padding: \"0px 12px 1px\",\n borderBottom: `1px solid ${solidBorderColor}`,\n background:\n index + 1 === historySnapshot.historyIndex\n ? \"rgb(59 130 246 / 15%)\"\n : undefined,\n }}\n >\n {entry.redoPatches?.map((patch, j) => (\n <pre\n key={j}\n style={{\n fontSize: \"11px\",\n display: \"flex\",\n flexWrap: \"wrap\",\n }}\n >\n {patch.op === \"add\" && (\n <>\n <span style={{ fontStyle: \"italic\" }}>\n {pathToString(patch.path)}\n </span>\n {\" = \"}\n <ObjectInspector data={patch.value} theme={theme} />\n </>\n )}\n {patch.op === \"replace\" && (\n <>\n <span style={{ fontStyle: \"italic\" }}>\n {pathToString(patch.path)}\n </span>\n {\" = \"}\n <ObjectInspector data={patch.value} theme={theme} />\n </>\n )}\n {patch.op === \"remove\" && (\n <>\n <span style={{ color: theme.OBJECT_VALUE_STRING_COLOR }}>\n delete{\" \"}\n </span>\n <span style={{ fontStyle: \"italic\" }}>\n {pathToString(patch.path)}\n </span>\n </>\n )}\n {patch.op === \"move\" && (\n <>\n <span style={{ fontStyle: \"italic\" }}>\n {pathToString(patch.from!)}\n </span>\n {\" -> \"}\n <span style={{ fontStyle: \"italic\" }}>\n {pathToString(patch.path)}\n </span>\n </>\n )}\n </pre>\n ))}\n </div>\n ))}\n </div>\n </DisclosureSection>\n <DisclosureSection\n title=\"Tasks\"\n colorScheme={colorScheme}\n open={showTasks}\n setOpen={setShowTasks}\n >\n <div style={styles.sectionInner}>\n {tasks?.map((task) => (\n <InspectorRow\n key={task.id}\n colorScheme={colorScheme}\n style={{\n backgroundColor:\n task.status === \"done\"\n ? \"rgba(0,255,0,0.2)\"\n : task.status === \"error\"\n ? \"rgba(255,0,0,0.2)\"\n : undefined,\n }}\n >\n <ObjectInspector\n name={task.name}\n data={task.payload}\n theme={theme}\n />\n </InspectorRow>\n ))}\n </div>\n </DisclosureSection>\n <DisclosureSection\n open={showEvents}\n setOpen={setShowEvents}\n title=\"Events\"\n colorScheme={colorScheme}\n >\n <div ref={eventsContainerRef} style={styles.sectionInner}>\n {connectionEvents?.map((event, index) =>\n event.type === \"stateChange\" ? (\n <InspectorRow key={index} colorScheme={colorScheme}>\n connection:{\" \"}\n <span style={{ fontStyle: \"italic\" }}>\n {event.state.toLowerCase()}\n </span>\n </InspectorRow>\n ) : (\n <InspectorRow\n key={index}\n colorScheme={colorScheme}\n variant={event.type === \"send\" ? \"up\" : \"down\"}\n style={{\n padding: \"0px 12px 1px\",\n }}\n >\n <ObjectInspector\n data={event.message}\n theme={theme}\n nodeRenderer={({\n depth,\n name,\n data,\n isNonenumerable,\n expanded,\n }: any) => {\n const direction = event.type === \"send\" ? \"up\" : \"down\";\n\n return depth === 0 ? (\n <ObjectRootLabel direction={direction} data={data} />\n ) : (\n <ObjectLabel\n direction={direction}\n name={name}\n data={data}\n isNonenumerable={isNonenumerable}\n />\n );\n }}\n />\n </InspectorRow>\n )\n )}\n {!connectionEvents && (\n <div\n style={{\n padding: \"12px\",\n fontSize: \"12px\",\n display: \"flex\",\n flexDirection: \"column\",\n gap: \"4px\",\n }}\n >\n <span>No recorded events</span>\n </div>\n )}\n </div>\n </DisclosureSection>\n </div>\n );\n});\n\ntype MessageDirection = \"up\" | \"down\";\n\nconst ObjectRootLabel: FC<{\n name?: string;\n data: any;\n direction?: MessageDirection;\n}> = ({ name, data, direction }) => {\n if (typeof name === \"string\") {\n return (\n <span>\n <ObjectName name={name} />\n <span>: </span>\n <ObjectPreview data={data} />\n </span>\n );\n }\n if (direction === \"up\" || direction === \"down\") {\n const arrow = direction === \"up\" ? \"↑\" : \"↓\";\n\n return (\n <span>\n <span\n style={{\n background:\n direction === \"up\" ? \"rgba(0,255,0,0.2)\" : \"rgba(255,0,0,0.2)\",\n // color: \"white\",\n width: 12,\n height: 12,\n borderRadius: 2,\n display: \"inline-flex\",\n justifyContent: \"center\",\n alignItems: \"center\",\n marginRight: 4,\n lineHeight: \"12px\",\n }}\n >\n {arrow}\n </span>\n <ObjectPreview data={data} />\n </span>\n );\n } else {\n return <ObjectPreview data={data} />;\n }\n};\n\n// Rotates ▶️ or ▼ based on expanded state\nfunction Arrow({\n expanded,\n onClick,\n style,\n}: {\n expanded: boolean;\n onClick?: () => void;\n style?: CSSProperties;\n}) {\n return (\n <span\n role=\"button\"\n onClick={onClick}\n style={{\n display: \"inline-block\",\n textAlign: \"center\",\n transform: expanded ? \"rotate(0deg)\" : \"rotate(-90deg)\",\n fontFamily: \"Menlo, monospace\",\n ...style,\n }}\n >\n {\"▼\"}\n </span>\n );\n}\n\nfunction pathToString(extendedPath: ExtendedPathKey[]) {\n return extendedPath\n .map((key) =>\n typeof key === \"object\"\n ? `:${ellipsis(key.id.toString(), 8, \"middle\")}`\n : key\n )\n .map((key, index) =>\n index === 0 || (typeof key === \"string\" && key.startsWith(\":\"))\n ? key\n : `.${key}`\n )\n .join(\"\");\n}\n\ntype EllipsisPosition = \"start\" | \"middle\" | \"end\";\n\nfunction ellipsis(\n str: string,\n maxLength: number,\n position: EllipsisPosition = \"end\"\n) {\n if (str.length <= maxLength) return str;\n\n switch (position) {\n case \"start\":\n return `…${str.slice(str.length - maxLength)}`;\n case \"middle\":\n const halfLength = Math.floor(maxLength / 2);\n return `${str.slice(0, halfLength)}…${str.slice(str.length - halfLength)}`;\n case \"end\":\n return `${str.slice(0, maxLength)}…`;\n }\n}\n\nfunction SmallButton({\n children,\n onClick,\n style,\n theme,\n disabled,\n}: {\n children: React.ReactNode;\n onClick: () => void;\n style?: CSSProperties;\n theme: typeof chromeLight;\n disabled?: boolean;\n}) {\n return (\n <button\n type=\"button\"\n disabled={disabled}\n onClick={(event) => {\n event.stopPropagation();\n onClick();\n }}\n style={{\n flex: \"0\",\n appearance: \"none\",\n background: \"none\",\n color: theme.BASE_COLOR,\n opacity: disabled ? 0.25 : 1,\n border: \"none\",\n fontSize: \"12px\",\n whiteSpace: \"nowrap\",\n display: \"inline-flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n padding: \"2px 0\",\n cursor: \"pointer\",\n ...style,\n }}\n >\n {children}\n </button>\n );\n}\n","\"use client\";\n\nimport React from \"react\";\n\nconst localStorage = typeof window !== \"undefined\" ? window.localStorage : null;\n\nexport function useLocalStorageState<S>(\n key: string,\n defaultValue: S\n): [S, (value: S) => void] {\n const [state, setState] = React.useState<S>(() => {\n const value = localStorage?.getItem(key);\n let result = defaultValue;\n if (value) {\n try {\n result = JSON.parse(value);\n } catch (error) {\n console.error(\"Error parsing localStorage value\", error);\n }\n }\n return result;\n });\n\n const setLocalStorageState = (value: S) => {\n localStorage?.setItem(key, JSON.stringify(value));\n setState(value);\n };\n\n return [state, setLocalStorageState];\n}\n"],"mappings":"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","names":["useEffect","useState","React","useLayoutEffect","React","useEffect","React","StateInspector","useEffect","React","useLayoutEffect","root","useState","useEffect","userId","tasks","state"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/hooks.ts","../src/globals.ts","../src/inspector/useStateInspector.tsx","../src/inspector/StateInspector.tsx","../src/inspector/useLocalStorageState.tsx"],"sourcesContent":["export * from \"@noya-app/state-manager\";\nexport * from \"./hooks\";\nexport * from \"./inspector/StateInspector\";\n","\"use client\";\n\nimport {\n ActionHandler,\n ConnectionEvent,\n EphemeralUserData,\n Get,\n MultiplayerStateManager,\n MultiplayerStateManagerOptions,\n MultiplayerUser,\n StateManager,\n StateManagerOptions,\n SyncAdapter,\n Task,\n stubSync,\n} from \"@noya-app/state-manager\";\nimport {\n ComponentProps,\n SetStateAction,\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n useSyncExternalStore,\n} from \"react\";\nimport {\n shouldTrackEvents$,\n shouldTrackTasks$,\n useObservable,\n} from \"./globals\";\nimport { StateInspector } from \"./inspector/StateInspector\";\nimport { useStateInspector } from \"./inspector/useStateInspector\";\n\nexport function useSyncStateManager<S extends object, M>(\n stateManager: StateManager<S, M>\n): S;\nexport function useSyncStateManager<S extends object, M, P extends string>(\n stateManager: StateManager<S, M>,\n path: P\n): Get<S, P, unknown>;\nexport function useSyncStateManager<S extends object, M>(\n stateManager: StateManager<S, M>,\n path?: string\n) {\n const getSnapshot = useCallback(\n () =>\n typeof path === \"string\"\n ? stateManager.getState(path)\n : stateManager.getState(),\n [stateManager, path]\n );\n\n return useSyncExternalStore(\n stateManager.addListener,\n getSnapshot,\n getSnapshot\n );\n}\n\nexport function useManagedState<S extends object, M = void>(\n createInitialState: () => S,\n options?: StateManagerOptions<S, M>\n) {\n const [stateManager] = useState(\n () => new StateManager<S, M>(createInitialState(), options)\n );\n\n const state = useSyncStateManager(stateManager);\n\n const setState = useCallback(\n (\n ...args: M extends {}\n ? [metadata: M, action: SetStateAction<S>]\n : [action: SetStateAction<S>]\n ) => {\n const [metadata, action] =\n args.length === 1 ? [undefined, args[0]] : args;\n\n if (metadata === undefined) {\n const performActionNoMetadata = stateManager.performAction as (\n handler: ActionHandler<S>\n ) => void;\n\n if (typeof action === \"function\") {\n performActionNoMetadata({\n run: action,\n undo: () => state,\n });\n } else {\n performActionNoMetadata({\n run: () => action,\n undo: () => state,\n });\n }\n } else {\n const performActionWithMetadata = stateManager.performAction as (\n metadata: M,\n handler: ActionHandler<S>\n ) => void;\n\n if (typeof action === \"function\") {\n performActionWithMetadata(metadata, {\n run: action,\n undo: () => state,\n });\n } else {\n performActionWithMetadata(metadata, {\n run: () => action,\n undo: () => state,\n });\n }\n }\n },\n [state, stateManager]\n );\n\n return [state, setState, stateManager] as const;\n}\n\nexport function useManagedHistory<S extends object, M = void>(\n stateManager: StateManager<S, M>\n) {\n return useSyncExternalStore(\n stateManager.historyEmittor.addListener,\n stateManager.getHistorySnapshot,\n stateManager.getHistorySnapshot\n );\n}\n\nexport function useSyncMultiplayerStateManager<S extends object, M = void>(\n multiplayerStateManager: MultiplayerStateManager<S, M>\n) {\n return useSyncExternalStore(\n multiplayerStateManager.addListener,\n multiplayerStateManager.getOptimisticState,\n multiplayerStateManager.getOptimisticState\n );\n}\n\nexport function useEphemeralUserData<E>(\n ephemeralUserData: EphemeralUserData<E>\n) {\n return useSyncExternalStore(\n ephemeralUserData.addListener,\n ephemeralUserData.getSnapshot,\n ephemeralUserData.getSnapshot\n );\n}\n\nconst defaultStubSync = stubSync();\n\nexport function useMultiplayerState<S extends object, M = void, E = void>(\n initialState: S | (() => S),\n options?: MultiplayerStateManagerOptions<S, M> & {\n sync?: (options: {\n ms: MultiplayerStateManager<S, M>;\n ephemeralUserData: EphemeralUserData<E>;\n }) => void;\n trackConnectionEvents?: boolean;\n trackTasks?: boolean;\n inspector?:\n | boolean\n | Pick<ComponentProps<typeof StateInspector>, \"colorScheme\" | \"anchor\">;\n }\n) {\n const { sync = defaultStubSync as unknown as SyncAdapter<S, M, E>, ...rest } =\n options ?? {};\n\n const [multiplayerStateManager] = useState(\n () => new MultiplayerStateManager<S, M>(initialState, rest)\n );\n const syncRef = useRef(sync);\n\n const [connectionEvents, setConnectionEvents] = useState<\n ConnectionEvent<S>[] | undefined\n >();\n\n const [connectedUsers, setConnectedUsers] = useState<MultiplayerUser[]>([]);\n const [ephemeralUserData] = useState<EphemeralUserData<E>>(\n () => new EphemeralUserData()\n );\n const [tasks, setTasks] = useState<Task[]>([]);\n const [userId, setUserId] = useState<string | undefined>();\n\n const extras = useMemo(() => {\n return {\n tasks,\n connectionEvents,\n connectedUsers,\n ephemeralUserData,\n // evaluate: async (code: string) => {\n // const payload = await rpcManager.request({ type: \"eval\", code });\n\n // if (payload.type !== \"eval\") {\n // throw new Error(`Unexpected rpc response type: ${payload.type}`);\n // }\n // },\n };\n }, [tasks, connectionEvents, connectedUsers, ephemeralUserData]);\n\n const globalTrackEvents = useObservable(shouldTrackEvents$);\n const globalTrackTasks = useObservable(shouldTrackTasks$);\n\n const shouldTrackEvents = options?.trackConnectionEvents ?? globalTrackEvents;\n const shouldTrackTasks = options?.trackTasks ?? globalTrackTasks;\n\n const trackEventsCallbackRef = useRef<\n ((e: ConnectionEvent<S>) => void) | undefined\n >();\n const trackConnectedUsersCallbackRef = useRef<\n ((users: MultiplayerUser[], userId: string) => void) | undefined\n >();\n const trackTasksCallbackRef = useRef<((tasks: Task[]) => void) | undefined>();\n\n useEffect(() => {\n trackEventsCallbackRef.current = shouldTrackEvents\n ? (e) =>\n setConnectionEvents((events) => {\n const updated = events ? [...events, e] : [e];\n return updated.length > 50 ? updated.slice(-50) : updated;\n })\n : undefined;\n }, [shouldTrackEvents]);\n\n useEffect(() => {\n trackConnectedUsersCallbackRef.current = (users, userId) => {\n setConnectedUsers(users);\n setUserId(userId);\n };\n }, []);\n\n useEffect(() => {\n trackTasksCallbackRef.current = shouldTrackTasks\n ? (tasks) => {\n setTasks((old) => {\n const newIds = new Set(tasks.map((t) => t.id));\n\n // merge old and new tasks, preferring new tasks\n const unchanged = old.filter((t) => !newIds.has(t.id));\n\n return [...unchanged, ...tasks];\n });\n }\n : undefined;\n }, [shouldTrackTasks]);\n\n useEffect(() => {\n if (syncRef.current) {\n return syncRef.current({\n ms: multiplayerStateManager,\n ephemeralUserData,\n onConnectionEvent: (e) => trackEventsCallbackRef.current?.(e),\n onChangeConnectedUsers: (users, userId) =>\n trackConnectedUsersCallbackRef.current?.(users, userId),\n onChangeTasks: (tasks) => trackTasksCallbackRef.current?.(tasks),\n });\n }\n }, [ephemeralUserData, multiplayerStateManager]);\n\n const state = useSyncMultiplayerStateManager(multiplayerStateManager);\n\n const forceInit = useCallback(() => {\n const state =\n typeof initialState === \"function\" ? initialState() : initialState;\n const schema = options?.schema;\n multiplayerStateManager.sm.clearHistory();\n multiplayerStateManager.sendInit({ force: true, state, schema });\n }, [initialState, multiplayerStateManager, options?.schema]);\n\n useStateInspector({\n ephemeralUserData,\n multiplayerStateManager,\n state,\n connectionEvents,\n connectedUsers,\n tasks,\n userId,\n forceInit,\n disabled: !options?.inspector,\n ...(typeof options?.inspector === \"object\" && options.inspector),\n });\n\n return [\n state,\n multiplayerStateManager.setState,\n multiplayerStateManager,\n extras,\n ] as const;\n}\n","import { Emitter } from \"@noya-app/emitter\";\nimport { useEffect, useState } from \"react\";\n\nclass Observable<T> {\n private value: T;\n private emitter = new Emitter<[T]>();\n\n constructor(value: T) {\n this.value = value;\n }\n\n get() {\n return this.value;\n }\n\n set(value: T) {\n if (this.value === value) return;\n\n this.value = value;\n\n this.emitter.emit(value);\n }\n\n listen(callback: (value: T) => void) {\n callback(this.value);\n\n return this.emitter.addListener(callback);\n }\n}\n\nexport function useObservable<T>(observable: Observable<T>) {\n const [value, setValue] = useState(observable.get());\n\n useEffect(() => {\n return observable.listen(setValue);\n }, [observable]);\n\n return value;\n}\n\nexport const shouldTrackEvents$ = new Observable(false);\nexport const shouldTrackTasks$ = new Observable(false);\n","\"use client\";\n\nimport {\n ConnectionEvent,\n EphemeralUserData,\n MultiplayerStateManager,\n MultiplayerUser,\n Task,\n} from \"@noya-app/state-manager\";\nimport React, { ComponentProps, useLayoutEffect } from \"react\";\nimport { createRoot } from \"react-dom/client\";\n\nimport { Root } from \"react-dom/client\";\nimport { StateInspector } from \"./StateInspector\";\n\nfunction createPortalElement() {\n const el = document.createElement(\"div\");\n el.id = \"noya-multiplayer-react-inspector-portal\";\n return el;\n}\n\nexport function useStateInspector<S extends object, M, E>({\n state,\n connectionEvents,\n connectedUsers,\n tasks,\n userId,\n disabled = false,\n colorScheme,\n anchor,\n multiplayerStateManager,\n ephemeralUserData,\n forceInit,\n}: {\n state: S;\n connectionEvents?: ConnectionEvent<S>[];\n connectedUsers?: MultiplayerUser[];\n tasks?: Task[];\n userId?: string;\n disabled: boolean;\n multiplayerStateManager: MultiplayerStateManager<S, M>;\n ephemeralUserData: EphemeralUserData<E>;\n forceInit: () => void;\n} & Pick<ComponentProps<typeof StateInspector>, \"colorScheme\" | \"anchor\">) {\n const [root, setRoot] = React.useState<Root | null>(null);\n\n useLayoutEffect(() => {\n if (disabled) return;\n\n const portalElement = createPortalElement();\n\n const root = createRoot(portalElement);\n\n document.body.appendChild(portalElement);\n\n setRoot(root);\n\n return () => {\n document.body.removeChild(portalElement);\n };\n }, [disabled]);\n\n useLayoutEffect(() => {\n if (!root) return;\n\n root.render(\n <StateInspector\n state={state}\n connectionEvents={connectionEvents}\n connectedUsers={connectedUsers}\n tasks={tasks}\n userId={userId}\n colorScheme={colorScheme}\n anchor={anchor}\n forceInit={forceInit}\n multiplayerStateManager={\n multiplayerStateManager as MultiplayerStateManager<any, any>\n }\n ephemeralUserData={ephemeralUserData as EphemeralUserData<any>}\n />\n );\n }, [\n anchor,\n colorScheme,\n connectedUsers,\n connectionEvents,\n root,\n state,\n multiplayerStateManager,\n userId,\n tasks,\n forceInit,\n ephemeralUserData,\n ]);\n}\n","\"use client\";\n\nimport {\n ConnectionEvent,\n EphemeralUserData,\n ExtendedPathKey,\n MultiplayerStateManager,\n MultiplayerUser,\n Task,\n} from \"@noya-app/state-manager\";\nimport React, {\n CSSProperties,\n ComponentPropsWithoutRef,\n FC,\n memo,\n useEffect,\n useLayoutEffect,\n} from \"react\";\nimport {\n ObjectInspector,\n ObjectLabel,\n ObjectName,\n ObjectPreview,\n chromeDark,\n chromeLight,\n} from \"react-inspector\";\nimport { shouldTrackEvents$, shouldTrackTasks$ } from \"../globals\";\nimport { useEphemeralUserData, useManagedHistory } from \"../hooks\";\nimport { useLocalStorageState } from \"./useLocalStorageState\";\n\nconst lightTheme = {\n ...chromeLight,\n BASE_BACKGROUND_COLOR: \"transparent\",\n OBJECT_NAME_COLOR: \"rgba(0,0,0,0.7)\",\n} as any;\n\nconst darkTheme = {\n ...chromeDark,\n BASE_BACKGROUND_COLOR: \"transparent\",\n OBJECT_NAME_COLOR: \"rgba(255,255,255,0.7)\",\n} as any;\n\nconst styles = {\n sectionInner: {\n flex: \"1 1 0\",\n overflowY: \"auto\",\n overflowX: \"hidden\",\n display: \"flex\",\n flexDirection: \"column\",\n },\n} as const;\n\nfunction ToggleButton({\n showInspector,\n setShowInspector,\n theme,\n anchor,\n}: {\n showInspector: boolean;\n setShowInspector: (value: boolean) => void;\n theme: typeof chromeLight;\n anchor: Anchor;\n}) {\n return (\n <span\n role=\"button\"\n style={{\n flex: \"0\",\n appearance: \"none\",\n color: theme.BASE_COLOR,\n border: \"none\",\n fontSize: \"12px\",\n whiteSpace: \"nowrap\",\n display: \"inline-flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n padding: \"2px 0\",\n }}\n onClick={(event) => {\n event.stopPropagation();\n\n setShowInspector(!showInspector);\n }}\n >\n <span style={{ width: \"12px\", height: \"12px\" }}>\n {showInspector === (anchor === \"right\") ? (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n strokeWidth={1.5}\n stroke=\"currentColor\"\n className=\"size-6\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n d=\"m5.25 4.5 7.5 7.5-7.5 7.5m6-15 7.5 7.5-7.5 7.5\"\n />\n </svg>\n ) : (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n strokeWidth={1.5}\n stroke=\"currentColor\"\n className=\"size-6\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n d=\"m18.75 4.5-7.5 7.5 7.5 7.5m-6-15L5.25 12l7.5 7.5\"\n />\n </svg>\n )}\n </span>\n </span>\n );\n}\n\nfunction DisclosureSection({\n open,\n setOpen,\n title,\n right,\n children,\n colorScheme,\n isFirst,\n style,\n}: {\n open: boolean;\n setOpen?: (value: boolean) => void;\n title: string;\n right?: React.ReactNode;\n children: React.ReactNode;\n colorScheme: \"light\" | \"dark\";\n isFirst?: boolean;\n style?: CSSProperties;\n}) {\n const theme = colorScheme === \"light\" ? lightTheme : darkTheme;\n\n const borderColor =\n colorScheme === \"light\" ? \"rgba(0,0,0,0.1)\" : \"rgba(255,255,255,0.1)\";\n\n return (\n <div\n style={{\n flex: open ? \"1 1 0\" : \"0\",\n display: \"flex\",\n flexDirection: \"column\",\n ...style,\n }}\n >\n <div\n onClick={() => setOpen?.(!open)}\n style={{\n cursor: \"default\",\n fontSize: \"12px\",\n padding: \"4px 10px\",\n display: \"flex\",\n alignItems: \"center\",\n ...(!isFirst && { borderTop: `1px solid ${borderColor}` }),\n ...(open && { borderBottom: `1px solid ${borderColor}` }),\n }}\n >\n {setOpen && (\n <Arrow\n expanded={open}\n style={{\n fontSize: theme.ARROW_FONT_SIZE,\n marginRight: theme.ARROW_MARGIN_RIGHT + 1,\n color: theme.ARROW_COLOR,\n }}\n />\n )}\n <span style={{ flex: \"1 1 0\", userSelect: \"none\" }}>{title}</span>\n {right}\n </div>\n {open && children}\n </div>\n );\n}\n\nfunction InspectorRow({\n children,\n colorScheme,\n selected,\n style,\n variant,\n}: {\n children: React.ReactNode;\n colorScheme: \"light\" | \"dark\";\n selected?: boolean;\n style?: CSSProperties;\n variant?: \"up\" | \"down\";\n}) {\n const solidBorderColor =\n colorScheme === \"light\" ? \"rgb(223 223 223)\" : \"rgb(29 29 29)\";\n\n return (\n <div\n style={{\n borderBottom: `1px solid ${solidBorderColor}`,\n fontSize: \"12px\",\n fontFamily: \"Menlo, monospace\",\n padding: \"2px 12px 1px\",\n display: \"flex\",\n alignItems: \"center\",\n background:\n variant === \"up\"\n ? \"rgba(0,255,0,0.1)\"\n : variant === \"down\"\n ? \"rgba(255,0,0,0.1)\"\n : selected\n ? \"rgb(59 130 246 / 15%)\"\n : undefined,\n // background:\n // colorScheme === \"light\"\n // ? \"rgba(0,0,0,0.05)\"\n // : \"rgba(255,255,255,0.05)\",\n ...style,\n }}\n >\n <span\n style={{\n fontFamily: \"Menlo, monospace\",\n fontSize: \"11px\",\n borderRadius: 4,\n display: \"inline-block\",\n }}\n >\n {children}\n </span>\n </div>\n );\n}\n\nconst HISTORY_ELEMENT_PREFIX = \"noya-multiplayer-history-\";\n\ntype Anchor = \"left\" | \"right\" | \"bottom left\" | \"bottom right\";\n\nexport const StateInspector = memo(function StateInspector<\n S extends object,\n M,\n E,\n>({\n state,\n connectionEvents,\n connectedUsers,\n tasks,\n userId,\n unstyled,\n colorScheme = \"light\",\n multiplayerStateManager,\n ephemeralUserData,\n anchor = \"right\",\n forceInit,\n ...props\n}: {\n state: S;\n connectionEvents?: ConnectionEvent<S>[];\n connectedUsers?: MultiplayerUser[];\n tasks?: Task[];\n userId?: string;\n unstyled?: boolean;\n colorScheme?: \"light\" | \"dark\";\n multiplayerStateManager: MultiplayerStateManager<S, M>;\n ephemeralUserData: EphemeralUserData<E>;\n anchor?: Anchor;\n forceInit: () => void;\n} & ComponentPropsWithoutRef<\"div\">) {\n const [didMount, setDidMount] = React.useState(false);\n\n useLayoutEffect(() => {\n setDidMount(true);\n }, []);\n\n const eventsContainerRef = React.useRef<HTMLDivElement>(null);\n const historyContainerRef = React.useRef<HTMLDivElement>(null);\n\n const [showInspector, setShowInspector] = useLocalStorageState(\n \"noya-multiplayer-react-show-inspector\",\n true\n );\n const [showEvents, setShowEvents] = useLocalStorageState(\n \"noya-multiplayer-react-show-events\",\n false\n );\n const [showHistory, setShowHistory] = useLocalStorageState(\n \"noya-multiplayer-react-show-history\",\n false\n );\n const [showUsers, setShowUsers] = useLocalStorageState(\n \"noya-multiplayer-react-show-users\",\n true\n );\n const [showData, setShowData] = useLocalStorageState(\n \"noya-multiplayer-react-show-data\",\n true\n );\n const [showTasks, setShowTasks] = useLocalStorageState(\n \"noya-multiplayer-react-show-tasks\",\n false\n );\n const [showEphemeral, setShowEphemeral] = useLocalStorageState(\n \"noya-multiplayer-react-show-ephemeral\",\n false\n );\n\n useEffect(() => {\n shouldTrackEvents$.set(showEvents);\n }, [showEvents]);\n\n useEffect(() => {\n shouldTrackTasks$.set(showTasks);\n }, [showTasks]);\n\n useEffect(() => {\n if (eventsContainerRef.current) {\n eventsContainerRef.current.scrollTop =\n eventsContainerRef.current.scrollHeight;\n }\n }, [connectionEvents]);\n\n const ephemeral = useEphemeralUserData(ephemeralUserData);\n const historySnapshot = useManagedHistory(multiplayerStateManager.sm);\n\n useEffect(() => {\n if (historyContainerRef.current) {\n historyContainerRef.current.scrollTop =\n historyContainerRef.current.scrollHeight;\n }\n }, [historySnapshot]);\n\n // If history index changes, scroll to the new history entry into view\n useEffect(() => {\n if (!historyContainerRef.current) return;\n\n const historyEntry = historyContainerRef.current.querySelector(\n `#${HISTORY_ELEMENT_PREFIX}${historySnapshot.historyIndex}`\n );\n\n if (historyEntry) {\n historyEntry.scrollIntoView({\n block: \"nearest\",\n inline: \"nearest\",\n });\n }\n }, [historySnapshot.historyIndex]);\n\n const theme = colorScheme === \"light\" ? lightTheme : darkTheme;\n // const borderColor =\n // colorScheme === \"light\" ? \"rgba(0,0,0,0.1)\" : \"rgba(255,255,255,0.1)\";\n const solidBorderColor =\n colorScheme === \"light\" ? \"rgb(223 223 223)\" : \"rgb(29 29 29)\";\n\n const baseStyle: CSSProperties = {\n position: \"fixed\",\n top: 12,\n ...(anchor.includes(\"right\") ? { right: 12 } : { left: 12 }),\n bottom: 12,\n width: 400,\n background:\n colorScheme === \"light\" ? \"rgba(255,255,255,0.95)\" : \"rgba(0,0,0,0.95)\",\n backdropFilter: \"blur(48px)\",\n border: `1px solid ${solidBorderColor}`,\n // outlineOffset: -1,\n borderRadius: 4,\n display: \"flex\",\n flexDirection: \"column\",\n // gap: 12,\n color: theme.BASE_COLOR,\n overflow: \"hidden\",\n zIndex: 9999,\n lineHeight: \"13px\",\n };\n\n if (!didMount) return null;\n\n if (!showInspector) {\n return (\n <div\n {...props}\n style={{\n ...baseStyle,\n padding: \"4px 10px\",\n width: undefined,\n ...(anchor.includes(\"bottom\")\n ? { bottom: 12, top: undefined }\n : { bottom: undefined }),\n }}\n onClick={() => setShowInspector(true)}\n >\n <ToggleButton\n showInspector={showInspector}\n setShowInspector={setShowInspector}\n theme={theme}\n anchor={anchor}\n />\n </div>\n );\n }\n\n return (\n <div\n {...props}\n style={{\n ...(!unstyled && baseStyle),\n ...props.style,\n }}\n >\n <DisclosureSection\n isFirst\n open={showUsers}\n setOpen={setShowUsers}\n title=\"Users\"\n colorScheme={colorScheme}\n style={{\n flex: \"0 0 auto\",\n maxHeight: \"200px\",\n }}\n right={\n <ToggleButton\n showInspector={showInspector}\n setShowInspector={setShowInspector}\n theme={theme}\n anchor={anchor}\n />\n }\n >\n <div style={{ ...styles.sectionInner, flex: \"0 0 auto\" }}>\n {connectedUsers?.map((user) => (\n <InspectorRow\n key={user.id}\n colorScheme={colorScheme}\n variant={user.id === userId ? \"up\" : undefined}\n >\n {user.name} ({ellipsis(user.id.toString(), 8, \"middle\")})\n </InspectorRow>\n ))}\n {!connectedUsers && (\n <div\n style={{\n padding: \"12px\",\n fontSize: \"12px\",\n display: \"flex\",\n flexDirection: \"column\",\n gap: \"4px\",\n }}\n >\n <span>No connected users</span>\n </div>\n )}\n </div>\n </DisclosureSection>\n <DisclosureSection\n title=\"Data\"\n colorScheme={colorScheme}\n setOpen={setShowData}\n open={showData}\n right={\n <span\n style={{\n display: \"flex\",\n gap: \"4px\",\n }}\n >\n <SmallButton\n // disabled={!historySnapshot.canUndo}\n theme={theme}\n onClick={() => {\n forceInit();\n }}\n >\n Reset\n </SmallButton>\n </span>\n }\n >\n <div style={styles.sectionInner}>\n <InspectorRow colorScheme={colorScheme}>\n <ObjectInspector\n name={multiplayerStateManager.schema ? \"state\" : undefined}\n data={state}\n theme={theme}\n />\n </InspectorRow>\n {multiplayerStateManager.schema && (\n <InspectorRow colorScheme={colorScheme}>\n <ObjectInspector\n name=\"schema\"\n data={multiplayerStateManager.schema}\n theme={theme}\n />\n </InspectorRow>\n )}\n </div>\n </DisclosureSection>\n <DisclosureSection\n open={showHistory}\n setOpen={setShowHistory}\n title=\"History\"\n colorScheme={colorScheme}\n right={\n <span\n style={{\n display: \"flex\",\n gap: \"4px\",\n }}\n >\n <SmallButton\n disabled={!historySnapshot.canUndo}\n theme={theme}\n onClick={() => {\n multiplayerStateManager.undo();\n }}\n >\n <span style={{ width: \"12px\", height: \"12px\" }}>\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"1em\"\n height=\"1em\"\n viewBox=\"0 0 20 20\"\n >\n <path\n fill=\"currentColor\"\n 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\"\n />\n </svg>\n </span>\n </SmallButton>\n <SmallButton\n disabled={!historySnapshot.canRedo}\n theme={theme}\n onClick={() => {\n multiplayerStateManager.redo();\n }}\n >\n <span style={{ width: \"12px\", height: \"12px\" }}>\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"1em\"\n height=\"1em\"\n viewBox=\"0 0 20 20\"\n >\n <path\n fill=\"currentColor\"\n 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\"\n />\n </svg>\n </span>\n </SmallButton>\n </span>\n }\n >\n <div ref={historyContainerRef} style={styles.sectionInner}>\n <div\n id={`${HISTORY_ELEMENT_PREFIX}0`}\n style={{\n borderBottom: `1px solid ${solidBorderColor}`,\n fontSize: \"12px\",\n fontFamily: \"Menlo, monospace\",\n padding: \"2px 12px 1px\",\n display: \"flex\",\n alignItems: \"center\",\n background:\n historySnapshot.historyIndex === 0\n ? \"rgb(59 130 246 / 15%)\"\n : undefined,\n }}\n >\n <span\n style={{\n fontFamily: \"Menlo, monospace\",\n fontSize: \"11px\",\n borderRadius: 4,\n display: \"inline-block\",\n }}\n >\n Initial state\n </span>\n </div>\n {historySnapshot.history.map((entry, index) => (\n <div\n id={`${HISTORY_ELEMENT_PREFIX}${index + 1}`}\n key={index}\n style={{\n padding: \"0px 12px 1px\",\n borderBottom: `1px solid ${solidBorderColor}`,\n background:\n index + 1 === historySnapshot.historyIndex\n ? \"rgb(59 130 246 / 15%)\"\n : undefined,\n }}\n >\n {entry.redoPatches?.map((patch, j) => (\n <pre\n key={j}\n style={{\n fontSize: \"11px\",\n display: \"flex\",\n flexWrap: \"wrap\",\n }}\n >\n {patch.op === \"add\" && (\n <>\n <span style={{ fontStyle: \"italic\" }}>\n {pathToString(patch.path)}\n </span>\n {\" = \"}\n <ObjectInspector data={patch.value} theme={theme} />\n </>\n )}\n {patch.op === \"replace\" && (\n <>\n <span style={{ fontStyle: \"italic\" }}>\n {pathToString(patch.path)}\n </span>\n {\" = \"}\n <ObjectInspector data={patch.value} theme={theme} />\n </>\n )}\n {patch.op === \"remove\" && (\n <>\n <span style={{ color: theme.OBJECT_VALUE_STRING_COLOR }}>\n delete{\" \"}\n </span>\n <span style={{ fontStyle: \"italic\" }}>\n {pathToString(patch.path)}\n </span>\n </>\n )}\n {patch.op === \"move\" && (\n <>\n <span style={{ fontStyle: \"italic\" }}>\n {pathToString(patch.from!)}\n </span>\n {\" -> \"}\n <span style={{ fontStyle: \"italic\" }}>\n {pathToString(patch.path)}\n </span>\n </>\n )}\n </pre>\n ))}\n </div>\n ))}\n </div>\n </DisclosureSection>\n <DisclosureSection\n title=\"Tasks\"\n colorScheme={colorScheme}\n open={showTasks}\n setOpen={setShowTasks}\n >\n <div style={styles.sectionInner}>\n {tasks?.map((task) => (\n <InspectorRow\n key={task.id}\n colorScheme={colorScheme}\n style={{\n backgroundColor:\n task.status === \"done\"\n ? \"rgba(0,255,0,0.2)\"\n : task.status === \"error\"\n ? \"rgba(255,0,0,0.2)\"\n : undefined,\n }}\n >\n <ObjectInspector\n name={task.name}\n data={task.payload}\n theme={theme}\n />\n </InspectorRow>\n ))}\n </div>\n </DisclosureSection>\n <DisclosureSection\n open={showEphemeral}\n setOpen={setShowEphemeral}\n title=\"Ephemeral User Data\"\n colorScheme={colorScheme}\n >\n <div style={styles.sectionInner}>\n {Object.entries(ephemeral).map(([key, value]) => (\n <InspectorRow key={key} colorScheme={colorScheme}>\n <ObjectInspector name={key} data={value} theme={theme} />\n </InspectorRow>\n ))}\n </div>\n </DisclosureSection>\n <DisclosureSection\n open={showEvents}\n setOpen={setShowEvents}\n title=\"Events\"\n colorScheme={colorScheme}\n >\n <div ref={eventsContainerRef} style={styles.sectionInner}>\n {connectionEvents?.map((event, index) =>\n event.type === \"stateChange\" ? (\n <InspectorRow key={index} colorScheme={colorScheme}>\n connection:{\" \"}\n <span style={{ fontStyle: \"italic\" }}>\n {event.state.toLowerCase()}\n </span>\n </InspectorRow>\n ) : (\n <InspectorRow\n key={index}\n colorScheme={colorScheme}\n variant={event.type === \"send\" ? \"up\" : \"down\"}\n style={{\n padding: \"0px 12px 1px\",\n }}\n >\n <ObjectInspector\n data={event.message}\n theme={theme}\n nodeRenderer={({\n depth,\n name,\n data,\n isNonenumerable,\n expanded,\n }: any) => {\n const direction = event.type === \"send\" ? \"up\" : \"down\";\n\n return depth === 0 ? (\n <ObjectRootLabel direction={direction} data={data} />\n ) : (\n <ObjectLabel\n direction={direction}\n name={name}\n data={data}\n isNonenumerable={isNonenumerable}\n />\n );\n }}\n />\n </InspectorRow>\n )\n )}\n {!connectionEvents && (\n <div\n style={{\n padding: \"12px\",\n fontSize: \"12px\",\n display: \"flex\",\n flexDirection: \"column\",\n gap: \"4px\",\n }}\n >\n <span>No recorded events</span>\n </div>\n )}\n </div>\n </DisclosureSection>\n </div>\n );\n});\n\ntype MessageDirection = \"up\" | \"down\";\n\nconst ObjectRootLabel: FC<{\n name?: string;\n data: any;\n direction?: MessageDirection;\n}> = ({ name, data, direction }) => {\n if (typeof name === \"string\") {\n return (\n <span>\n <ObjectName name={name} />\n <span>: </span>\n <ObjectPreview data={data} />\n </span>\n );\n }\n if (direction === \"up\" || direction === \"down\") {\n const arrow = direction === \"up\" ? \"↑\" : \"↓\";\n\n return (\n <span>\n <span\n style={{\n background:\n direction === \"up\" ? \"rgba(0,255,0,0.2)\" : \"rgba(255,0,0,0.2)\",\n // color: \"white\",\n width: 12,\n height: 12,\n borderRadius: 2,\n display: \"inline-flex\",\n justifyContent: \"center\",\n alignItems: \"center\",\n marginRight: 4,\n lineHeight: \"12px\",\n }}\n >\n {arrow}\n </span>\n <ObjectPreview data={data} />\n </span>\n );\n } else {\n return <ObjectPreview data={data} />;\n }\n};\n\n// Rotates ▶️ or ▼ based on expanded state\nfunction Arrow({\n expanded,\n onClick,\n style,\n}: {\n expanded: boolean;\n onClick?: () => void;\n style?: CSSProperties;\n}) {\n return (\n <span\n role=\"button\"\n onClick={onClick}\n style={{\n display: \"inline-block\",\n textAlign: \"center\",\n transform: expanded ? \"rotate(0deg)\" : \"rotate(-90deg)\",\n fontFamily: \"Menlo, monospace\",\n ...style,\n }}\n >\n {\"▼\"}\n </span>\n );\n}\n\nfunction pathToString(extendedPath: ExtendedPathKey[]) {\n return extendedPath\n .map((key) =>\n typeof key === \"object\"\n ? `:${ellipsis(key.id.toString(), 8, \"middle\")}`\n : key\n )\n .map((key, index) =>\n index === 0 || (typeof key === \"string\" && key.startsWith(\":\"))\n ? key\n : `.${key}`\n )\n .join(\"\");\n}\n\ntype EllipsisPosition = \"start\" | \"middle\" | \"end\";\n\nfunction ellipsis(\n str: string,\n maxLength: number,\n position: EllipsisPosition = \"end\"\n) {\n if (str.length <= maxLength) return str;\n\n switch (position) {\n case \"start\":\n return `…${str.slice(str.length - maxLength)}`;\n case \"middle\":\n const halfLength = Math.floor(maxLength / 2);\n return `${str.slice(0, halfLength)}…${str.slice(str.length - halfLength)}`;\n case \"end\":\n return `${str.slice(0, maxLength)}…`;\n }\n}\n\nfunction SmallButton({\n children,\n onClick,\n style,\n theme,\n disabled,\n}: {\n children: React.ReactNode;\n onClick: () => void;\n style?: CSSProperties;\n theme: typeof chromeLight;\n disabled?: boolean;\n}) {\n return (\n <button\n type=\"button\"\n disabled={disabled}\n onClick={(event) => {\n event.stopPropagation();\n onClick();\n }}\n style={{\n flex: \"0\",\n appearance: \"none\",\n background: \"none\",\n color: theme.BASE_COLOR,\n opacity: disabled ? 0.25 : 1,\n border: \"none\",\n fontSize: \"12px\",\n whiteSpace: \"nowrap\",\n display: \"inline-flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n padding: \"2px 0\",\n cursor: \"pointer\",\n ...style,\n }}\n >\n {children}\n </button>\n );\n}\n","\"use client\";\n\nimport React from \"react\";\n\nconst localStorage = typeof window !== \"undefined\" ? window.localStorage : null;\n\nexport function useLocalStorageState<S>(\n key: string,\n defaultValue: S\n): [S, (value: S) => void] {\n const [state, setState] = React.useState<S>(() => {\n const value = localStorage?.getItem(key);\n let result = defaultValue;\n if (value) {\n try {\n result = JSON.parse(value);\n } catch (error) {\n console.error(\"Error parsing localStorage value\", error);\n }\n }\n return result;\n });\n\n const setLocalStorageState = (value: S) => {\n localStorage?.setItem(key, JSON.stringify(value));\n setState(value);\n };\n\n return [state, setLocalStorageState];\n}\n"],"mappings":"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","names":["useEffect","useState","React","useLayoutEffect","React","useEffect","React","StateInspector","useEffect","React","useLayoutEffect","root","useState","useEffect","userId","tasks","state"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@noya-app/noya-multiplayer-react",
3
- "version": "0.1.17",
3
+ "version": "0.1.18",
4
4
  "main": "./dist/index.js",
5
5
  "module": "./dist/index.mjs",
6
6
  "types": "./dist/index.d.ts",
@@ -9,7 +9,7 @@
9
9
  "dev": "npm run build -- --watch"
10
10
  },
11
11
  "dependencies": {
12
- "@noya-app/state-manager": "0.1.17",
12
+ "@noya-app/state-manager": "0.1.18",
13
13
  "@noya-app/emitter": "0.1.0",
14
14
  "react-inspector": "6.0.2"
15
15
  },
package/src/hooks.ts CHANGED
@@ -3,6 +3,7 @@
3
3
  import {
4
4
  ActionHandler,
5
5
  ConnectionEvent,
6
+ EphemeralUserData,
6
7
  Get,
7
8
  MultiplayerStateManager,
8
9
  MultiplayerStateManagerOptions,
@@ -137,12 +138,25 @@ export function useSyncMultiplayerStateManager<S extends object, M = void>(
137
138
  );
138
139
  }
139
140
 
141
+ export function useEphemeralUserData<E>(
142
+ ephemeralUserData: EphemeralUserData<E>
143
+ ) {
144
+ return useSyncExternalStore(
145
+ ephemeralUserData.addListener,
146
+ ephemeralUserData.getSnapshot,
147
+ ephemeralUserData.getSnapshot
148
+ );
149
+ }
150
+
140
151
  const defaultStubSync = stubSync();
141
152
 
142
- export function useMultiplayerState<S extends object, M = void>(
153
+ export function useMultiplayerState<S extends object, M = void, E = void>(
143
154
  initialState: S | (() => S),
144
155
  options?: MultiplayerStateManagerOptions<S, M> & {
145
- sync?: (options: { ms: MultiplayerStateManager<S, M> }) => void;
156
+ sync?: (options: {
157
+ ms: MultiplayerStateManager<S, M>;
158
+ ephemeralUserData: EphemeralUserData<E>;
159
+ }) => void;
146
160
  trackConnectionEvents?: boolean;
147
161
  trackTasks?: boolean;
148
162
  inspector?:
@@ -150,7 +164,7 @@ export function useMultiplayerState<S extends object, M = void>(
150
164
  | Pick<ComponentProps<typeof StateInspector>, "colorScheme" | "anchor">;
151
165
  }
152
166
  ) {
153
- const { sync = defaultStubSync as unknown as SyncAdapter<S, M>, ...rest } =
167
+ const { sync = defaultStubSync as unknown as SyncAdapter<S, M, E>, ...rest } =
154
168
  options ?? {};
155
169
 
156
170
  const [multiplayerStateManager] = useState(
@@ -163,6 +177,9 @@ export function useMultiplayerState<S extends object, M = void>(
163
177
  >();
164
178
 
165
179
  const [connectedUsers, setConnectedUsers] = useState<MultiplayerUser[]>([]);
180
+ const [ephemeralUserData] = useState<EphemeralUserData<E>>(
181
+ () => new EphemeralUserData()
182
+ );
166
183
  const [tasks, setTasks] = useState<Task[]>([]);
167
184
  const [userId, setUserId] = useState<string | undefined>();
168
185
 
@@ -171,6 +188,7 @@ export function useMultiplayerState<S extends object, M = void>(
171
188
  tasks,
172
189
  connectionEvents,
173
190
  connectedUsers,
191
+ ephemeralUserData,
174
192
  // evaluate: async (code: string) => {
175
193
  // const payload = await rpcManager.request({ type: "eval", code });
176
194
 
@@ -179,7 +197,7 @@ export function useMultiplayerState<S extends object, M = void>(
179
197
  // }
180
198
  // },
181
199
  };
182
- }, [tasks, connectionEvents, connectedUsers]);
200
+ }, [tasks, connectionEvents, connectedUsers, ephemeralUserData]);
183
201
 
184
202
  const globalTrackEvents = useObservable(shouldTrackEvents$);
185
203
  const globalTrackTasks = useObservable(shouldTrackTasks$);
@@ -231,13 +249,14 @@ export function useMultiplayerState<S extends object, M = void>(
231
249
  if (syncRef.current) {
232
250
  return syncRef.current({
233
251
  ms: multiplayerStateManager,
252
+ ephemeralUserData,
234
253
  onConnectionEvent: (e) => trackEventsCallbackRef.current?.(e),
235
254
  onChangeConnectedUsers: (users, userId) =>
236
255
  trackConnectedUsersCallbackRef.current?.(users, userId),
237
256
  onChangeTasks: (tasks) => trackTasksCallbackRef.current?.(tasks),
238
257
  });
239
258
  }
240
- }, [multiplayerStateManager]);
259
+ }, [ephemeralUserData, multiplayerStateManager]);
241
260
 
242
261
  const state = useSyncMultiplayerStateManager(multiplayerStateManager);
243
262
 
@@ -250,6 +269,7 @@ export function useMultiplayerState<S extends object, M = void>(
250
269
  }, [initialState, multiplayerStateManager, options?.schema]);
251
270
 
252
271
  useStateInspector({
272
+ ephemeralUserData,
253
273
  multiplayerStateManager,
254
274
  state,
255
275
  connectionEvents,
@@ -2,6 +2,7 @@
2
2
 
3
3
  import {
4
4
  ConnectionEvent,
5
+ EphemeralUserData,
5
6
  ExtendedPathKey,
6
7
  MultiplayerStateManager,
7
8
  MultiplayerUser,
@@ -24,7 +25,7 @@ import {
24
25
  chromeLight,
25
26
  } from "react-inspector";
26
27
  import { shouldTrackEvents$, shouldTrackTasks$ } from "../globals";
27
- import { useManagedHistory } from "../hooks";
28
+ import { useEphemeralUserData, useManagedHistory } from "../hooks";
28
29
  import { useLocalStorageState } from "./useLocalStorageState";
29
30
 
30
31
  const lightTheme = {
@@ -242,6 +243,7 @@ type Anchor = "left" | "right" | "bottom left" | "bottom right";
242
243
  export const StateInspector = memo(function StateInspector<
243
244
  S extends object,
244
245
  M,
246
+ E,
245
247
  >({
246
248
  state,
247
249
  connectionEvents,
@@ -251,6 +253,7 @@ export const StateInspector = memo(function StateInspector<
251
253
  unstyled,
252
254
  colorScheme = "light",
253
255
  multiplayerStateManager,
256
+ ephemeralUserData,
254
257
  anchor = "right",
255
258
  forceInit,
256
259
  ...props
@@ -263,6 +266,7 @@ export const StateInspector = memo(function StateInspector<
263
266
  unstyled?: boolean;
264
267
  colorScheme?: "light" | "dark";
265
268
  multiplayerStateManager: MultiplayerStateManager<S, M>;
269
+ ephemeralUserData: EphemeralUserData<E>;
266
270
  anchor?: Anchor;
267
271
  forceInit: () => void;
268
272
  } & ComponentPropsWithoutRef<"div">) {
@@ -299,6 +303,10 @@ export const StateInspector = memo(function StateInspector<
299
303
  "noya-multiplayer-react-show-tasks",
300
304
  false
301
305
  );
306
+ const [showEphemeral, setShowEphemeral] = useLocalStorageState(
307
+ "noya-multiplayer-react-show-ephemeral",
308
+ false
309
+ );
302
310
 
303
311
  useEffect(() => {
304
312
  shouldTrackEvents$.set(showEvents);
@@ -315,6 +323,7 @@ export const StateInspector = memo(function StateInspector<
315
323
  }
316
324
  }, [connectionEvents]);
317
325
 
326
+ const ephemeral = useEphemeralUserData(ephemeralUserData);
318
327
  const historySnapshot = useManagedHistory(multiplayerStateManager.sm);
319
328
 
320
329
  useEffect(() => {
@@ -668,6 +677,20 @@ export const StateInspector = memo(function StateInspector<
668
677
  ))}
669
678
  </div>
670
679
  </DisclosureSection>
680
+ <DisclosureSection
681
+ open={showEphemeral}
682
+ setOpen={setShowEphemeral}
683
+ title="Ephemeral User Data"
684
+ colorScheme={colorScheme}
685
+ >
686
+ <div style={styles.sectionInner}>
687
+ {Object.entries(ephemeral).map(([key, value]) => (
688
+ <InspectorRow key={key} colorScheme={colorScheme}>
689
+ <ObjectInspector name={key} data={value} theme={theme} />
690
+ </InspectorRow>
691
+ ))}
692
+ </div>
693
+ </DisclosureSection>
671
694
  <DisclosureSection
672
695
  open={showEvents}
673
696
  setOpen={setShowEvents}
@@ -2,6 +2,7 @@
2
2
 
3
3
  import {
4
4
  ConnectionEvent,
5
+ EphemeralUserData,
5
6
  MultiplayerStateManager,
6
7
  MultiplayerUser,
7
8
  Task,
@@ -18,7 +19,7 @@ function createPortalElement() {
18
19
  return el;
19
20
  }
20
21
 
21
- export function useStateInspector<S extends object, M>({
22
+ export function useStateInspector<S extends object, M, E>({
22
23
  state,
23
24
  connectionEvents,
24
25
  connectedUsers,
@@ -28,6 +29,7 @@ export function useStateInspector<S extends object, M>({
28
29
  colorScheme,
29
30
  anchor,
30
31
  multiplayerStateManager,
32
+ ephemeralUserData,
31
33
  forceInit,
32
34
  }: {
33
35
  state: S;
@@ -37,6 +39,7 @@ export function useStateInspector<S extends object, M>({
37
39
  userId?: string;
38
40
  disabled: boolean;
39
41
  multiplayerStateManager: MultiplayerStateManager<S, M>;
42
+ ephemeralUserData: EphemeralUserData<E>;
40
43
  forceInit: () => void;
41
44
  } & Pick<ComponentProps<typeof StateInspector>, "colorScheme" | "anchor">) {
42
45
  const [root, setRoot] = React.useState<Root | null>(null);
@@ -73,6 +76,7 @@ export function useStateInspector<S extends object, M>({
73
76
  multiplayerStateManager={
74
77
  multiplayerStateManager as MultiplayerStateManager<any, any>
75
78
  }
79
+ ephemeralUserData={ephemeralUserData as EphemeralUserData<any>}
76
80
  />
77
81
  );
78
82
  }, [
@@ -86,5 +90,6 @@ export function useStateInspector<S extends object, M>({
86
90
  userId,
87
91
  tasks,
88
92
  forceInit,
93
+ ephemeralUserData,
89
94
  ]);
90
95
  }