@noya-app/noya-multiplayer-react 0.1.43 → 0.1.45

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/dist/index.mjs CHANGED
@@ -1,63 +1,34 @@
1
1
  // src/index.ts
2
2
  export * from "@noya-app/state-manager";
3
3
 
4
- // src/avatarStyle.ts
5
- function hashCode(string) {
6
- let hash = 0;
7
- for (const char of string) {
8
- hash = char.charCodeAt(0) + ((hash << 5) - hash);
9
- }
10
- return hash;
11
- }
12
- function elementFromHash(array, hash) {
13
- return array[Math.abs(hash) % array.length];
14
- }
15
- function getAvatarInitials(name) {
16
- return name.split(" ").filter((c) => c.length > 0).map((c) => c[0]).join("").toLocaleUpperCase();
17
- }
18
- function getAvatarStyle(name) {
19
- const hash = hashCode(name);
20
- const color = elementFromHash(Object.values(colors500), hash);
21
- return {
22
- color: "white",
23
- backgroundColor: color
24
- // primaryColors: {
25
- // "500": color,
26
- // "800": darkerColor,
27
- // },
28
- };
29
- }
30
- var colors500 = {
31
- red500: "#ef4444",
32
- orange500: "#f97316",
33
- amber500: "#f59e0b",
34
- yellow500: "#eab308",
35
- lime500: "#84cc16",
36
- green500: "#22c55e",
37
- emerald500: "#10b981",
38
- teal500: "#14b8a6",
39
- cyan500: "#06b6d4",
40
- sky500: "#0ea5e9",
41
- blue500: "#3b82f6",
42
- indigo500: "#6366f1",
43
- violet500: "#8b5cf6",
44
- purple500: "#a855f7",
45
- fuchsia500: "#d946ef",
46
- pink500: "#ec4899",
47
- rose500: "#f43f5e"
48
- };
49
-
50
4
  // src/components/UserPointersOverlay.tsx
51
- import React, { memo, useEffect, useMemo as useMemo2, useState } from "react";
5
+ import { memoGeneric } from "@noya-app/react-utils";
6
+ import React, { useEffect, useState } from "react";
52
7
 
53
8
  // src/useObservable.ts
54
9
  import { useMemo, useSyncExternalStore } from "react";
55
- function useObservable(observable, path) {
10
+ function useObservable(observable, pathOrSelector, options) {
56
11
  const { get, listen } = useMemo(() => {
57
- const listen2 = path ? (callback) => observable.subscribe(path, callback) : observable.subscribe;
58
- const get2 = path ? () => observable.get(path) : observable.get;
12
+ if (!observable) {
13
+ return {
14
+ get: () => void 0,
15
+ listen: () => () => {
16
+ }
17
+ };
18
+ }
19
+ if (typeof pathOrSelector === "function") {
20
+ const mappedObservable = observable.map(pathOrSelector, {
21
+ isEqual: options?.isEqual
22
+ });
23
+ return {
24
+ get: mappedObservable.get,
25
+ listen: mappedObservable.subscribe
26
+ };
27
+ }
28
+ const listen2 = pathOrSelector ? (callback) => observable.subscribe(pathOrSelector, callback) : observable.subscribe;
29
+ const get2 = pathOrSelector ? () => observable.get(pathOrSelector) : observable.get;
59
30
  return { get: get2, listen: listen2 };
60
- }, [observable, path]);
31
+ }, [observable, pathOrSelector, options?.isEqual]);
61
32
  return useSyncExternalStore(listen, get, get);
62
33
  }
63
34
 
