@noya-app/noya-multiplayer-react 0.1.48 → 0.1.50

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@noya-app/noya-multiplayer-react",
3
- "version": "0.1.48",
3
+ "version": "0.1.50",
4
4
  "main": "./dist/index.js",
5
5
  "module": "./dist/index.mjs",
6
6
  "types": "./dist/index.d.ts",
@@ -12,11 +12,11 @@
12
12
  "dev": "npm run build -- --watch"
13
13
  },
14
14
  "dependencies": {
15
- "@noya-app/state-manager": "0.1.37",
15
+ "@noya-app/state-manager": "0.1.39",
16
16
  "@noya-app/emitter": "0.1.0",
17
17
  "@noya-app/observable": "0.1.5",
18
18
  "@noya-app/task-runner": "0.1.5",
19
- "@noya-app/react-utils": "0.1.13",
19
+ "@noya-app/react-utils": "0.1.15",
20
20
  "react-inspector": "6.0.2"
21
21
  },
22
22
  "peerDependencies": {
@@ -1,3 +1,5 @@
1
+ import { MenuItem } from "@noya-app/noya-designsystem";
2
+ import { Input, OutputTransform } from "@noya-app/noya-schemas";
1
3
  import {
2
4
  GetAtPath,
3
5
  Observable,
@@ -6,6 +8,7 @@ import {
6
8
  } from "@noya-app/observable";
7
9
  import {
8
10
  ConnectedUsersManager,
11
+ CreateParams,
9
12
  EphemeralUserDataManager,
10
13
  MultiplayerStateManager,
11
14
  NoyaManager,
@@ -21,7 +24,12 @@ import React, {
21
24
  useEffect,
22
25
  useMemo,
23
26
  } from "react";
24
- import { useNoyaState, UseNoyaStateOptions } from "./noyaApp";
27
+ import {
28
+ AppTheme,
29
+ AppViewType,
30
+ useNoyaState,
31
+ UseNoyaStateOptions,
32
+ } from "./noyaApp";
25
33
  import { useObservable } from "./useObservable";
26
34
 
27
35
  interface NoyaStateProviderProps<
@@ -39,10 +47,15 @@ interface NoyaStateProviderProps<
39
47
  }
40
48
 
41
49
  const AnyNoyaStateContext = createContext<
42
- NoyaManager<any, any, any, any> | undefined
50
+ | {
51
+ noyaManager: NoyaManager<any, any, any, any>;
52
+ theme: AppTheme;
53
+ viewType: AppViewType;
54
+ }
55
+ | undefined
43
56
  >(undefined);
44
57
 
45
- function useAnyNoyaStateContext() {
58
+ export function useAnyNoyaStateContext() {
46
59
  const value = useContext(AnyNoyaStateContext);
47
60
 
48
61
  if (!value) {
@@ -54,81 +67,114 @@ function useAnyNoyaStateContext() {
54
67
  return value;
55
68
  }
56
69
 
70
+ export function useAnyNoyaManager() {
71
+ const { noyaManager } = useAnyNoyaStateContext();
72
+ return noyaManager as NoyaManager<any, any, any, any>;
73
+ }
74
+
75
+ export function useColorScheme() {
76
+ const { theme } = useAnyNoyaStateContext();
77
+ return theme;
78
+ }
79
+
80
+ export function useViewType() {
81
+ const { viewType } = useAnyNoyaStateContext();
82
+ return viewType;
83
+ }
84
+
57
85
  export function useAssets() {
58
- const { assetManager } = useAnyNoyaStateContext();
59
- return useObservable(assetManager.assets$);
86
+ const { noyaManager } = useAnyNoyaStateContext();
87
+ return useObservable(noyaManager.assetManager.assets$);
60
88
  }
61
89
 
62
90
  export function useAsset(id: string | undefined) {
63
- const { assetManager } = useAnyNoyaStateContext();
91
+ const { noyaManager } = useAnyNoyaStateContext();
64
92
  const observable = useMemo(
65
- () => assetManager.assets$.map((assets) => assets.find((a) => a.id === id)),
66
- [assetManager, id]
93
+ () =>
94
+ noyaManager.assetManager.assets$.map((assets) =>
95
+ assets.find((a) => a.id === id)
96
+ ),
97
+ [noyaManager, id]
67
98
  );
68
99
  return useObservable(observable);
69
100
  }
70
101
 
71
102
  export function useAssetManager() {
72
- const { assetManager } = useAnyNoyaStateContext();
103
+ const { noyaManager } = useAnyNoyaStateContext();
104
+ const isInitialized = useObservable(noyaManager.assetManager.isInitialized$);
73
105
  return useMemo(
74
106
  () => ({
75
- create: assetManager.create,
76
- delete: assetManager.delete,
107
+ create: noyaManager.assetManager.create,
108
+ delete: noyaManager.assetManager.delete,
109
+ isInitialized,
77
110
  }),
78
- [assetManager]
111
+ [noyaManager, isInitialized]
79
112
  );
80
113
  }
81
114
 
82
115
  export function useAIManager() {
83
- const { aiManager } = useAnyNoyaStateContext();
84
- return aiManager;
116
+ const { noyaManager } = useAnyNoyaStateContext();
117
+ return noyaManager.aiManager;
85
118
  }
86
119
 
87
120
  export function useSecretManager() {
88
- const { secretManager } = useAnyNoyaStateContext();
89
- return secretManager;
121
+ const { noyaManager } = useAnyNoyaStateContext();
122
+ return noyaManager.secretManager;
90
123
  }
91
124
 
92
125
  export function useSecrets() {
93
- const { secretManager } = useAnyNoyaStateContext();
94
- return useObservable(secretManager.secrets$);
126
+ const { noyaManager } = useAnyNoyaStateContext();
127
+ return useObservable(noyaManager.secretManager.secrets$);
95
128
  }
96
129
 
97
130
  export function useSecret(name: string) {
98
- const { secretManager } = useAnyNoyaStateContext();
131
+ const { noyaManager } = useAnyNoyaStateContext();
99
132
 
100
133
  return useObservable(
101
- secretManager.secrets$,
134
+ noyaManager.secretManager.secrets$,
102
135
  useCallback((secrets) => secrets.find((s) => s.name === name), [name])
103
136
  );
104
137
  }
105
138
 
139
+ export function useInputs() {
140
+ const { noyaManager } = useAnyNoyaStateContext();
141
+ return useObservable(noyaManager.ioManager.inputs$);
142
+ }
143
+
144
+ export function useOutputTransforms() {
145
+ const { noyaManager } = useAnyNoyaStateContext();
146
+ return useObservable(noyaManager.ioManager.outputTransforms$);
147
+ }
148
+
149
+ export function useIOManager() {
150
+ const { noyaManager } = useAnyNoyaStateContext();
151
+ return noyaManager.ioManager;
152
+ }
153
+
106
154
  export function useIsInitialized() {
107
- const {
108
- multiplayerStateManager: ms,
109
- secretManager,
110
- assetManager,
111
- } = useAnyNoyaStateContext();
155
+ const { noyaManager, viewType } = useAnyNoyaStateContext();
112
156
 
113
157
  const isInitializedObservable = useMemo(
114
158
  () =>
115
159
  Observable.combine(
116
160
  [
117
- ms.isInitialized$,
118
- secretManager.isInitialized$,
119
- assetManager.isInitialized$,
161
+ ...(viewType === "preview"
162
+ ? []
163
+ : [noyaManager.multiplayerStateManager.isInitialized$]),
164
+ noyaManager.secretManager.isInitialized$,
165
+ noyaManager.assetManager.isInitialized$,
120
166
  ],
121
167
  (values) => values.every((v) => v)
122
168
  ),
123
- [ms, secretManager, assetManager]
169
+ [noyaManager, viewType]
124
170
  );
125
171
 
126
172
  return useObservable(isInitializedObservable);
127
173
  }
128
174
 
129
175
  export function useWorkflowManager() {
130
- const { workflowManager } = useAnyNoyaStateContext();
131
- return workflowManager;
176
+ const { noyaManager } = useAnyNoyaStateContext();
177
+ return noyaManager.workflowManager;
132
178
  }
133
179
 
134
180
  export const ConnectedUsersContext = createContext<
@@ -172,12 +218,16 @@ export function createNoyaContext<
172
218
  >({
173
219
  schema,
174
220
  mergeHistoryEntries,
221
+ outputTransforms,
222
+ inputs,
175
223
  }: {
176
224
  schema: Schema;
177
225
  mergeHistoryEntries?: StateManagerOptions<
178
226
  Static<Schema>,
179
227
  M
180
228
  >["mergeHistoryEntries"];
229
+ outputTransforms?: CreateParams<OutputTransform>[];
230
+ inputs?: CreateParams<Input>[];
181
231
  }) {
182
232
  type S = Static<Schema>;
183
233
 
@@ -195,14 +245,28 @@ export function createNoyaContext<
195
245
  initialState,
196
246
  ...options
197
247
  }: NoyaStateProviderProps<S, M, E, MenuT>) {
198
- const [, , { noyaManager }] = useNoyaState<S, M, E, MenuT>(initialState, {
199
- ...(options as any),
200
- schema,
201
- mergeHistoryEntries,
202
- });
248
+ const [, , { noyaManager, theme, viewType }] = useNoyaState<S, M, E, MenuT>(
249
+ initialState,
250
+ {
251
+ schema,
252
+ mergeHistoryEntries,
253
+ outputTransforms,
254
+ inputs,
255
+ ...(options as any),
256
+ }
257
+ );
258
+
259
+ const contextValue = useMemo(
260
+ () => ({
261
+ noyaManager,
262
+ theme,
263
+ viewType,
264
+ }),
265
+ [noyaManager, theme, viewType]
266
+ );
203
267
 
204
268
  return (
205
- <AnyNoyaStateContext.Provider value={noyaManager}>
269
+ <AnyNoyaStateContext.Provider value={contextValue}>
206
270
  <ConnectedUsersContext.Provider
207
271
  value={noyaManager.connectedUsersManager}
208
272
  >
@@ -232,17 +296,19 @@ export function createNoyaContext<
232
296
  path?: PathKey[] | string | ((value: S) => any),
233
297
  options?: Pick<ObservableOptions, "isEqual">
234
298
  ) {
235
- const { multiplayerStateManager: ms } = useAnyNoyaStateContext();
299
+ const { noyaManager } = useAnyNoyaStateContext();
236
300
 
237
301
  let actualPath: PathKey[] | string =
238
302
  typeof path === "function" || !path ? "" : path;
239
303
 
240
304
  const mappedObservable = useMemo(() => {
241
305
  if (typeof path === "function") {
242
- return ms.optimisticState$.map(path, { isEqual: options?.isEqual });
306
+ return noyaManager.multiplayerStateManager.optimisticState$.map(path, {
307
+ isEqual: options?.isEqual,
308
+ });
243
309
  }
244
- return ms.optimisticState$;
245
- }, [ms, path, options?.isEqual]);
310
+ return noyaManager.multiplayerStateManager.optimisticState$;
311
+ }, [noyaManager, path, options?.isEqual]);
246
312
 
247
313
  const value = useObservable(mappedObservable, actualPath);
248
314
 
@@ -262,7 +328,7 @@ export function createNoyaContext<
262
328
  | [value: SetStateAction<GetAtPath<S, P>>]
263
329
  ) => void;
264
330
  function useSetValue<P extends PathKey[] | string>(path?: P) {
265
- const { multiplayerStateManager: ms } = useAnyNoyaStateContext();
331
+ const { noyaManager } = useAnyNoyaStateContext();
266
332
 
267
333
  const setValue = useCallback(
268
334
  (
@@ -272,9 +338,13 @@ export function createNoyaContext<
272
338
  ): void => {
273
339
  const [metadata, value] = args.length === 2 ? args : [{}, args[0]];
274
340
 
275
- ms.setStateAtPath(metadata, (path ?? "") as P, value as never);
341
+ noyaManager.multiplayerStateManager.setStateAtPath(
342
+ metadata,
343
+ (path ?? "") as P,
344
+ value as never
345
+ );
276
346
  },
277
- [ms, path]
347
+ [noyaManager, path]
278
348
  );
279
349
 
280
350
  return setValue;
@@ -323,9 +393,9 @@ export function createNoyaContext<
323
393
  }
324
394
 
325
395
  function useStateManager() {
326
- const { multiplayerStateManager: ms } = useAnyNoyaStateContext();
396
+ const { noyaManager } = useAnyNoyaStateContext();
327
397
 
328
- return ms as MultiplayerStateManager<S, M>;
398
+ return noyaManager.multiplayerStateManager as MultiplayerStateManager<S, M>;
329
399
  }
330
400
 
331
401
  function useEphemeralUserDataManager() {
@@ -341,19 +411,41 @@ export function createNoyaContext<
341
411
  }
342
412
 
343
413
  function useNoyaManager() {
344
- return useAnyNoyaStateContext() as NoyaManager<S, M, E, MenuT>;
414
+ return useAnyNoyaStateContext().noyaManager as NoyaManager<S, M, E, MenuT>;
345
415
  }
346
416
 
347
417
  function useLeftMenuItems() {
348
418
  const { menuManager } = useNoyaManager();
419
+
349
420
  return useObservable(menuManager.leftMenuItems$);
350
421
  }
351
422
 
423
+ function useSetLeftMenuItems(leftMenuItems?: MenuItem<MenuT>[]) {
424
+ const { menuManager } = useNoyaManager();
425
+
426
+ useEffect(() => {
427
+ if (leftMenuItems) {
428
+ menuManager.setLeftMenuItems(leftMenuItems);
429
+ }
430
+ }, [menuManager, leftMenuItems]);
431
+ }
432
+
352
433
  function useRightMenuItems() {
353
434
  const { menuManager } = useNoyaManager();
435
+
354
436
  return useObservable(menuManager.rightMenuItems$);
355
437
  }
356
438
 
439
+ function useSetRightMenuItems(rightMenuItems?: MenuItem<MenuT>[]) {
440
+ const { menuManager } = useNoyaManager();
441
+
442
+ useEffect(() => {
443
+ if (rightMenuItems) {
444
+ menuManager.setRightMenuItems(rightMenuItems);
445
+ }
446
+ }, [menuManager, rightMenuItems]);
447
+ }
448
+
357
449
  function useHandleMenuItem(callback: (type: MenuT) => void) {
358
450
  const { menuManager } = useNoyaManager();
359
451
 
@@ -368,6 +460,20 @@ export function createNoyaContext<
368
460
  return useCallback((type: MenuT) => menuManager.emit(type), [menuManager]);
369
461
  }
370
462
 
463
+ function useMenu(options: {
464
+ leftMenuItems?: MenuItem<MenuT>[];
465
+ rightMenuItems?: MenuItem<MenuT>[];
466
+ onSelectMenuItem?: (type: MenuT) => void;
467
+ }) {
468
+ const { leftMenuItems, rightMenuItems, onSelectMenuItem } = options;
469
+
470
+ useSetLeftMenuItems(leftMenuItems);
471
+ useSetRightMenuItems(rightMenuItems);
472
+
473
+ const noop = useCallback(() => {}, []);
474
+ useHandleMenuItem(onSelectMenuItem ?? noop);
475
+ }
476
+
371
477
  return {
372
478
  Context: NoyaStateContext,
373
479
  Provider: NoyaStateProvider,
@@ -377,9 +483,12 @@ export function createNoyaContext<
377
483
  useStateManager,
378
484
  useEphemeralUserDataManager,
379
485
  useLeftMenuItems,
486
+ useSetLeftMenuItems,
380
487
  useRightMenuItems,
488
+ useSetRightMenuItems,
381
489
  useHandleMenuItem,
382
490
  useOnSelectMenuItemCallback,
383
491
  useNoyaManager,
492
+ useMenu,
384
493
  };
385
494
  }
@@ -1,5 +1,6 @@
1
1
  "use client";
2
2
 
3
+ import { Input, OutputTransform } from "@noya-app/noya-schemas";
3
4
  import { memoGeneric } from "@noya-app/react-utils";
4
5
  import { ExtendedPathKey, NoyaManager } from "@noya-app/state-manager";
5
6
  import React, {
@@ -266,10 +267,13 @@ export const StateInspector = memoGeneric(function StateInspector<
266
267
  ephemeralUserDataManager,
267
268
  connectionEventManager,
268
269
  taskManager,
270
+ ioManager,
269
271
  } = noyaManager;
270
272
 
271
273
  const [didMount, setDidMount] = React.useState(false);
272
274
  const tasks = useObservable(taskManager.tasks$);
275
+ const inputs = useObservable(ioManager.inputs$);
276
+ const outputTransforms = useObservable(ioManager.outputTransforms$);
273
277
 
274
278
  useLayoutEffect(() => {
275
279
  setDidMount(true);
@@ -314,6 +318,14 @@ export const StateInspector = memoGeneric(function StateInspector<
314
318
  "noya-multiplayer-react-show-secrets",
315
319
  false
316
320
  );
321
+ const [showInputs, setShowInputs] = useLocalStorageState(
322
+ "noya-multiplayer-react-show-inputs",
323
+ false
324
+ );
325
+ const [showOutputTransforms, setShowOutputTransforms] = useLocalStorageState(
326
+ "noya-multiplayer-react-show-output-transforms",
327
+ false
328
+ );
317
329
 
318
330
  const connectionEvents = useObservable(connectionEventManager.events$);
319
331
 
@@ -336,6 +348,10 @@ export const StateInspector = memoGeneric(function StateInspector<
336
348
  const connectedUsers = useObservable(connectedUsersManager.connectedUsers$);
337
349
  const userId = useObservable(connectedUsersManager.currentUserId$);
338
350
  const state = useObservable(multiplayerStateManager.optimisticState$);
351
+ const inputsInitialized = useObservable(ioManager.inputsInitialized$);
352
+ const outputTransformsInitialized = useObservable(
353
+ ioManager.outputTransformsInitialized$
354
+ );
339
355
 
340
356
  useEffect(() => {
341
357
  if (historyContainerRef.current) {
@@ -759,35 +775,6 @@ export const StateInspector = memoGeneric(function StateInspector<
759
775
  ))}
760
776
  </div>
761
777
  </DisclosureSection>
762
- <DisclosureSection
763
- title="Tasks"
764
- colorScheme={colorScheme}
765
- open={showTasks}
766
- setOpen={setShowTasks}
767
- >
768
- <div style={styles.sectionInner}>
769
- {tasks?.map((task) => (
770
- <InspectorRow
771
- key={task.id}
772
- colorScheme={colorScheme}
773
- style={{
774
- backgroundColor:
775
- task.status === "done"
776
- ? "rgba(0,255,0,0.2)"
777
- : task.status === "error"
778
- ? "rgba(255,0,0,0.2)"
779
- : undefined,
780
- }}
781
- >
782
- <ObjectInspector
783
- name={task.name}
784
- data={task.payload}
785
- theme={theme}
786
- />
787
- </InspectorRow>
788
- ))}
789
- </div>
790
- </DisclosureSection>
791
778
  <DisclosureSection
792
779
  title={
793
780
  <TitleLabel>
@@ -826,6 +813,101 @@ export const StateInspector = memoGeneric(function StateInspector<
826
813
  </InspectorRow>
827
814
  ))}
828
815
  </div>
816
+ </DisclosureSection>{" "}
817
+ <DisclosureSection
818
+ open={showInputs}
819
+ setOpen={setShowInputs}
820
+ title={
821
+ <TitleLabel>
822
+ Inputs
823
+ <ColoredDot type={inputsInitialized ? "success" : "error"} />
824
+ </TitleLabel>
825
+ }
826
+ colorScheme={colorScheme}
827
+ >
828
+ <div style={styles.sectionInner}>
829
+ {inputs?.map((input: Input) => (
830
+ <InspectorRow key={input.id} colorScheme={colorScheme}>
831
+ <ObjectInspector data={input} theme={theme} />
832
+ </InspectorRow>
833
+ ))}
834
+ {!inputs?.length && (
835
+ <div
836
+ style={{
837
+ padding: "12px",
838
+ fontSize: "12px",
839
+ display: "flex",
840
+ flexDirection: "column",
841
+ gap: "4px",
842
+ }}
843
+ >
844
+ <span>No inputs</span>
845
+ </div>
846
+ )}
847
+ </div>
848
+ </DisclosureSection>
849
+ <DisclosureSection
850
+ open={showOutputTransforms}
851
+ setOpen={setShowOutputTransforms}
852
+ title={
853
+ <TitleLabel>
854
+ Output Transforms
855
+ <ColoredDot
856
+ type={outputTransformsInitialized ? "success" : "error"}
857
+ />
858
+ </TitleLabel>
859
+ }
860
+ colorScheme={colorScheme}
861
+ >
862
+ <div style={styles.sectionInner}>
863
+ {outputTransforms?.map((transform: OutputTransform) => (
864
+ <InspectorRow key={transform.id} colorScheme={colorScheme}>
865
+ <ObjectInspector data={transform} theme={theme} />
866
+ </InspectorRow>
867
+ ))}
868
+ {!outputTransforms?.length && (
869
+ <div
870
+ style={{
871
+ padding: "12px",
872
+ fontSize: "12px",
873
+ display: "flex",
874
+ flexDirection: "column",
875
+ gap: "4px",
876
+ }}
877
+ >
878
+ <span>No output transforms</span>
879
+ </div>
880
+ )}
881
+ </div>
882
+ </DisclosureSection>
883
+ <DisclosureSection
884
+ title="Tasks"
885
+ colorScheme={colorScheme}
886
+ open={showTasks}
887
+ setOpen={setShowTasks}
888
+ >
889
+ <div style={styles.sectionInner}>
890
+ {tasks?.map((task) => (
891
+ <InspectorRow
892
+ key={task.id}
893
+ colorScheme={colorScheme}
894
+ style={{
895
+ backgroundColor:
896
+ task.status === "done"
897
+ ? "rgba(0,255,0,0.2)"
898
+ : task.status === "error"
899
+ ? "rgba(255,0,0,0.2)"
900
+ : undefined,
901
+ }}
902
+ >
903
+ <ObjectInspector
904
+ name={task.name}
905
+ data={task.payload}
906
+ theme={theme}
907
+ />
908
+ </InspectorRow>
909
+ ))}
910
+ </div>
829
911
  </DisclosureSection>
830
912
  <DisclosureSection
831
913
  open={showEphemeral}
package/src/noyaApp.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  /* eslint-disable no-restricted-globals */
2
+ import { findAllDefs } from "@noya-app/noya-schemas";
2
3
  import {
3
4
  createOrCastValue,
4
- findAllDefs,
5
5
  isEmbeddedApp,
6
6
  localStorageSync,
7
7
  parentFrameSync,