@@ -65,150 +36,96 @@ function useObservable(observable, path) {
65
36
  function shouldShow(hideAfter, updatedAt) {
66
37
  return !!updatedAt && Date.now() - updatedAt < hideAfter;
67
38
  }
68
- var UserPointer_ = function UserPointer({
39
+ var UserPointerInternal = memoGeneric(function UserPointerInternal2({
69
40
  user,
70
- ephemeralUserData,
71
- hideAfter = 5e3
41
+ ephemeralUserDataManager,
42
+ hideAfter = 5e3,
43
+ renderUserPointer
72
44
  }) {
73
- const metadata = useObservable(ephemeralUserData.metadata$, [
45
+ const metadata = useObservable(ephemeralUserDataManager.metadata$, [
46
+ user.id
47
+ ]);
48
+ const data = useObservable(ephemeralUserDataManager.data$, [
74
49
  user.id
75
50
  ]);
76
- const data = useObservable(ephemeralUserData.data$, [user.id]);
77
- const avatarStyle = useMemo2(() => getAvatarStyle(user.id), [user.id]);
78
51
  const [, setForceUpdate] = useState(0);
79
52
  const updatedAt = metadata?.updatedAt ?? 0;
53
+ const show = shouldShow(hideAfter, updatedAt);
80
54
  useEffect(() => {
81
- if (!shouldShow(hideAfter, updatedAt)) return;
55
+ if (!show) return;
82
56
  const timeoutId = setTimeout(() => {
83
57
  setForceUpdate((prev) => prev + 1);
84
58
  }, hideAfter);
85
59
  return () => clearTimeout(timeoutId);
86
- }, [hideAfter, updatedAt]);
87
- const show = shouldShow(hideAfter, updatedAt);
88
- if (!data || !data.pointer) return null;
89
- return /* @__PURE__ */ React.createElement(UserPointerContainer, { key: user.id, point: data.pointer, show }, /* @__PURE__ */ React.createElement(UserPointerIcon, { color: avatarStyle.backgroundColor }), /* @__PURE__ */ React.createElement(UserNameTag, { background: avatarStyle.backgroundColor }, user.name));
90
- };
91
- var UserPointer2 = memo(UserPointer_);
92
- var UserPointerContainer = memo(function UserPointerContainer2({
93
- point,
94
- children,
95
- show = true
96
- }) {
97
- return /* @__PURE__ */ React.createElement(
98
- "div",
99
- {
100
- style: {
101
- position: "absolute",
102
- top: 0,
103
- left: 0,
104
- transform: `translate(${Math.round(point.x)}px, ${Math.round(
105
- point.y
106
- )}px)`,
107
- opacity: show ? 1 : 0,
108
- transition: "transform 0.1s, opacity 0.2s"
109
- }
110
- },
111
- children
112
- );
60
+ }, [hideAfter, show, updatedAt]);
61
+ if (!data?.pointer) return null;
62
+ return renderUserPointer({
63
+ userId: user.id,
64
+ name: user.name,
65
+ point: data.pointer,
66
+ visible: show
67
+ });
113
68
  });
114
- var UserNameTag = memo(function UserNameTag2({
115
- background,
116
- children
69
+ var UserPointersOverlay = memoGeneric(function UserPointers({
70
+ connectedUsersManager,
71
+ ephemeralUserDataManager,
72
+ renderUserPointer
117
73
  }) {
118
- return /* @__PURE__ */ React.createElement(
119
- "span",
120
- {
121
- style: {
122
- position: "relative",
123
- top: "8px",
124
- left: "15px",
125
- fontSize: 11,
126
- lineHeight: "1",
127
- fontWeight: 500,
128
- boxShadow: "0 1px 1px rgba(0,0,0,0.1)",
129
- padding: "2px 4px 3px",
130
- pointerEvents: "none",
131
- letterSpacing: "-0.3px",
132
- outline: `1px solid rgba(255,255,255,0.3)`,
133
- outlineOffset: "-1px",
134
- background,
135
- color: "white"
136
- }
137
- },
138
- children
139
- );
140
- });
141
- var UserPointersOverlay = memo(function UserPointers({ connectedUsers, ephemeralUserData }) {
142
- const currentUserId = useObservable(ephemeralUserData.currentUserId$);
74
+ const currentUserId = useObservable(ephemeralUserDataManager.currentUserId$);
75
+ const connectedUsers = useObservable(connectedUsersManager.connectedUsers$);
143
76
  return /* @__PURE__ */ React.createElement(React.Fragment, null, connectedUsers.map((user) => {
144
77
  if (user.id === currentUserId) return null;
145
78
  return /* @__PURE__ */ React.createElement(
146
- UserPointer2,
79
+ UserPointerInternal,
147
80
  {
148
81
  key: user.id,
149
82
  user,
150
- ephemeralUserData
83
+ ephemeralUserDataManager,
84
+ renderUserPointer
151
85
  }
152
86
  );
153
87
  }));
154
88
  });
155
- var UserPointerIcon = memo(function CursorIcon({
156
- color = "black"
157
- }) {
158
- return /* @__PURE__ */ React.createElement(
159
- "svg",
160
- {
161
- width: "15",
162
- height: "15",
163
- viewBox: "0 0 15 15",
164
- fill: "none",
165
- xmlns: "http://www.w3.org/2000/svg",
166
- style: {
167
- position: "absolute",
168
- filter: "drop-shadow(0 1px 1px rgba(0,0,0,0.1))"
169
- // filter: "drop-shadow(0 1px 1px rgba(0,0,0,0.6))",
170
- }
171
- },
172
- /* @__PURE__ */ React.createElement(
173
- "path",
174
- {
175
- fillRule: "evenodd",
176
- clipRule: "evenodd",
177
- d: "M3.29227 0.0451998C3.47033 -0.0361222 3.67946 -0.00606629 3.8274 0.122107L12.8587 7.94648C13.0134 8.08054 13.0708 8.29538 13.0035 8.48873C12.9362 8.68208 12.7578 8.81488 12.5533 8.8239L9.21887 8.97096L11.1504 13.2149C11.2648 13.4662 11.1538 13.7626 10.9026 13.877L8.75024 14.8575C8.499 14.972 8.20255 14.8611 8.08802 14.6099L6.15339 10.3665L3.86279 12.7817C3.72196 12.9302 3.50487 12.9779 3.31479 12.9021C3.1247 12.8263 3 12.6423 3 12.4376V0.500008C3 0.304264 3.11422 0.126522 3.29227 0.0451998Z",
178
- fill: color
179
- }
180
- ),
181
- /* @__PURE__ */ React.createElement(
182
- "path",
183
- {
184
- fillRule: "evenodd",
185
- clipRule: "evenodd",
186
- d: "M3.29227 0.048984C3.47033 -0.032338 3.67946 -0.00228211 3.8274 0.125891L12.8587 7.95026C13.0134 8.08432 13.0708 8.29916 13.0035 8.49251C12.9362 8.68586 12.7578 8.81866 12.5533 8.82768L9.21887 8.97474L11.1504 13.2187C11.2648 13.47 11.1538 13.7664 10.9026 13.8808L8.75024 14.8613C8.499 14.9758 8.20255 14.8649 8.08802 14.6137L6.15339 10.3703L3.86279 12.7855C3.72196 12.934 3.50487 12.9817 3.31479 12.9059C3.1247 12.8301 3 12.6461 3 12.4414V0.503792C3 0.308048 3.11422 0.130306 3.29227 0.048984ZM4 1.59852V11.1877L5.93799 9.14425C6.05238 9.02363 6.21924 8.96776 6.38319 8.99516C6.54715 9.02256 6.68677 9.12965 6.75573 9.2809L8.79056 13.7441L10.0332 13.178L8.00195 8.71497C7.93313 8.56376 7.94391 8.38824 8.03072 8.24659C8.11753 8.10494 8.26903 8.01566 8.435 8.00834L11.2549 7.88397L4 1.59852Z",
187
- fill: "rgba(255,255,255,0.5)"
188
- }
189
- )
190
- );
191
- });
192
89
 
193
90
  // src/hooks.ts
194
91
  import {
92
+ AIManager,
195
93
  AssetManager,
196
- EphemeralUserData,
94
+ ConnectedUsersManager,
95
+ EphemeralUserDataManager,
197
96
  MultiplayerStateManager,
97
+ RPCManager,
198
98
  StateManager,
199
- stubSync
99
+ stubSync,
100
+ WorkflowManager
200
101
  } from "@noya-app/state-manager";
201
102
  import {
202
103
  createElement,
203
104
  useCallback,
204
105
  useEffect as useEffect3,
205
- useMemo as useMemo3,
106
+ useMemo as useMemo2,
206
107
  useRef,
207
108
  useState as useState2,
208
109
  useSyncExternalStore as useSyncExternalStore2
209
110
  } from "react";
210
111
  import { createRoot as createRoot2 } from "react-dom/client";
211
112
 
113
+ // ../noya-state-manager/src/SecretManager.ts
114
+ import { Observable } from "@noya-app/observable";
115
+ var SecretManager = class {
116
+ constructor(rpcManager) {
117
+ this.rpcManager = rpcManager;
118
+ }
119
+ secrets$ = new Observable([]);
120
+ isInitialized$ = new Observable(false);
121
+ async createSecret(name, value) {
122
+ return await this.rpcManager.request({ type: "createSecret", name, value });
123
+ }
124
+ async deleteSecret(name) {
125
+ return await this.rpcManager.request({ type: "deleteSecret", name });
126
+ }
127
+ };
128
+
212
129
  // src/components/ErrorOverlay.tsx
213
130
  import React2 from "react";
214
131
  function ErrorOverlay({
@@ -284,17 +201,17 @@ function ErrorOverlay({
284
201
  }
285
202
 
286
203
  // src/globals.ts
287
- import { Observable } from "@noya-app/observable";
288
- var shouldTrackEvents$ = new Observable(false);
289
- var shouldTrackTasks$ = new Observable(false);
204
+ import { Observable as Observable2 } from "@noya-app/observable";
205
+ var shouldTrackEvents$ = new Observable2(false);
206
+ var shouldTrackTasks$ = new Observable2(false);
290
207
 
291
208
  // src/inspector/useStateInspector.tsx
292
209
  import React5, { useLayoutEffect as useLayoutEffect2 } from "react";
293
210
  import { createRoot } from "react-dom/client";
294
211
 
295
212
  // src/inspector/StateInspector.tsx
213
+ import { memoGeneric as memoGeneric2 } from "@noya-app/react-utils";
296
214
  import React4, {
297
- memo as memo2,
298
215
  useEffect as useEffect2,
299
216
  useLayoutEffect
300
217
  } from "react";
@@ -487,7 +404,7 @@ function InspectorRow({
487
404
  padding: "2px 12px 1px",
488
405
  display: "flex",
489
406
  alignItems: "center",
490
- background: variant === "up" ? "rgba(0,255,0,0.1)" : variant === "down" ? "rgba(255,0,0,0.1)" : selected ? "rgb(59 130 246 / 15%)" : void 0,
407
+ background: variant === "up" ? "rgba(0,255,0,0.1)" : variant === "down" ? "transparent" : selected ? "rgb(59 130 246 / 15%)" : void 0,
491
408
  // background:
492
409
  // colorScheme === "light"
493
410
  // ? "rgba(0,0,0,0.05)"
@@ -510,17 +427,17 @@ function InspectorRow({
510
427
  );
511
428
  }
512
429
  var HISTORY_ELEMENT_PREFIX = "noya-multiplayer-history-";
513
- var StateInspector = memo2(function StateInspector2({
430
+ var StateInspector = memoGeneric2(function StateInspector2({
514
431
  state,
515
432
  connectionEvents,
516
- connectedUsers,
517
433
  tasks,
518
- userId,
519
434
  unstyled,
520
435
  colorScheme = "light",
521
436
  multiplayerStateManager,
522
437
  assetManager,
523
- ephemeralUserData,
438
+ secretManager,
439
+ connectedUsersManager,
440
+ ephemeralUserDataManager,
524
441
  anchor = "right",
525
442
  forceInit,
526
443
  ...props
@@ -563,6 +480,10 @@ var StateInspector = memo2(function StateInspector2({
563
480
  "noya-multiplayer-react-show-assets",
564
481
  false
565
482
  );
483
+ const [showSecrets, setShowSecrets] = useLocalStorageState(
484
+ "noya-multiplayer-react-show-secrets",
485
+ false
486
+ );
566
487
  useEffect2(() => {
567
488
  shouldTrackEvents$.set(showEvents);
568
489
  }, [showEvents]);
@@ -574,9 +495,12 @@ var StateInspector = memo2(function StateInspector2({
574
495
  eventsContainerRef.current.scrollTop = eventsContainerRef.current.scrollHeight;
575
496
  }
576
497
  }, [connectionEvents]);
577
- const ephemeral = useEphemeralUserData(ephemeralUserData);
498
+ const ephemeral = useObservable(ephemeralUserDataManager.data$);
578
499
  const historySnapshot = useManagedHistory(multiplayerStateManager.sm);
579
500
  const assets = useObservable(assetManager.assets$);
501
+ const secrets = useObservable(secretManager.secrets$);
502
+ const connectedUsers = useObservable(connectedUsersManager.connectedUsers$);
503
+ const userId = useObservable(connectedUsersManager.currentUserId$);
580
504
  useEffect2(() => {
581
505
  if (historyContainerRef.current) {
582
506
  historyContainerRef.current.scrollTop = historyContainerRef.current.scrollHeight;
@@ -959,6 +883,37 @@ var StateInspector = memo2(function StateInspector2({
959
883
  )
960
884
  )))
961
885
  ),
886
+ /* @__PURE__ */ React4.createElement(
887
+ DisclosureSection,
888
+ {
889
+ title: "Secrets",
890
+ colorScheme,
891
+ open: showSecrets,
892
+ setOpen: setShowSecrets,
893
+ right: /* @__PURE__ */ React4.createElement(
894
+ SmallButton,
895
+ {
896
+ theme,
897
+ onClick: () => {
898
+ const name = prompt("Enter secret name");
899
+ if (!name) return;
900
+ const value = prompt("Enter secret value");
901
+ if (!value) return;
902
+ secretManager.createSecret(name, value);
903
+ }
904
+ },
905
+ "Create"
906
+ )
907
+ },
908
+ /* @__PURE__ */ React4.createElement("div", { style: styles.sectionInner }, secrets.map((secret) => /* @__PURE__ */ React4.createElement(InspectorRow, { key: secret.id, colorScheme }, /* @__PURE__ */ React4.createElement(ObjectInspector, { data: secret, theme }), /* @__PURE__ */ React4.createElement(
909
+ SmallButton,
910
+ {
911
+ theme,
912
+ onClick: () => secretManager.deleteSecret(secret.name)
913
+ },
914
+ "Delete"
915
+ ))))
916
+ ),
962
917
  /* @__PURE__ */ React4.createElement(
963
918
  DisclosureSection,
964
919
  {
@@ -1155,15 +1110,15 @@ function createShadowRootElement() {
1155
1110
  function useStateInspector({
1156
1111
  state,
1157
1112
  connectionEvents,
1158
- connectedUsers,
1159
1113
  tasks,
1160
- userId,
1161
1114
  disabled = false,
1162
1115
  colorScheme,
1163
1116
  anchor,
1164
1117
  multiplayerStateManager,
1165
1118
  assetManager,
1166
- ephemeralUserData,
1119
+ secretManager,
1120
+ connectedUsersManager,
1121
+ ephemeralUserDataManager,
1167
1122
  forceInit
1168
1123
  }) {
1169
1124
  const [root, setRoot] = React5.useState(null);
@@ -1185,31 +1140,31 @@ function useStateInspector({
1185
1140
  {
1186
1141
  state,
1187
1142
  connectionEvents,
1188
- connectedUsers,
1189
1143
  tasks,
1190
- userId,
1191
1144
  colorScheme,
1192
1145
  anchor,
1193
1146
  forceInit,
1194
1147
  multiplayerStateManager,
1195
1148
  assetManager,
1196
- ephemeralUserData
1149
+ secretManager,
1150
+ ephemeralUserDataManager,
1151
+ connectedUsersManager
1197
1152
  }
1198
1153
  )
1199
1154
  );
1200
1155
  }, [
1201
1156
  anchor,
1202
1157
  colorScheme,
1203
- connectedUsers,
1204
1158
  connectionEvents,
1205
1159
  root,
1206
1160
  state,
1207
1161
  multiplayerStateManager,
1208
- userId,
1209
1162
  tasks,
1210
1163
  forceInit,
1211
- ephemeralUserData,
1212
- assetManager
1164
+ ephemeralUserDataManager,
1165
+ assetManager,
1166
+ secretManager,
1167
+ connectedUsersManager
1213
1168
  ]);
1214
1169
  }
1215
1170
 
@@ -1279,95 +1234,63 @@ function useSyncMultiplayerStateManager(multiplayerStateManager) {
1279
1234
  multiplayerStateManager.getOptimisticState
1280
1235
  );
1281
1236
  }
1282
- function throttle(fn, ms) {
1283
- let lastCall = 0;
1284
- let timeoutId = null;
1285
- return (...args) => {
1286
- const now = Date.now();
1287
- if (now - lastCall < ms) {
1288
- if (timeoutId) clearTimeout(timeoutId);
1289
- timeoutId = setTimeout(
1290
- () => {
1291
- lastCall = now;
1292
- fn(...args);
1293
- },
1294
- ms - (now - lastCall)
1295
- );
1296
- return;
1297
- }
1298
- lastCall = now;
1299
- return fn(...args);
1300
- };
1301
- }
1302
- function useEphemeralUserData(ephemeralUserData, options) {
1303
- const throttledAddListener = useMemo3(() => {
1304
- const throttleMs = options?.throttle ?? 0;
1305
- if (throttleMs > 0) {
1306
- const addListener = (fn) => {
1307
- const throttledFn = throttle(fn, throttleMs);
1308
- return ephemeralUserData.addListener(throttledFn);
1309
- };
1310
- return addListener;
1311
- }
1312
- return ephemeralUserData.addListener;
1313
- }, [ephemeralUserData, options?.throttle]);
1314
- return useSyncExternalStore2(
1315
- throttledAddListener,
1316
- ephemeralUserData.getSnapshot,
1317
- ephemeralUserData.getSnapshot
1318
- );
1319
- }
1320
1237
  var defaultStubSync = stubSync();
1321
1238
  function useMultiplayerState(initialState, options) {
1322
1239
  const { sync = defaultStubSync, ...rest } = options ?? {};
1240
+ const [rpcManager] = useState2(
1241
+ () => new RPCManager()
1242
+ );
1323
1243
  const [multiplayerStateManager] = useState2(
1324
1244
  () => new MultiplayerStateManager(initialState, rest)
1325
1245
  );
1326
1246
  const [assetManager] = useState2(() => new AssetManager());
1247
+ const [aiManager] = useState2(() => new AIManager(rpcManager));
1248
+ const [workflowManager] = useState2(() => new WorkflowManager(rpcManager));
1249
+ const [secretManager] = useState2(() => new SecretManager(rpcManager));
1250
+ const [connectedUsersManager] = useState2(() => new ConnectedUsersManager());
1327
1251
  const syncRef = useRef(sync);
1328
1252
  const [connectionEvents, setConnectionEvents] = useState2();
1329
- const [connectedUsers, setConnectedUsers] = useState2([]);
1330
- const [ephemeralUserData] = useState2(
1331
- () => new EphemeralUserData()
1253
+ const [ephemeralUserDataManager] = useState2(
1254
+ () => new EphemeralUserDataManager()
1332
1255
  );
1333
1256
  const [tasks, setTasks] = useState2([]);
1334
- const [userId, setUserId] = useState2();
1335
1257
  const assets = useObservable(assetManager.assets$);
1336
- const extras = useMemo3(() => {
1258
+ const extras = useMemo2(() => {
1337
1259
  return {
1338
1260
  tasks,
1339
- userId,
1340
1261
  connectionEvents,
1341
- connectedUsers,
1342
- ephemeralUserData,
1262
+ connectedUsersManager,
1263
+ ephemeralUserDataManager,
1343
1264
  multiplayerStateManager,
1344
1265
  assets,
1345
1266
  assetManager,
1267
+ aiManager,
1268
+ rpcManager,
1269
+ workflowManager,
1270
+ secretManager,
1346
1271
  createAsset: assetManager.create,
1347
1272
  deleteAsset: assetManager.delete
1348
- // evaluate: async (code: string) => {
1349
- // const payload = await rpcManager.request({ type: "eval", code });
1350
- // if (payload.type !== "eval") {
1351
- // throw new Error(`Unexpected rpc response type: ${payload.type}`);
1352
- // }
1353
- // },
1273
+ // connectionStatus,
1274
+ // isConnected: connectionStatus === "connected",
1354
1275
  };
1355
1276
  }, [
1356
1277
  tasks,
1357
- userId,
1358
1278
  connectionEvents,
1359
- connectedUsers,
1360
- ephemeralUserData,
1279
+ connectedUsersManager,
1280
+ ephemeralUserDataManager,
1361
1281
  multiplayerStateManager,
1282
+ assets,
1362
1283
  assetManager,
1363
- assets
1284
+ aiManager,
1285
+ rpcManager,
1286
+ workflowManager,
1287
+ secretManager
1364
1288
  ]);
1365
1289
  const globalTrackEvents = useObservable(shouldTrackEvents$);
1366
1290
  const globalTrackTasks = useObservable(shouldTrackTasks$);
1367
1291
  const shouldTrackEvents = options?.trackConnectionEvents ?? globalTrackEvents;
1368
1292
  const shouldTrackTasks = options?.trackTasks ?? globalTrackTasks;
1369
1293
  const trackEventsCallbackRef = useRef();
1370
- const trackConnectedUsersCallbackRef = useRef();
1371
1294
  const trackTasksCallbackRef = useRef();
1372
1295
  useEffect3(() => {
1373
1296
  trackEventsCallbackRef.current = shouldTrackEvents ? (e) => setConnectionEvents((events) => {
@@ -1375,12 +1298,6 @@ function useMultiplayerState(initialState, options) {
1375
1298
  return updated.length > 50 ? updated.slice(-50) : updated;
1376
1299
  }) : void 0;
1377
1300
  }, [shouldTrackEvents]);
1378
- useEffect3(() => {
1379
- trackConnectedUsersCallbackRef.current = (users, userId2) => {
1380
- setConnectedUsers(users);
1381
- setUserId(userId2);
1382
- };
1383
- }, []);
1384
1301
  useEffect3(() => {
1385
1302
  trackTasksCallbackRef.current = shouldTrackTasks ? (tasks2) => {
1386
1303
  setTasks((old) => {
@@ -1395,13 +1312,26 @@ function useMultiplayerState(initialState, options) {
1395
1312
  return syncRef.current({
1396
1313
  ms: multiplayerStateManager,
1397
1314
  assetManager,
1398
- ephemeralUserData,
1315
+ workflowManager,
1316
+ secretManager,
1317
+ rpcManager,
1318
+ aiManager,
1319
+ ephemeralUserDataManager,
1320
+ connectedUsersManager,
1399
1321
  onConnectionEvent: (e) => trackEventsCallbackRef.current?.(e),
1400
- onChangeConnectedUsers: (users, userId2) => trackConnectedUsersCallbackRef.current?.(users, userId2),
1401
1322
  onChangeTasks: (tasks2) => trackTasksCallbackRef.current?.(tasks2)
1402
1323
  });
1403
1324
  }
1404
- }, [assetManager, ephemeralUserData, multiplayerStateManager]);
1325
+ }, [
1326
+ assetManager,
1327
+ ephemeralUserDataManager,
1328
+ multiplayerStateManager,
1329
+ workflowManager,
1330
+ secretManager,
1331
+ rpcManager,
1332
+ aiManager,
1333
+ connectedUsersManager
1334
+ ]);
1405
1335
  const state = useSyncMultiplayerStateManager(multiplayerStateManager);
1406
1336
  const forceInit = useCallback(() => {
1407
1337
  const state2 = initialState instanceof Function ? initialState() : initialState;
@@ -1436,14 +1366,14 @@ function useMultiplayerState(initialState, options) {
1436
1366
  };
1437
1367
  });
1438
1368
  useStateInspector({
1439
- ephemeralUserData,
1369
+ ephemeralUserDataManager,
1440
1370
  multiplayerStateManager,
1441
1371
  assetManager,
1372
+ secretManager,
1442
1373
  state,
1443
1374
  connectionEvents,
1444
- connectedUsers,
1375
+ connectedUsersManager,
1445
1376
  tasks,
1446
- userId,
1447
1377
  forceInit,
1448
1378
  disabled: !options?.inspector,
1449
1379
  ...typeof options?.inspector === "object" && options.inspector
@@ -1460,7 +1390,7 @@ import {
1460
1390
  TypeGuard,
1461
1391
  webSocketSync
1462
1392
  } from "@noya-app/state-manager";
1463
- import { useEffect as useEffect4, useMemo as useMemo4, useState as useState3 } from "react";
1393
+ import { useEffect as useEffect4, useMemo as useMemo3, useState as useState3 } from "react";
1464
1394
  function createDefaultAppData(initialState) {
1465
1395
  return {
1466
1396
  theme: "light",
@@ -1499,15 +1429,20 @@ function getAppData(initialState, schema, options) {
1499
1429
  appData.initialState = enforceSchema(appData.initialState, schema);
1500
1430
  return appData;
1501
1431
  }
1502
- function useBroadcastConnectedUsers(connectedUsers) {
1432
+ function useBroadcastConnectedUsers(connectedUsersManager) {
1503
1433
  useEffect4(() => {
1504
1434
  if (!isEmbeddedApp()) return;
1505
- const message = {
1506
- type: "multiplayer.setUsers",
1507
- payload: { users: connectedUsers }
1508
- };
1509
- parent?.postMessage(message, "*");
1510
- }, [connectedUsers]);
1435
+ const unsubscribe = connectedUsersManager.connectedUsers$.subscribe(
1436
+ (users) => {
1437
+ const message = {
1438
+ type: "multiplayer.setUsers",
1439
+ payload: { users }
1440
+ };
1441
+ parent?.postMessage(message, "*");
1442
+ }
1443
+ );
1444
+ return unsubscribe;
1445
+ }, [connectedUsersManager]);
1511
1446
  }
1512
1447
  function useBroadcastMenuItems(menuItems, handleSelectMenuItem) {
1513
1448
  useEffect4(() => {
@@ -1546,7 +1481,7 @@ function useNoyaState(...args) {
1546
1481
  overrideExistingState: options?.overrideExistingState
1547
1482
  });
1548
1483
  });
1549
- const sync = useMemo4(() => {
1484
+ const sync = useMemo3(() => {
1550
1485
  return multiplayerUrl ? webSocketSync(multiplayerUrl) : options?.offlineStorageKey ? localStorageSync({ key: options.offlineStorageKey }) : stubSync2();
1551
1486
  }, [multiplayerUrl, options?.offlineStorageKey]);
1552
1487
  const result = useMultiplayerState(noyaAppInitialState, {
@@ -1555,19 +1490,22 @@ function useNoyaState(...args) {
1555
1490
  sync: options?.sync ?? sync
1556
1491
  });
1557
1492
  const [, , extras] = result;
1558
- useBroadcastConnectedUsers(extras.connectedUsers);
1559
- const mergedExtras = useMemo4(() => {
1493
+ useBroadcastConnectedUsers(extras.connectedUsersManager);
1494
+ const mergedExtras = useMemo3(() => {
1560
1495
  return { ...extras, theme, viewType };
1561
1496
  }, [extras, theme, viewType]);
1562
1497
  return [result[0], result[1], mergedExtras];
1563
1498
  }
1564
1499
 
1565
1500
  // src/NoyaStateContext.tsx
1501
+ import {
1502
+ Observable as Observable3
1503
+ } from "@noya-app/observable";
1566
1504
  import React6, {
1567
1505
  createContext,
1568
1506
  useCallback as useCallback2,
1569
1507
  useContext,
1570
- useMemo as useMemo5
1508
+ useMemo as useMemo4
1571
1509
  } from "react";
1572
1510
  var AnyNoyaStateContext = createContext(void 0);
1573
1511
  function useAnyNoyaStateContext() {
@@ -1585,7 +1523,7 @@ function useAssets() {
1585
1523
  }
1586
1524
  function useAsset(id) {
1587
1525
  const { assetManager } = useAnyNoyaStateContext();
1588
- const observable = useMemo5(
1526
+ const observable = useMemo4(
1589
1527
  () => assetManager.assets$.map((assets) => assets.find((a) => a.id === id)),
1590
1528
  [assetManager, id]
1591
1529
  );
@@ -1593,7 +1531,7 @@ function useAsset(id) {
1593
1531
  }
1594
1532
  function useAssetManager() {
1595
1533
  const { assetManager } = useAnyNoyaStateContext();
1596
- return useMemo5(
1534
+ return useMemo4(
1597
1535
  () => ({
1598
1536
  create: assetManager.create,
1599
1537
  delete: assetManager.delete
@@ -1601,37 +1539,119 @@ function useAssetManager() {
1601
1539
  [assetManager]
1602
1540
  );
1603
1541
  }
1542
+ function useAIManager() {
1543
+ const { aiManager } = useAnyNoyaStateContext();
1544
+ return aiManager;
1545
+ }
1546
+ function useSecretManager() {
1547
+ const { secretManager } = useAnyNoyaStateContext();
1548
+ return secretManager;
1549
+ }
1550
+ function useSecrets() {
1551
+ const { secretManager } = useAnyNoyaStateContext();
1552
+ return useObservable(secretManager.secrets$);
1553
+ }
1554
+ function useSecret(name) {
1555
+ const { secretManager } = useAnyNoyaStateContext();
1556
+ return useObservable(
1557
+ secretManager.secrets$,
1558
+ useCallback2((secrets) => secrets.find((s) => s.name === name), [name])
1559
+ );
1560
+ }
1561
+ function useIsInitialized() {
1562
+ const { ms, secretManager, assetManager } = useAnyNoyaStateContext();
1563
+ const isInitializedObservable = useMemo4(
1564
+ () => Observable3.combine(
1565
+ [
1566
+ ms.isInitialized$,
1567
+ secretManager.isInitialized$,
1568
+ assetManager.isInitialized$
1569
+ ],
1570
+ (values) => values.every((v) => v)
1571
+ ),
1572
+ [ms, secretManager, assetManager]
1573
+ );
1574
+ return useObservable(isInitializedObservable);
1575
+ }
1576
+ function useWorkflowManager() {
1577
+ const { workflowManager } = useAnyNoyaStateContext();
1578
+ return workflowManager;
1579
+ }
1580
+ var ConnectedUsersContext = createContext(void 0);
1581
+ function useConnectedUsersManager() {
1582
+ return useContext(ConnectedUsersContext);
1583
+ }
1584
+ var AnyEphemeralUserDataManagerContext = createContext(void 0);
1585
+ function useAnyEphemeralUserData() {
1586
+ return useContext(AnyEphemeralUserDataManagerContext);
1587
+ }
1588
+ function useCurrentUserId() {
1589
+ const connectedUsersManager = useConnectedUsersManager();
1590
+ return useObservable(connectedUsersManager?.currentUserId$);
1591
+ }
1592
+ var FallbackUntilInitialized = ({
1593
+ children,
1594
+ fallback
1595
+ }) => {
1596
+ const isInitialized = useIsInitialized();
1597
+ return isInitialized ? children : fallback ?? null;
1598
+ };
1604
1599
  function createNoyaContext({
1605
1600
  schema,
1606
1601
  mergeHistoryEntries
1607
1602
  }) {
1608
1603
  const NoyaStateContext = AnyNoyaStateContext;
1604
+ const EphemeralUserDataManagerContext = AnyEphemeralUserDataManagerContext;
1609
1605
  function NoyaStateProvider({
1610
1606
  children,
1611
1607
  initialState,
1612
1608
  ...options
1613
1609
  }) {
1614
- const [, , { multiplayerStateManager, assetManager }] = useNoyaState(
1615
- initialState,
1610
+ const [
1611
+ ,
1612
+ ,
1616
1613
  {
1617
- ...options,
1618
- schema,
1619
- mergeHistoryEntries
1614
+ multiplayerStateManager,
1615
+ assetManager,
1616
+ workflowManager,
1617
+ secretManager,
1618
+ aiManager,
1619
+ ephemeralUserDataManager,
1620
+ connectedUsersManager
1620
1621
  }
1621
- );
1622
- const value = useMemo5(
1622
+ ] = useNoyaState(initialState, {
1623
+ ...options,
1624
+ schema,
1625
+ mergeHistoryEntries
1626
+ });
1627
+ const value = useMemo4(
1623
1628
  () => ({
1624
1629
  ms: multiplayerStateManager,
1625
- assetManager
1630
+ assetManager,
1631
+ workflowManager,
1632
+ secretManager,
1633
+ aiManager
1626
1634
  }),
1627
- [multiplayerStateManager, assetManager]
1635
+ [
1636
+ multiplayerStateManager,
1637
+ assetManager,
1638
+ workflowManager,
1639
+ secretManager,
1640
+ aiManager
1641
+ ]
1628
1642
  );
1629
- return /* @__PURE__ */ React6.createElement(AnyNoyaStateContext.Provider, { value }, children);
1643
+ return /* @__PURE__ */ React6.createElement(AnyNoyaStateContext.Provider, { value }, /* @__PURE__ */ React6.createElement(ConnectedUsersContext.Provider, { value: connectedUsersManager }, /* @__PURE__ */ React6.createElement(
1644
+ EphemeralUserDataManagerContext.Provider,
1645
+ {
1646
+ value: ephemeralUserDataManager
1647
+ },
1648
+ options.fallback !== void 0 ? /* @__PURE__ */ React6.createElement(FallbackUntilInitialized, { fallback: options.fallback }, children) : children
1649
+ )));
1630
1650
  }
1631
1651
  function useValue(path, options) {
1632
1652
  const { ms } = useAnyNoyaStateContext();
1633
1653
  let actualPath = typeof path === "function" || !path ? "" : path;
1634
- const mappedObservable = useMemo5(() => {
1654
+ const mappedObservable = useMemo4(() => {
1635
1655
  if (typeof path === "function") {
1636
1656
  return ms.optimisticState$.map(path, { isEqual: options?.isEqual });
1637
1657
  }
@@ -1662,45 +1682,60 @@ function createNoyaContext({
1662
1682
  const { ms } = useAnyNoyaStateContext();
1663
1683
  return ms;
1664
1684
  }
1685
+ function useEphemeralUserDataManager() {
1686
+ const ephemeralUserData = useContext(EphemeralUserDataManagerContext);
1687
+ if (!ephemeralUserData) {
1688
+ throw new Error(
1689
+ "useEphemeralUserData must be used within a NoyaStateProvider"
1690
+ );
1691
+ }
1692
+ return ephemeralUserData;
1693
+ }
1665
1694
  return {
1666
1695
  Context: NoyaStateContext,
1667
1696
  Provider: NoyaStateProvider,
1668
1697
  useValue,
1669
1698
  useSetValue,
1670
1699
  useValueState,
1671
- useStateManager
1700
+ useStateManager,
1701
+ useEphemeralUserDataManager
1672
1702
  // useAssets,
1673
1703
  // useAssetManager,
1674
1704
  // useAsset,
1675
1705
  };
1676
1706
  }
1677
1707
  export {
1708
+ AnyEphemeralUserDataManagerContext,
1709
+ ConnectedUsersContext,
1710
+ FallbackUntilInitialized,
1678
1711
  StateInspector,
1679
- UserNameTag,
1680
- UserPointer2 as UserPointer,
1681
- UserPointerContainer,
1682
- UserPointerIcon,
1683
1712
  UserPointersOverlay,
1684
1713
  createDefaultAppData,
1685
1714
  createNoyaContext,
1686
1715
  enforceSchema,
1687
1716
  getAppData,
1688
- getAvatarInitials,
1689
- getAvatarStyle,
1690
1717
  isEmbeddedApp,
1691
1718
  parseAppDataParameter,
1719
+ useAIManager,
1720
+ useAnyEphemeralUserData,
1692
1721
  useAsset,
1693
1722
  useAssetManager,
1694
1723
  useAssets,
1695
1724
  useBroadcastConnectedUsers,
1696
1725
  useBroadcastMenuItems,
1697
- useEphemeralUserData,
1726
+ useConnectedUsersManager,
1727
+ useCurrentUserId,
1728
+ useIsInitialized,
1698
1729
  useManagedHistory,
1699
1730
  useManagedState,
1700
1731
  useMultiplayerState,
1701
1732
  useNoyaState,
1702
1733
  useObservable,
1734
+ useSecret,
1735
+ useSecretManager,
1736
+ useSecrets,
1703
1737
  useSyncMultiplayerStateManager,
1704
- useSyncStateManager
1738
+ useSyncStateManager,
1739
+ useWorkflowManager
1705
1740
  };
1706
1741
  //# sourceMappingURL=index.mjs.map