@noya-app/noya-multiplayer-react 0.1.61 → 0.1.63

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
@@ -1108,18 +1108,18 @@ var require_lib = __commonJS({
1108
1108
  export * from "@noya-app/state-manager";
1109
1109
 
1110
1110
  // src/ai.ts
1111
- import { useEffect as useEffect4 } from "react";
1111
+ import { useEffect as useEffect5 } from "react";
1112
1112
 
1113
1113
  // src/NoyaStateContext.tsx
1114
1114
  import {
1115
1115
  Observable
1116
1116
  } from "@noya-app/observable";
1117
1117
  import { useStableCallback } from "@noya-app/react-utils";
1118
- import React5, {
1118
+ import React14, {
1119
1119
  createContext,
1120
- useCallback as useCallback2,
1120
+ useCallback as useCallback3,
1121
1121
  useContext,
1122
- useEffect as useEffect3,
1122
+ useEffect as useEffect4,
1123
1123
  useMemo as useMemo4
1124
1124
  } from "react";
1125
1125
 
@@ -3547,6 +3547,71 @@ function Void(options = {}) {
3547
3547
  // ../../node_modules/@sinclair/typebox/build/esm/type/type/index.mjs
3548
3548
  var Type = type_exports3;
3549
3549
 
3550
+ // ../noya-schemas/src/asset.ts
3551
+ var assetSchema = Type.Object({
3552
+ id: Type.String(),
3553
+ url: Type.String(),
3554
+ createdAt: Type.String(),
3555
+ size: Type.Number(),
3556
+ contentType: Type.Optional(Type.String())
3557
+ });
3558
+
3559
+ // ../noya-schemas/src/encode.ts
3560
+ var SchemaMap = /* @__PURE__ */ new Map([
3561
+ [Kind, "_kind"],
3562
+ [Hint, "_hint"]
3563
+ ]);
3564
+ var InverseSchemaMap = new Map(
3565
+ Array.from(SchemaMap.entries()).map(([key, value]) => [value, key])
3566
+ );
3567
+ function encodeSchema(obj, symMap = SchemaMap) {
3568
+ function replaceSymbols(item) {
3569
+ if (Array.isArray(item)) {
3570
+ return item.map(replaceSymbols);
3571
+ } else if (typeof item === "object" && item !== null) {
3572
+ const newObj = {};
3573
+ for (const key in item) {
3574
+ if (Object.prototype.hasOwnProperty.call(item, key)) {
3575
+ const value = item[key];
3576
+ newObj[key] = replaceSymbols(value);
3577
+ }
3578
+ }
3579
+ for (const sym of Object.getOwnPropertySymbols(item)) {
3580
+ const stringKey = symMap.get(sym);
3581
+ if (stringKey) {
3582
+ newObj[stringKey] = replaceSymbols(item[sym]);
3583
+ }
3584
+ }
3585
+ return newObj;
3586
+ }
3587
+ return item;
3588
+ }
3589
+ return replaceSymbols(obj);
3590
+ }
3591
+ function decodeSchema(obj, strMap = InverseSchemaMap) {
3592
+ function replaceStringKeys(item) {
3593
+ if (Array.isArray(item)) {
3594
+ return item.map(replaceStringKeys);
3595
+ } else if (typeof item === "object" && item !== null) {
3596
+ const newObj = {};
3597
+ for (const key in item) {
3598
+ if (Object.prototype.hasOwnProperty.call(item, key)) {
3599
+ const value = item[key];
3600
+ const symbolKey = strMap.get(key);
3601
+ if (symbolKey) {
3602
+ newObj[symbolKey] = replaceStringKeys(value);
3603
+ } else {
3604
+ newObj[key] = replaceStringKeys(value);
3605
+ }
3606
+ }
3607
+ }
3608
+ return newObj;
3609
+ }
3610
+ return item;
3611
+ }
3612
+ return replaceStringKeys(obj);
3613
+ }
3614
+
3550
3615
  // ../noya-schemas/src/filesSchema.ts
3551
3616
  var encodingSchema = Type.Union([
3552
3617
  Type.Literal("utf-8"),
@@ -3567,8 +3632,13 @@ var fileSchema = Type.Object({
3567
3632
  content: Type.String(),
3568
3633
  encoding: encodingSchema
3569
3634
  });
3635
+ var noyaFileSchema = Type.Object({
3636
+ id: Type.String({ format: "uuid", default: "" }),
3637
+ kind: Type.Literal("noyaFile"),
3638
+ fileId: Type.String()
3639
+ });
3570
3640
  var mediaItemSchema = Type.Union(
3571
- [folderMediaItemSchema, assetMediaItemSchema, fileSchema],
3641
+ [folderMediaItemSchema, assetMediaItemSchema, fileSchema, noyaFileSchema],
3572
3642
  {
3573
3643
  discriminator: "kind"
3574
3644
  }
@@ -3922,16 +3992,16 @@ import { useMemo as useMemo3, useState as useState2 } from "react";
3922
3992
 
3923
3993
  // src/hooks.ts
3924
3994
  import {
3925
- NoyaManager,
3995
+ NoyaManager as NoyaManager2,
3926
3996
  StateManager,
3927
3997
  stubSync
3928
3998
  } from "@noya-app/state-manager";
3929
3999
  import {
3930
4000
  createElement as createElement2,
3931
- useCallback,
3932
- useEffect as useEffect2,
4001
+ useCallback as useCallback2,
4002
+ useEffect as useEffect3,
3933
4003
  useMemo as useMemo2,
3934
- useRef,
4004
+ useRef as useRef2,
3935
4005
  useState,
3936
4006
  useSyncExternalStore as useSyncExternalStore2
3937
4007
  } from "react";
@@ -4012,24 +4082,18 @@ var ErrorOverlay = React.memo(function ErrorOverlay2({
4012
4082
  });
4013
4083
 
4014
4084
  // src/inspector/useStateInspector.tsx
4015
- import React4 from "react";
4085
+ import React13 from "react";
4016
4086
  import { createRoot } from "react-dom/client";
4017
4087
  import { useIsomorphicLayoutEffect } from "@noya-app/react-utils";
4018
4088
 
4019
4089
  // src/inspector/StateInspector.tsx
4020
- import { memoGeneric } from "@noya-app/react-utils";
4021
- import React3, {
4022
- useEffect,
4090
+ import { downloadBlob, memoGeneric } from "@noya-app/react-utils";
4091
+ import React12, {
4092
+ useCallback,
4093
+ useEffect as useEffect2,
4023
4094
  useLayoutEffect
4024
4095
  } from "react";
4025
- import {
4026
- ObjectInspector,
4027
- ObjectLabel,
4028
- ObjectName,
4029
- ObjectPreview,
4030
- chromeDark,
4031
- chromeLight
4032
- } from "react-inspector";
4096
+ import { ObjectInspector as ObjectInspector3 } from "react-inspector";
4033
4097
 
4034
4098
  // src/useObservable.ts
4035
4099
  import { useMemo, useSyncExternalStore } from "react";
@@ -4058,119 +4122,110 @@ function useObservable(observable, pathOrSelector, options) {
4058
4122
  return useSyncExternalStore(listen, get, get);
4059
4123
  }
4060
4124
 
4061
- // src/inspector/useLocalStorageState.tsx
4125
+ // src/inspector/ColoredDot.tsx
4062
4126
  import React2 from "react";
4063
- var localStorage = typeof window !== "undefined" ? window.localStorage : null;
4064
- function useLocalStorageState(key, defaultValue) {
4065
- const [state, setState] = React2.useState(() => {
4066
- const value = localStorage?.getItem(key);
4067
- let result = defaultValue;
4068
- if (value) {
4069
- try {
4070
- result = JSON.parse(value);
4071
- } catch (error) {
4072
- console.error("Error parsing localStorage value", error);
4127
+ function ColoredDot({ type }) {
4128
+ return /* @__PURE__ */ React2.createElement(
4129
+ "span",
4130
+ {
4131
+ style: {
4132
+ display: "inline-block",
4133
+ width: 6,
4134
+ height: 6,
4135
+ background: type === "success" ? "rgba(0,200,0,0.8)" : "rgba(200,0,0,0.8)",
4136
+ borderRadius: "50%",
4137
+ verticalAlign: "middle"
4073
4138
  }
4074
4139
  }
4075
- return result;
4076
- });
4077
- const setLocalStorageState = (value) => {
4078
- localStorage?.setItem(key, JSON.stringify(value));
4079
- setState(value);
4080
- };
4081
- return [state, setLocalStorageState];
4140
+ );
4082
4141
  }
4083
4142
 
4084
- // src/inspector/StateInspector.tsx
4085
- var lightTheme = {
4143
+ // src/inspector/inspectorTheme.ts
4144
+ import { chromeDark, chromeLight } from "react-inspector";
4145
+ var lightInspectorTheme = {
4086
4146
  ...chromeLight,
4087
4147
  BASE_BACKGROUND_COLOR: "transparent",
4088
4148
  OBJECT_NAME_COLOR: "rgba(0,0,0,0.7)"
4089
4149
  };
4090
- var darkTheme = {
4150
+ var darkInspectorTheme = {
4091
4151
  ...chromeDark,
4092
4152
  BASE_BACKGROUND_COLOR: "transparent",
4093
4153
  OBJECT_NAME_COLOR: "rgba(255,255,255,0.7)"
4094
4154
  };
4095
- var styles = {
4096
- sectionInner: {
4097
- flex: "1 1 0",
4098
- overflowY: "auto",
4099
- overflowX: "hidden",
4100
- display: "flex",
4101
- flexDirection: "column"
4155
+ var getStateInspectorTheme = (colorScheme) => {
4156
+ return colorScheme === "light" ? lightInspectorTheme : darkInspectorTheme;
4157
+ };
4158
+ var getStateInspectorBorderColor = (colorScheme) => {
4159
+ return colorScheme === "light" ? "rgb(223 223 223)" : "rgb(29 29 29)";
4160
+ };
4161
+
4162
+ // src/inspector/sections/EventsSection.tsx
4163
+ import React7 from "react";
4164
+ import { ObjectInspector, ObjectLabel } from "react-inspector";
4165
+
4166
+ // src/inspector/ObjectRootLabel.tsx
4167
+ import React3 from "react";
4168
+ import { ObjectName, ObjectPreview } from "react-inspector";
4169
+ var ObjectRootLabel = ({ name, data, direction }) => {
4170
+ if (typeof name === "string") {
4171
+ return /* @__PURE__ */ React3.createElement("span", null, /* @__PURE__ */ React3.createElement(ObjectName, { name }), /* @__PURE__ */ React3.createElement("span", null, ": "), /* @__PURE__ */ React3.createElement(ObjectPreview, { data }));
4172
+ }
4173
+ if (direction === "up" || direction === "down") {
4174
+ const arrow = direction === "up" ? "\u2191" : "\u2193";
4175
+ return /* @__PURE__ */ React3.createElement("span", null, /* @__PURE__ */ React3.createElement(
4176
+ "span",
4177
+ {
4178
+ style: {
4179
+ background: direction === "up" ? "rgba(0,255,0,0.2)" : "rgba(255,0,0,0.2)",
4180
+ // color: "white",
4181
+ width: 12,
4182
+ height: 12,
4183
+ borderRadius: 2,
4184
+ display: "inline-flex",
4185
+ justifyContent: "center",
4186
+ alignItems: "center",
4187
+ marginRight: 4,
4188
+ lineHeight: "12px"
4189
+ }
4190
+ },
4191
+ arrow
4192
+ ), /* @__PURE__ */ React3.createElement(ObjectPreview, { data }));
4193
+ } else {
4194
+ return /* @__PURE__ */ React3.createElement(ObjectPreview, { data });
4102
4195
  }
4103
4196
  };
4104
- function ToggleButton({
4105
- showInspector,
4106
- setShowInspector,
4107
- theme,
4108
- anchor
4197
+
4198
+ // src/inspector/StateInspectorDisclosureSection.tsx
4199
+ import React5, {
4200
+ forwardRef
4201
+ } from "react";
4202
+
4203
+ // src/inspector/StateInspectorArrow.tsx
4204
+ import React4 from "react";
4205
+ function StateInspectorArrow({
4206
+ expanded,
4207
+ onClick,
4208
+ style
4109
4209
  }) {
4110
- const isRightAnchor = anchor === "right";
4111
- const rightIcon = /* @__PURE__ */ React3.createElement(
4112
- "svg",
4113
- {
4114
- xmlns: "http://www.w3.org/2000/svg",
4115
- fill: "none",
4116
- viewBox: "0 0 24 24",
4117
- strokeWidth: 1.5,
4118
- stroke: "currentColor",
4119
- className: "size-6"
4120
- },
4121
- /* @__PURE__ */ React3.createElement(
4122
- "path",
4123
- {
4124
- strokeLinecap: "round",
4125
- strokeLinejoin: "round",
4126
- d: "m5.25 4.5 7.5 7.5-7.5 7.5m6-15 7.5 7.5-7.5 7.5"
4127
- }
4128
- )
4129
- );
4130
- const leftIcon = /* @__PURE__ */ React3.createElement(
4131
- "svg",
4132
- {
4133
- xmlns: "http://www.w3.org/2000/svg",
4134
- fill: "none",
4135
- viewBox: "0 0 24 24",
4136
- strokeWidth: 1.5,
4137
- stroke: "currentColor",
4138
- className: "size-6"
4139
- },
4140
- /* @__PURE__ */ React3.createElement(
4141
- "path",
4142
- {
4143
- strokeLinecap: "round",
4144
- strokeLinejoin: "round",
4145
- d: "m18.75 4.5-7.5 7.5 7.5 7.5m-6-15L5.25 12l7.5 7.5"
4146
- }
4147
- )
4148
- );
4149
- return /* @__PURE__ */ React3.createElement(
4210
+ return /* @__PURE__ */ React4.createElement(
4150
4211
  "span",
4151
4212
  {
4152
4213
  role: "button",
4214
+ onClick,
4153
4215
  style: {
4154
- flex: "0",
4155
- appearance: "none",
4156
- color: theme.BASE_COLOR,
4157
- border: "none",
4158
- fontSize: "12px",
4159
- whiteSpace: "nowrap",
4160
- display: "inline-flex",
4161
- alignItems: "center",
4162
- justifyContent: "center",
4163
- padding: "2px 0"
4164
- },
4165
- onClick: (event) => {
4166
- event.stopPropagation();
4167
- setShowInspector(!showInspector);
4216
+ display: "inline-block",
4217
+ textAlign: "center",
4218
+ transform: expanded ? "rotate(0deg)" : "rotate(-90deg)",
4219
+ fontFamily: "Menlo, monospace",
4220
+ ...style
4168
4221
  }
4169
4222
  },
4170
- showInspector ? "Hide Inspector" : /* @__PURE__ */ React3.createElement("span", { style: { width: "12px", height: "12px" } }, isRightAnchor ? rightIcon : leftIcon)
4223
+ "\u25BC"
4171
4224
  );
4172
4225
  }
4173
- function DisclosureSection({
4226
+
4227
+ // src/inspector/StateInspectorDisclosureSection.tsx
4228
+ function StateInspectorDisclosureSection({
4174
4229
  open,
4175
4230
  setOpen,
4176
4231
  title,
@@ -4180,9 +4235,9 @@ function DisclosureSection({
4180
4235
  isFirst,
4181
4236
  style
4182
4237
  }) {
4183
- const theme = colorScheme === "light" ? lightTheme : darkTheme;
4238
+ const theme = colorScheme === "light" ? lightInspectorTheme : darkInspectorTheme;
4184
4239
  const borderColor = colorScheme === "light" ? "rgba(0,0,0,0.1)" : "rgba(255,255,255,0.1)";
4185
- return /* @__PURE__ */ React3.createElement(
4240
+ return /* @__PURE__ */ React5.createElement(
4186
4241
  "div",
4187
4242
  {
4188
4243
  style: {
@@ -4192,7 +4247,7 @@ function DisclosureSection({
4192
4247
  ...style
4193
4248
  }
4194
4249
  },
4195
- /* @__PURE__ */ React3.createElement(
4250
+ /* @__PURE__ */ React5.createElement(
4196
4251
  "div",
4197
4252
  {
4198
4253
  onClick: () => setOpen?.(!open),
@@ -4206,8 +4261,8 @@ function DisclosureSection({
4206
4261
  ...open && { borderBottom: `1px solid ${borderColor}` }
4207
4262
  }
4208
4263
  },
4209
- setOpen && /* @__PURE__ */ React3.createElement(
4210
- Arrow,
4264
+ setOpen && /* @__PURE__ */ React5.createElement(
4265
+ StateInspectorArrow,
4211
4266
  {
4212
4267
  expanded: open,
4213
4268
  style: {
@@ -4217,13 +4272,35 @@ function DisclosureSection({
4217
4272
  }
4218
4273
  }
4219
4274
  ),
4220
- /* @__PURE__ */ React3.createElement("span", { style: { flex: "1 1 0", userSelect: "none" } }, title),
4275
+ /* @__PURE__ */ React5.createElement("span", { style: { flex: "1 1 0", userSelect: "none" } }, title),
4221
4276
  right
4222
4277
  ),
4223
4278
  open && children
4224
4279
  );
4225
4280
  }
4226
- function InspectorRow({
4281
+ var StateInspectorDisclosureRowInner = forwardRef(
4282
+ function StateInspectorDisclosureRowInner2(props, forwardedRef) {
4283
+ return /* @__PURE__ */ React5.createElement(
4284
+ "div",
4285
+ {
4286
+ style: {
4287
+ flex: "1 1 0",
4288
+ overflowY: "auto",
4289
+ overflowX: "hidden",
4290
+ display: "flex",
4291
+ flexDirection: "column",
4292
+ ...props.style
4293
+ },
4294
+ ...props,
4295
+ ref: forwardedRef
4296
+ }
4297
+ );
4298
+ }
4299
+ );
4300
+
4301
+ // src/inspector/StateInspectorRow.tsx
4302
+ import React6 from "react";
4303
+ function StateInspectorRow({
4227
4304
  children,
4228
4305
  colorScheme,
4229
4306
  selected,
@@ -4231,7 +4308,7 @@ function InspectorRow({
4231
4308
  variant
4232
4309
  }) {
4233
4310
  const solidBorderColor = colorScheme === "light" ? "rgb(223 223 223)" : "rgb(29 29 29)";
4234
- return /* @__PURE__ */ React3.createElement(
4311
+ return /* @__PURE__ */ React6.createElement(
4235
4312
  "div",
4236
4313
  {
4237
4314
  style: {
@@ -4249,7 +4326,7 @@ function InspectorRow({
4249
4326
  ...style
4250
4327
  }
4251
4328
  },
4252
- /* @__PURE__ */ React3.createElement(
4329
+ /* @__PURE__ */ React6.createElement(
4253
4330
  "span",
4254
4331
  {
4255
4332
  style: {
@@ -4263,68 +4340,1035 @@ function InspectorRow({
4263
4340
  )
4264
4341
  );
4265
4342
  }
4266
- var HISTORY_ELEMENT_PREFIX = "noya-multiplayer-history-";
4267
- var StateInspector = memoGeneric(function StateInspector2({
4268
- noyaManager,
4269
- colorScheme = "light",
4270
- anchor = "right",
4271
- unstyled = false,
4272
- ...props
4343
+
4344
+ // src/inspector/sections/EventsSection.tsx
4345
+ function EventsSection({
4346
+ showEvents,
4347
+ setShowEvents,
4348
+ colorScheme,
4349
+ eventsContainerRef,
4350
+ connectionEvents
4273
4351
  }) {
4274
- const {
4275
- multiplayerStateManager,
4276
- assetManager,
4277
- connectedUsersManager,
4278
- secretManager,
4279
- ephemeralUserDataManager,
4280
- connectionEventManager,
4281
- taskManager,
4282
- ioManager
4283
- } = noyaManager;
4284
- const [didMount, setDidMount] = React3.useState(false);
4285
- const tasks = useObservable(taskManager.tasks$);
4286
- const inputs = useObservable(ioManager.inputs$);
4287
- const outputTransforms = useObservable(ioManager.outputTransforms$);
4288
- useLayoutEffect(() => {
4289
- setDidMount(true);
4290
- }, []);
4291
- const eventsContainerRef = React3.useRef(null);
4292
- const historyContainerRef = React3.useRef(null);
4293
- const [showInspector, setShowInspector] = useLocalStorageState(
4294
- "noya-multiplayer-react-show-inspector",
4295
- true
4296
- );
4297
- const [showEvents, setShowEvents] = useLocalStorageState(
4298
- "noya-multiplayer-react-show-events",
4299
- false
4300
- );
4301
- const [showHistory, setShowHistory] = useLocalStorageState(
4302
- "noya-multiplayer-react-show-history",
4303
- false
4304
- );
4305
- const [showUsers, setShowUsers] = useLocalStorageState(
4306
- "noya-multiplayer-react-show-users",
4307
- true
4308
- );
4309
- const [showData, setShowData] = useLocalStorageState(
4310
- "noya-multiplayer-react-show-data",
4311
- true
4312
- );
4313
- const [showTasks, setShowTasks] = useLocalStorageState(
4314
- "noya-multiplayer-react-show-tasks",
4315
- false
4316
- );
4317
- const [showEphemeral, setShowEphemeral] = useLocalStorageState(
4318
- "noya-multiplayer-react-show-ephemeral",
4319
- false
4320
- );
4321
- const [showAssets, setShowAssets] = useLocalStorageState(
4322
- "noya-multiplayer-react-show-assets",
4323
- false
4324
- );
4325
- const [showSecrets, setShowSecrets] = useLocalStorageState(
4326
- "noya-multiplayer-react-show-secrets",
4327
- false
4352
+ const theme = getStateInspectorTheme(colorScheme);
4353
+ return /* @__PURE__ */ React7.createElement(
4354
+ StateInspectorDisclosureSection,
4355
+ {
4356
+ open: showEvents,
4357
+ setOpen: setShowEvents,
4358
+ title: "Events",
4359
+ colorScheme
4360
+ },
4361
+ /* @__PURE__ */ React7.createElement(StateInspectorDisclosureRowInner, { ref: eventsContainerRef }, connectionEvents?.map(
4362
+ (event, index) => event.type === "stateChange" ? /* @__PURE__ */ React7.createElement(StateInspectorRow, { key: index, colorScheme }, "connection:", " ", /* @__PURE__ */ React7.createElement("span", { style: { fontStyle: "italic" } }, event.state.toLowerCase())) : /* @__PURE__ */ React7.createElement(
4363
+ StateInspectorRow,
4364
+ {
4365
+ key: index,
4366
+ colorScheme,
4367
+ variant: event.type === "send" ? "up" : "down",
4368
+ style: {
4369
+ padding: "0px 12px 1px"
4370
+ }
4371
+ },
4372
+ /* @__PURE__ */ React7.createElement(
4373
+ ObjectInspector,
4374
+ {
4375
+ data: event.type === "error" ? event.error : event.message,
4376
+ theme,
4377
+ nodeRenderer: ({ depth, name, data, isNonenumerable }) => {
4378
+ const direction = event.type === "send" ? "up" : "down";
4379
+ return depth === 0 ? /* @__PURE__ */ React7.createElement(ObjectRootLabel, { direction, data }) : /* @__PURE__ */ React7.createElement(
4380
+ ObjectLabel,
4381
+ {
4382
+ direction,
4383
+ name,
4384
+ data,
4385
+ isNonenumerable
4386
+ }
4387
+ );
4388
+ }
4389
+ }
4390
+ )
4391
+ )
4392
+ ), !connectionEvents && /* @__PURE__ */ React7.createElement(
4393
+ "div",
4394
+ {
4395
+ style: {
4396
+ padding: "12px",
4397
+ fontSize: "12px",
4398
+ display: "flex",
4399
+ flexDirection: "column",
4400
+ gap: "4px"
4401
+ }
4402
+ },
4403
+ /* @__PURE__ */ React7.createElement("span", null, "No recorded events")
4404
+ ))
4405
+ );
4406
+ }
4407
+
4408
+ // src/inspector/sections/HistorySection.tsx
4409
+ import React9, { useEffect, useRef } from "react";
4410
+ import { ObjectInspector as ObjectInspector2 } from "react-inspector";
4411
+
4412
+ // src/inspector/StateInspectorButton.tsx
4413
+ import React8 from "react";
4414
+ function StateInspectorButton({
4415
+ children,
4416
+ onClick,
4417
+ style,
4418
+ theme,
4419
+ disabled
4420
+ }) {
4421
+ return /* @__PURE__ */ React8.createElement(
4422
+ "button",
4423
+ {
4424
+ type: "button",
4425
+ disabled,
4426
+ onClick: (event) => {
4427
+ event.stopPropagation();
4428
+ onClick();
4429
+ },
4430
+ style: {
4431
+ flex: "0",
4432
+ appearance: "none",
4433
+ background: "none",
4434
+ color: theme.BASE_COLOR,
4435
+ opacity: disabled ? 0.25 : 1,
4436
+ border: "none",
4437
+ fontSize: "12px",
4438
+ whiteSpace: "nowrap",
4439
+ display: "inline-flex",
4440
+ alignItems: "center",
4441
+ justifyContent: "center",
4442
+ padding: "0",
4443
+ cursor: "pointer",
4444
+ ...style
4445
+ }
4446
+ },
4447
+ children
4448
+ );
4449
+ }
4450
+
4451
+ // src/inspector/utils.ts
4452
+ function pathToString(extendedPath) {
4453
+ return extendedPath.map(
4454
+ (key) => typeof key === "object" ? `:${ellipsis(key.id.toString(), 8, "middle")}` : key
4455
+ ).map(
4456
+ (key, index) => index === 0 || typeof key === "string" && key.startsWith(":") ? key : `.${key}`
4457
+ ).join("");
4458
+ }
4459
+ function ellipsis(str, maxLength, position = "end") {
4460
+ if (str.length <= maxLength) return str;
4461
+ switch (position) {
4462
+ case "start":
4463
+ return `\u2026${str.slice(str.length - maxLength)}`;
4464
+ case "middle": {
4465
+ const halfLength = Math.floor(maxLength / 2);
4466
+ return `${str.slice(0, halfLength)}\u2026${str.slice(str.length - halfLength)}`;
4467
+ }
4468
+ case "end":
4469
+ return `${str.slice(0, maxLength)}\u2026`;
4470
+ }
4471
+ }
4472
+ function uploadFile() {
4473
+ return new Promise((resolve, reject) => {
4474
+ const input = document.createElement("input");
4475
+ input.type = "file";
4476
+ input.onchange = () => {
4477
+ const file = input.files?.[0];
4478
+ if (file) {
4479
+ const reader = new FileReader();
4480
+ reader.onload = () => {
4481
+ const buffer = reader.result;
4482
+ const blob = new Blob([buffer]);
4483
+ resolve(blob);
4484
+ };
4485
+ reader.onerror = () => {
4486
+ reject(new Error("Failed to read file"));
4487
+ };
4488
+ reader.readAsArrayBuffer(file);
4489
+ }
4490
+ };
4491
+ input.click();
4492
+ });
4493
+ }
4494
+
4495
+ // src/inspector/sections/HistorySection.tsx
4496
+ var HISTORY_ELEMENT_PREFIX = "noya-multiplayer-history-";
4497
+ function HistorySection({
4498
+ showHistory,
4499
+ setShowHistory,
4500
+ colorScheme,
4501
+ historySnapshot,
4502
+ multiplayerStateManager
4503
+ }) {
4504
+ const ref = useRef(null);
4505
+ const theme = getStateInspectorTheme(colorScheme);
4506
+ const solidBorderColor = getStateInspectorBorderColor(colorScheme);
4507
+ useEffect(() => {
4508
+ if (ref.current) {
4509
+ ref.current.scrollTop = ref.current.scrollHeight;
4510
+ }
4511
+ }, [historySnapshot]);
4512
+ useEffect(() => {
4513
+ if (!ref.current) return;
4514
+ const historyEntry = ref.current.querySelector(
4515
+ `#${HISTORY_ELEMENT_PREFIX}${historySnapshot.historyIndex}`
4516
+ );
4517
+ if (historyEntry) {
4518
+ historyEntry.scrollIntoView({
4519
+ block: "nearest",
4520
+ inline: "nearest"
4521
+ });
4522
+ }
4523
+ }, [historySnapshot.historyIndex]);
4524
+ return /* @__PURE__ */ React9.createElement(
4525
+ StateInspectorDisclosureSection,
4526
+ {
4527
+ open: showHistory,
4528
+ setOpen: setShowHistory,
4529
+ title: "History",
4530
+ colorScheme,
4531
+ right: /* @__PURE__ */ React9.createElement(
4532
+ "span",
4533
+ {
4534
+ style: {
4535
+ display: "flex",
4536
+ gap: "4px"
4537
+ }
4538
+ },
4539
+ /* @__PURE__ */ React9.createElement(
4540
+ StateInspectorButton,
4541
+ {
4542
+ disabled: !historySnapshot.canUndo,
4543
+ theme,
4544
+ onClick: () => {
4545
+ multiplayerStateManager.undo();
4546
+ }
4547
+ },
4548
+ /* @__PURE__ */ React9.createElement("span", { style: { width: "12px", height: "12px" } }, /* @__PURE__ */ React9.createElement(
4549
+ "svg",
4550
+ {
4551
+ xmlns: "http://www.w3.org/2000/svg",
4552
+ width: "1em",
4553
+ height: "1em",
4554
+ viewBox: "0 0 20 20"
4555
+ },
4556
+ /* @__PURE__ */ React9.createElement(
4557
+ "path",
4558
+ {
4559
+ fill: "currentColor",
4560
+ 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"
4561
+ }
4562
+ )
4563
+ ))
4564
+ ),
4565
+ /* @__PURE__ */ React9.createElement(
4566
+ StateInspectorButton,
4567
+ {
4568
+ disabled: !historySnapshot.canRedo,
4569
+ theme,
4570
+ onClick: () => {
4571
+ multiplayerStateManager.redo();
4572
+ }
4573
+ },
4574
+ /* @__PURE__ */ React9.createElement("span", { style: { width: "12px", height: "12px" } }, /* @__PURE__ */ React9.createElement(
4575
+ "svg",
4576
+ {
4577
+ xmlns: "http://www.w3.org/2000/svg",
4578
+ width: "1em",
4579
+ height: "1em",
4580
+ viewBox: "0 0 20 20"
4581
+ },
4582
+ /* @__PURE__ */ React9.createElement(
4583
+ "path",
4584
+ {
4585
+ fill: "currentColor",
4586
+ 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"
4587
+ }
4588
+ )
4589
+ ))
4590
+ )
4591
+ )
4592
+ },
4593
+ /* @__PURE__ */ React9.createElement(StateInspectorDisclosureRowInner, { ref }, /* @__PURE__ */ React9.createElement(
4594
+ "div",
4595
+ {
4596
+ id: `${HISTORY_ELEMENT_PREFIX}0`,
4597
+ style: {
4598
+ borderBottom: `1px solid ${solidBorderColor}`,
4599
+ fontSize: "12px",
4600
+ fontFamily: "Menlo, monospace",
4601
+ padding: "2px 12px 1px",
4602
+ display: "flex",
4603
+ alignItems: "center",
4604
+ background: historySnapshot.historyIndex === 0 ? "rgb(59 130 246 / 15%)" : void 0
4605
+ }
4606
+ },
4607
+ /* @__PURE__ */ React9.createElement(
4608
+ "span",
4609
+ {
4610
+ style: {
4611
+ fontFamily: "Menlo, monospace",
4612
+ fontSize: "11px",
4613
+ borderRadius: 4,
4614
+ display: "inline-block"
4615
+ }
4616
+ },
4617
+ "Initial state"
4618
+ )
4619
+ ), historySnapshot.history.map((entry, index) => {
4620
+ const metadata = entry.metadata;
4621
+ const { id, name, timestamp, ...rest } = metadata;
4622
+ return /* @__PURE__ */ React9.createElement(
4623
+ "div",
4624
+ {
4625
+ id: `${HISTORY_ELEMENT_PREFIX}${index + 1}`,
4626
+ key: index,
4627
+ style: {
4628
+ padding: "0px 12px 1px",
4629
+ borderBottom: `1px solid ${solidBorderColor}`,
4630
+ background: index + 1 === historySnapshot.historyIndex ? "rgb(59 130 246 / 15%)" : void 0
4631
+ }
4632
+ },
4633
+ typeof name === "string" && /* @__PURE__ */ React9.createElement(
4634
+ "pre",
4635
+ {
4636
+ style: {
4637
+ fontSize: "11px",
4638
+ display: "flex",
4639
+ flexWrap: "wrap",
4640
+ margin: 0
4641
+ }
4642
+ },
4643
+ entry.metadata.name,
4644
+ Object.keys(rest).length > 0 && /* @__PURE__ */ React9.createElement(ObjectInspector2, { data: rest, theme })
4645
+ ),
4646
+ entry.redoPatches?.map((patch, j) => /* @__PURE__ */ React9.createElement(
4647
+ "pre",
4648
+ {
4649
+ key: j,
4650
+ style: {
4651
+ fontSize: "11px",
4652
+ display: "flex",
4653
+ flexWrap: "wrap",
4654
+ margin: 0
4655
+ }
4656
+ },
4657
+ patch.op === "add" && /* @__PURE__ */ React9.createElement(React9.Fragment, null, /* @__PURE__ */ React9.createElement("span", { style: { fontStyle: "italic" } }, pathToString(patch.path)), " = ", /* @__PURE__ */ React9.createElement(ObjectInspector2, { data: patch.value, theme })),
4658
+ patch.op === "replace" && /* @__PURE__ */ React9.createElement(React9.Fragment, null, /* @__PURE__ */ React9.createElement("span", { style: { fontStyle: "italic" } }, pathToString(patch.path)), " = ", /* @__PURE__ */ React9.createElement(ObjectInspector2, { data: patch.value, theme })),
4659
+ patch.op === "remove" && /* @__PURE__ */ React9.createElement(React9.Fragment, null, /* @__PURE__ */ React9.createElement("span", { style: { color: theme.OBJECT_VALUE_STRING_COLOR } }, "delete", " "), /* @__PURE__ */ React9.createElement("span", { style: { fontStyle: "italic" } }, pathToString(patch.path))),
4660
+ patch.op === "move" && /* @__PURE__ */ React9.createElement(React9.Fragment, null, /* @__PURE__ */ React9.createElement("span", { style: { fontStyle: "italic" } }, pathToString(patch.from)), " -> ", /* @__PURE__ */ React9.createElement("span", { style: { fontStyle: "italic" } }, pathToString(patch.path)))
4661
+ ))
4662
+ );
4663
+ }))
4664
+ );
4665
+ }
4666
+
4667
+ // src/inspector/serialization.ts
4668
+ import { createHash } from "@noya-app/state-manager";
4669
+
4670
+ // src/inspector/zip/crc32.ts
4671
+ var CRC_TABLE = (() => {
4672
+ const t = new Uint32Array(256);
4673
+ for (let i = 0; i < 256; i++) {
4674
+ let c = i;
4675
+ for (let k = 0; k < 8; k++) c = c & 1 ? 3988292384 ^ c >>> 1 : c >>> 1;
4676
+ t[i] = c >>> 0;
4677
+ }
4678
+ return t;
4679
+ })();
4680
+ function crc32(u8) {
4681
+ let c = 4294967295;
4682
+ for (let i = 0; i < u8.length; i++)
4683
+ c = CRC_TABLE[(c ^ u8[i]) & 255] ^ c >>> 8;
4684
+ return (c ^ 4294967295) >>> 0;
4685
+ }
4686
+
4687
+ // src/inspector/zip/struct.ts
4688
+ var defineSchema = (s) => s;
4689
+ var defineSchemas = (m) => m;
4690
+ function sizeofFieldSpec(f, ctx) {
4691
+ if (f.type === "bytes")
4692
+ return typeof f.length === "function" ? f.length(ctx) : f.length;
4693
+ return f.type === "u8" ? 1 : f.type === "u16" ? 2 : 4;
4694
+ }
4695
+ function writeStruct(schema, values) {
4696
+ let total = 0;
4697
+ const ctx = { ...values };
4698
+ for (const f of schema) total += sizeofFieldSpec(f, ctx);
4699
+ const out = new Uint8Array(total);
4700
+ const view = new DataView(out.buffer);
4701
+ let o = 0;
4702
+ const vals = values;
4703
+ for (const f of schema) {
4704
+ if (f.type === "bytes") {
4705
+ const b = vals[f.name];
4706
+ out.set(b, o);
4707
+ o += b.length;
4708
+ continue;
4709
+ }
4710
+ const n = Number(vals[f.name]) >>> 0;
4711
+ if (f.type === "u8") {
4712
+ view.setUint8(o, n);
4713
+ o += 1;
4714
+ } else if (f.type === "u16") {
4715
+ view.setUint16(o, n, true);
4716
+ o += 2;
4717
+ } else {
4718
+ view.setUint32(o, n, true);
4719
+ o += 4;
4720
+ }
4721
+ }
4722
+ return out;
4723
+ }
4724
+ function readStruct(view, offset, schema) {
4725
+ const ctx = {};
4726
+ let o = offset;
4727
+ for (const f of schema) {
4728
+ if (f.type === "bytes") {
4729
+ const len = typeof f.length === "function" ? f.length(ctx) : f.length;
4730
+ const u8 = new Uint8Array(view.buffer, view.byteOffset + o, len);
4731
+ ctx[f.name] = new Uint8Array(u8);
4732
+ o += len;
4733
+ } else if (f.type === "u8") {
4734
+ ctx[f.name] = view.getUint8(o);
4735
+ o += 1;
4736
+ } else if (f.type === "u16") {
4737
+ ctx[f.name] = view.getUint16(o, true);
4738
+ o += 2;
4739
+ } else {
4740
+ ctx[f.name] = view.getUint32(o, true);
4741
+ o += 4;
4742
+ }
4743
+ }
4744
+ return { value: ctx, offset: o };
4745
+ }
4746
+
4747
+ // src/inspector/zip/TinyZip.ts
4748
+ var te = new TextEncoder();
4749
+ function utf8(s) {
4750
+ return te.encode(s);
4751
+ }
4752
+ function dosDateTime(d = /* @__PURE__ */ new Date()) {
4753
+ const time = d.getHours() << 11 | d.getMinutes() << 5 | Math.floor(d.getSeconds() / 2);
4754
+ const date = d.getFullYear() - 1980 << 9 | d.getMonth() + 1 << 5 | d.getDate();
4755
+ return { time, date };
4756
+ }
4757
+ var SIG = {
4758
+ LocalFile: 67324752,
4759
+ CentralFile: 33639248,
4760
+ EOCD: 101010256
4761
+ };
4762
+ var SCHEMA = defineSchemas({
4763
+ LocalFile: defineSchema([
4764
+ { name: "signature", type: "u32" },
4765
+ { name: "versionNeeded", type: "u16" },
4766
+ { name: "flags", type: "u16" },
4767
+ { name: "method", type: "u16" },
4768
+ { name: "time", type: "u16" },
4769
+ { name: "date", type: "u16" },
4770
+ { name: "crc32", type: "u32" },
4771
+ { name: "compSize", type: "u32" },
4772
+ { name: "uncompSize", type: "u32" },
4773
+ { name: "nameLen", type: "u16" },
4774
+ { name: "extraLen", type: "u16" },
4775
+ { name: "name", type: "bytes", length: (c) => c.nameLen },
4776
+ { name: "extra", type: "bytes", length: (c) => c.extraLen }
4777
+ ]),
4778
+ CentralFile: defineSchema([
4779
+ { name: "signature", type: "u32" },
4780
+ { name: "versionMadeBy", type: "u16" },
4781
+ { name: "versionNeeded", type: "u16" },
4782
+ { name: "flags", type: "u16" },
4783
+ { name: "method", type: "u16" },
4784
+ { name: "time", type: "u16" },
4785
+ { name: "date", type: "u16" },
4786
+ { name: "crc32", type: "u32" },
4787
+ { name: "compSize", type: "u32" },
4788
+ { name: "uncompSize", type: "u32" },
4789
+ { name: "nameLen", type: "u16" },
4790
+ { name: "extraLen", type: "u16" },
4791
+ { name: "commentLen", type: "u16" },
4792
+ { name: "diskStart", type: "u16" },
4793
+ { name: "intAttrs", type: "u16" },
4794
+ { name: "extAttrs", type: "u32" },
4795
+ { name: "localHeaderOffset", type: "u32" },
4796
+ { name: "name", type: "bytes", length: (c) => c.nameLen },
4797
+ { name: "extra", type: "bytes", length: (c) => c.extraLen },
4798
+ { name: "comment", type: "bytes", length: (c) => c.commentLen }
4799
+ ]),
4800
+ EOCD: defineSchema([
4801
+ { name: "signature", type: "u32" },
4802
+ { name: "diskNum", type: "u16" },
4803
+ { name: "cdStartDisk", type: "u16" },
4804
+ { name: "cdRecordsOnDisk", type: "u16" },
4805
+ { name: "cdRecordsTotal", type: "u16" },
4806
+ { name: "cdSize", type: "u32" },
4807
+ { name: "cdOffset", type: "u32" },
4808
+ { name: "commentLen", type: "u16" },
4809
+ { name: "comment", type: "bytes", length: (c) => c.commentLen }
4810
+ ])
4811
+ });
4812
+ function ensureSig(v, expect, where) {
4813
+ if (v !== expect)
4814
+ throw new Error(`Invalid signature for ${where}: 0x${v.toString(16)}`);
4815
+ }
4816
+ var TinyZip = class _TinyZip {
4817
+ _chunks = [];
4818
+ _offset = 0;
4819
+ _files = [];
4820
+ _rootPrefix = "";
4821
+ // e.g. "my-archive/" or ""
4822
+ _rootDirAdded = false;
4823
+ constructor(opts = {}) {
4824
+ if (opts.root) {
4825
+ const r = opts.root.replace(/^\/+|\/+$/g, "");
4826
+ if (r) this._rootPrefix = r + "/";
4827
+ }
4828
+ }
4829
+ _push(...arrs) {
4830
+ for (const a of arrs) {
4831
+ this._chunks.push(a);
4832
+ this._offset += a.length;
4833
+ }
4834
+ }
4835
+ _withRoot(name) {
4836
+ return this._rootPrefix && !name.startsWith(this._rootPrefix) ? this._rootPrefix + name.replace(/^\/+/, "") : name;
4837
+ }
4838
+ _ensureRootDir(date = /* @__PURE__ */ new Date()) {
4839
+ if (!this._rootPrefix || this._rootDirAdded) return;
4840
+ this._rootDirAdded = true;
4841
+ const dirName = this._rootPrefix;
4842
+ const nameBytes = utf8(dirName);
4843
+ const { time, date: dosDate } = dosDateTime(date);
4844
+ const localHeader = writeStruct(SCHEMA.LocalFile, {
4845
+ signature: SIG.LocalFile,
4846
+ versionNeeded: 20,
4847
+ flags: 2048,
4848
+ method: 0,
4849
+ time,
4850
+ date: dosDate,
4851
+ crc32: 0,
4852
+ compSize: 0,
4853
+ uncompSize: 0,
4854
+ nameLen: nameBytes.length,
4855
+ extraLen: 0,
4856
+ name: nameBytes,
4857
+ extra: new Uint8Array(0)
4858
+ });
4859
+ const localHeaderOffset = this._offset;
4860
+ this._push(localHeader);
4861
+ this._files.push({
4862
+ nameBytes,
4863
+ crc: 0,
4864
+ compSize: 0,
4865
+ uncompSize: 0,
4866
+ method: 0,
4867
+ time,
4868
+ dosDate,
4869
+ localHeaderOffset,
4870
+ externalAttrs: 16
4871
+ });
4872
+ }
4873
+ /** Add a file to the zip. "name" uses forward slashes for paths (e.g., "dir/file.txt"). */
4874
+ addFile(name, data, opts = {}) {
4875
+ const { date = /* @__PURE__ */ new Date() } = opts;
4876
+ this._ensureRootDir(date);
4877
+ const u8 = toU8(data);
4878
+ const fullName = this._withRoot(name);
4879
+ const nameBytes = utf8(fullName);
4880
+ const { time, date: dosDate } = dosDateTime(date);
4881
+ const crc = crc32(u8);
4882
+ const compSize = u8.length >>> 0;
4883
+ const uncompSize = u8.length >>> 0;
4884
+ const localHeaderOffset = this._offset;
4885
+ const localHeader = writeStruct(SCHEMA.LocalFile, {
4886
+ signature: SIG.LocalFile,
4887
+ versionNeeded: 20,
4888
+ flags: 2048,
4889
+ method: 0,
4890
+ time,
4891
+ date: dosDate,
4892
+ crc32: crc,
4893
+ compSize,
4894
+ uncompSize,
4895
+ nameLen: nameBytes.length,
4896
+ extraLen: 0,
4897
+ name: nameBytes,
4898
+ extra: new Uint8Array(0)
4899
+ });
4900
+ this._push(localHeader, u8);
4901
+ this._files.push({
4902
+ nameBytes,
4903
+ crc,
4904
+ compSize,
4905
+ uncompSize,
4906
+ method: 0,
4907
+ time,
4908
+ dosDate,
4909
+ localHeaderOffset,
4910
+ externalAttrs: 0
4911
+ });
4912
+ }
4913
+ // Create an explicit directory entry (name should end with "/").
4914
+ addDirectory(name, opts = {}) {
4915
+ const { date = /* @__PURE__ */ new Date() } = opts;
4916
+ this._ensureRootDir(date);
4917
+ const dirNameRaw = name.endsWith("/") ? name : name + "/";
4918
+ const fullName = this._withRoot(dirNameRaw);
4919
+ const nameBytes = utf8(fullName);
4920
+ const { time, date: dosDate } = dosDateTime(date);
4921
+ const localHeader = writeStruct(SCHEMA.LocalFile, {
4922
+ signature: SIG.LocalFile,
4923
+ versionNeeded: 20,
4924
+ flags: 2048,
4925
+ method: 0,
4926
+ time,
4927
+ date: dosDate,
4928
+ crc32: 0,
4929
+ compSize: 0,
4930
+ uncompSize: 0,
4931
+ nameLen: nameBytes.length,
4932
+ extraLen: 0,
4933
+ name: nameBytes,
4934
+ extra: new Uint8Array(0)
4935
+ });
4936
+ const localHeaderOffset = this._offset;
4937
+ this._push(localHeader);
4938
+ this._files.push({
4939
+ nameBytes,
4940
+ crc: 0,
4941
+ compSize: 0,
4942
+ uncompSize: 0,
4943
+ method: 0,
4944
+ time,
4945
+ dosDate,
4946
+ localHeaderOffset,
4947
+ externalAttrs: 16
4948
+ });
4949
+ }
4950
+ finish() {
4951
+ const cdStart = this._offset;
4952
+ for (const f of this._files) {
4953
+ const central = writeStruct(SCHEMA.CentralFile, {
4954
+ signature: SIG.CentralFile,
4955
+ versionMadeBy: 20,
4956
+ versionNeeded: 20,
4957
+ flags: 2048,
4958
+ method: f.method,
4959
+ time: f.time,
4960
+ date: f.dosDate,
4961
+ crc32: f.crc,
4962
+ compSize: f.compSize,
4963
+ uncompSize: f.uncompSize,
4964
+ nameLen: f.nameBytes.length,
4965
+ extraLen: 0,
4966
+ commentLen: 0,
4967
+ diskStart: 0,
4968
+ intAttrs: 0,
4969
+ extAttrs: f.externalAttrs,
4970
+ localHeaderOffset: f.localHeaderOffset,
4971
+ name: f.nameBytes,
4972
+ extra: new Uint8Array(0),
4973
+ comment: new Uint8Array(0)
4974
+ });
4975
+ this._push(central);
4976
+ }
4977
+ const cdSize = this._offset - cdStart;
4978
+ const eocd = writeStruct(SCHEMA.EOCD, {
4979
+ signature: SIG.EOCD,
4980
+ diskNum: 0,
4981
+ cdStartDisk: 0,
4982
+ cdRecordsOnDisk: this._files.length,
4983
+ cdRecordsTotal: this._files.length,
4984
+ cdSize,
4985
+ cdOffset: cdStart,
4986
+ commentLen: 0,
4987
+ comment: new Uint8Array(0)
4988
+ });
4989
+ this._push(eocd);
4990
+ const merged = new Uint8Array(this._offset);
4991
+ let offset = 0;
4992
+ for (const chunk of this._chunks) {
4993
+ merged.set(chunk, offset);
4994
+ offset += chunk.length;
4995
+ }
4996
+ return merged;
4997
+ }
4998
+ static create(files, opts = {}) {
4999
+ const zip = new _TinyZip(opts);
5000
+ for (const [name, data] of Object.entries(files)) {
5001
+ if (name.endsWith("/")) zip.addDirectory(name);
5002
+ else zip.addFile(name, data);
5003
+ }
5004
+ return zip.finish();
5005
+ }
5006
+ static parse(input) {
5007
+ const u8 = toU8(input);
5008
+ const view = new DataView(u8.buffer, u8.byteOffset, u8.byteLength);
5009
+ const eocdOff = findEOCDOffset(u8);
5010
+ const { value: eocd } = readStruct(view, eocdOff, SCHEMA.EOCD);
5011
+ ensureSig(eocd.signature, SIG.EOCD, "EOCD");
5012
+ if (eocd.commentLen !== 0) throw new Error("Unsupported: EOCD comment");
5013
+ if (eocd.diskNum !== 0 || eocd.cdStartDisk !== 0)
5014
+ throw new Error("Unsupported: multi-disk");
5015
+ const entries = [];
5016
+ let cdPtr = eocd.cdOffset;
5017
+ for (let i = 0; i < eocd.cdRecordsTotal; i++) {
5018
+ const { value: cdf, offset: next } = readStruct(
5019
+ view,
5020
+ cdPtr,
5021
+ SCHEMA.CentralFile
5022
+ );
5023
+ ensureSig(cdf.signature, SIG.CentralFile, "Central Directory");
5024
+ const name = new TextDecoder().decode(cdf.name);
5025
+ const isDir = (cdf.extAttrs & 16) !== 0 || name.endsWith("/");
5026
+ if (cdf.extraLen !== 0 || cdf.commentLen !== 0)
5027
+ throw new Error("Unsupported: extra/comment in central dir");
5028
+ entries.push({
5029
+ name,
5030
+ isDirectory: isDir,
5031
+ crc32: cdf.crc32 >>> 0,
5032
+ size: cdf.uncompSize >>> 0,
5033
+ compSize: cdf.compSize >>> 0,
5034
+ time: cdf.time >>> 0,
5035
+ dosDate: cdf.date >>> 0,
5036
+ localHeaderOffset: cdf.localHeaderOffset >>> 0
5037
+ });
5038
+ cdPtr = next;
5039
+ }
5040
+ const results = [];
5041
+ for (const e of entries) {
5042
+ const { value: lfh, offset: afterHeader } = readStruct(
5043
+ view,
5044
+ e.localHeaderOffset,
5045
+ SCHEMA.LocalFile
5046
+ );
5047
+ ensureSig(lfh.signature, SIG.LocalFile, "Local File");
5048
+ if (lfh.flags !== 2048)
5049
+ throw new Error("Unsupported: flags (expect UTF-8 only)");
5050
+ if (lfh.method !== 0)
5051
+ throw new Error("Unsupported: compression (expect store only)");
5052
+ if (lfh.extraLen !== 0)
5053
+ throw new Error("Unsupported: extra fields in local header");
5054
+ const dataStart = afterHeader;
5055
+ const dataEnd = dataStart + lfh.compSize;
5056
+ const data = e.isDirectory ? new Uint8Array(0) : new Uint8Array(u8.subarray(dataStart, dataEnd));
5057
+ results.push({ ...e, data });
5058
+ }
5059
+ return results;
5060
+ }
5061
+ // Quick utility: return a map of file data (directories omitted)
5062
+ static extract(input) {
5063
+ const entries = this.parse(input);
5064
+ const out = {};
5065
+ for (const e of entries) if (!e.isDirectory) out[e.name] = e.data;
5066
+ return out;
5067
+ }
5068
+ };
5069
+ function toU8(input) {
5070
+ if (typeof input === "string") return utf8(input);
5071
+ if (input instanceof Uint8Array) return input;
5072
+ return new Uint8Array(input);
5073
+ }
5074
+ function findEOCDOffset(u8) {
5075
+ const view = new DataView(u8.buffer, u8.byteOffset, u8.byteLength);
5076
+ const minSize = 22;
5077
+ if (u8.length >= minSize) {
5078
+ const at = u8.length - minSize;
5079
+ if (view.getUint32(at, true) === SIG.EOCD) return at;
5080
+ }
5081
+ const start = Math.max(0, u8.length - 1024);
5082
+ for (let i = u8.length - 4; i >= start; i--) {
5083
+ if (view.getUint32(i, true) === SIG.EOCD) return i;
5084
+ }
5085
+ throw new Error("EOCD not found");
5086
+ }
5087
+
5088
+ // src/inspector/serialization.ts
5089
+ async function fetchAssetMap(assets) {
5090
+ const res = await Promise.all(
5091
+ assets.map(async (asset) => {
5092
+ const url = asset.url;
5093
+ const response = await fetch(url);
5094
+ const data = await response.arrayBuffer();
5095
+ return [asset.id, data];
5096
+ })
5097
+ );
5098
+ return Object.fromEntries(res);
5099
+ }
5100
+ async function exportAll(noyaManager) {
5101
+ const { multiplayerStateManager, assetManager } = noyaManager;
5102
+ const assetMap = await fetchAssetMap(assetManager.assets$.get());
5103
+ return encodeAll({
5104
+ state: multiplayerStateManager.optimisticState$.get(),
5105
+ schema: multiplayerStateManager.schema,
5106
+ assets: assetManager.assets$.get(),
5107
+ assetMap
5108
+ });
5109
+ }
5110
+ function encodeAll({
5111
+ state,
5112
+ schema,
5113
+ assets,
5114
+ assetMap
5115
+ }) {
5116
+ const zip = new TinyZip();
5117
+ zip.addFile("state.json", JSON.stringify(state, null, 2));
5118
+ if (schema) {
5119
+ const encodedSchema = encodeSchema(schema);
5120
+ zip.addFile("schema.json", JSON.stringify(encodedSchema, null, 2));
5121
+ }
5122
+ if (assets.length) {
5123
+ zip.addFile("assets.json", JSON.stringify(assets, null, 2));
5124
+ }
5125
+ for (const [id, data] of Object.entries(assetMap)) {
5126
+ zip.addFile(`assets/${id}`, data);
5127
+ }
5128
+ return zip.finish();
5129
+ }
5130
+ async function importAll(buffer, noyaManager, {
5131
+ shouldAllowSchemaChange
5132
+ } = {}) {
5133
+ let { state, schema, assets, assetMap } = await decodeAll(buffer);
5134
+ if (shouldAllowSchemaChange && noyaManager.options.schema && schema) {
5135
+ const oldSchemaHash = createHash(noyaManager.options.schema);
5136
+ const newSchemaHash = createHash(schema);
5137
+ if (oldSchemaHash !== newSchemaHash) {
5138
+ const shouldAllow = shouldAllowSchemaChange({
5139
+ oldSchema: noyaManager.options.schema,
5140
+ newSchema: schema
5141
+ });
5142
+ if (!shouldAllow) {
5143
+ throw new Error("Schema change not allowed");
5144
+ }
5145
+ }
5146
+ }
5147
+ if (assets.length) {
5148
+ await Promise.all(
5149
+ noyaManager.assetManager.assets$.get().map((asset) => noyaManager.assetManager.delete(asset.id))
5150
+ );
5151
+ const idPairs = await Promise.all(
5152
+ assets.map(async (asset) => {
5153
+ const newAsset = await noyaManager.assetManager.create({
5154
+ data: assetMap[asset.id],
5155
+ contentType: asset.contentType
5156
+ });
5157
+ return [asset.id, newAsset.id];
5158
+ })
5159
+ );
5160
+ const replacementMap = Object.fromEntries(idPairs);
5161
+ state = replaceDeep(state, replacementMap);
5162
+ }
5163
+ noyaManager.initialState = state;
5164
+ noyaManager.forceInit();
5165
+ }
5166
+ function decodeAll(buffer) {
5167
+ const zip = TinyZip.extract(buffer);
5168
+ const stateFile = zip["state.json"];
5169
+ const schemaFile = zip["schema.json"];
5170
+ const assetsFile = zip["assets.json"];
5171
+ if (!stateFile) {
5172
+ throw new Error("State file not found");
5173
+ }
5174
+ const state = JSON.parse(new TextDecoder().decode(stateFile));
5175
+ let schema;
5176
+ if (schemaFile) {
5177
+ const encodedSchema = JSON.parse(new TextDecoder().decode(schemaFile));
5178
+ schema = decodeSchema(encodedSchema);
5179
+ }
5180
+ let assets = [];
5181
+ let assetMap = {};
5182
+ if (assetsFile) {
5183
+ assets = JSON.parse(new TextDecoder().decode(assetsFile));
5184
+ for (const asset of assets) {
5185
+ const data = zip[`assets/${asset.id}`];
5186
+ if (!data) {
5187
+ throw new Error(`Asset ${asset.id} not found`);
5188
+ }
5189
+ assetMap[asset.id] = data;
5190
+ }
5191
+ }
5192
+ return { state, schema, assets, assetMap };
5193
+ }
5194
+ function replaceDeep(obj, replacementMap) {
5195
+ switch (typeof obj) {
5196
+ case "object":
5197
+ if (obj === null) {
5198
+ return obj;
5199
+ }
5200
+ if (Array.isArray(obj)) {
5201
+ return obj.map((item) => replaceDeep(item, replacementMap));
5202
+ }
5203
+ return Object.fromEntries(
5204
+ Object.entries(obj).map(([key, value]) => [
5205
+ key,
5206
+ replaceDeep(value, replacementMap)
5207
+ ])
5208
+ );
5209
+ case "string":
5210
+ return replacementMap[obj] ?? obj;
5211
+ default:
5212
+ return obj;
5213
+ }
5214
+ }
5215
+
5216
+ // src/inspector/StateInspectorToggleButton.tsx
5217
+ import React10 from "react";
5218
+ function StateInspectorToggleButton({
5219
+ showInspector,
5220
+ setShowInspector,
5221
+ theme,
5222
+ anchor
5223
+ }) {
5224
+ const isRightAnchor = anchor === "right";
5225
+ const rightIcon = /* @__PURE__ */ React10.createElement(
5226
+ "svg",
5227
+ {
5228
+ xmlns: "http://www.w3.org/2000/svg",
5229
+ fill: "none",
5230
+ viewBox: "0 0 24 24",
5231
+ strokeWidth: 1.5,
5232
+ stroke: "currentColor",
5233
+ className: "size-6"
5234
+ },
5235
+ /* @__PURE__ */ React10.createElement(
5236
+ "path",
5237
+ {
5238
+ strokeLinecap: "round",
5239
+ strokeLinejoin: "round",
5240
+ d: "m5.25 4.5 7.5 7.5-7.5 7.5m6-15 7.5 7.5-7.5 7.5"
5241
+ }
5242
+ )
5243
+ );
5244
+ const leftIcon = /* @__PURE__ */ React10.createElement(
5245
+ "svg",
5246
+ {
5247
+ xmlns: "http://www.w3.org/2000/svg",
5248
+ fill: "none",
5249
+ viewBox: "0 0 24 24",
5250
+ strokeWidth: 1.5,
5251
+ stroke: "currentColor",
5252
+ className: "size-6"
5253
+ },
5254
+ /* @__PURE__ */ React10.createElement(
5255
+ "path",
5256
+ {
5257
+ strokeLinecap: "round",
5258
+ strokeLinejoin: "round",
5259
+ d: "m18.75 4.5-7.5 7.5 7.5 7.5m-6-15L5.25 12l7.5 7.5"
5260
+ }
5261
+ )
5262
+ );
5263
+ return /* @__PURE__ */ React10.createElement(
5264
+ "span",
5265
+ {
5266
+ role: "button",
5267
+ style: {
5268
+ flex: "0",
5269
+ appearance: "none",
5270
+ color: theme.BASE_COLOR,
5271
+ border: "none",
5272
+ fontSize: "12px",
5273
+ whiteSpace: "nowrap",
5274
+ display: "inline-flex",
5275
+ alignItems: "center",
5276
+ justifyContent: "center",
5277
+ padding: "2px 0"
5278
+ },
5279
+ onClick: (event) => {
5280
+ event.stopPropagation();
5281
+ setShowInspector(!showInspector);
5282
+ }
5283
+ },
5284
+ showInspector ? "Hide Inspector" : /* @__PURE__ */ React10.createElement("span", { style: { width: "12px", height: "12px" } }, isRightAnchor ? rightIcon : leftIcon)
5285
+ );
5286
+ }
5287
+
5288
+ // src/inspector/useLocalStorageState.tsx
5289
+ import React11 from "react";
5290
+ var localStorage = typeof window !== "undefined" ? window.localStorage : null;
5291
+ function useLocalStorageState(key, defaultValue) {
5292
+ const [state, setState] = React11.useState(() => {
5293
+ const value = localStorage?.getItem(key);
5294
+ let result = defaultValue;
5295
+ if (value) {
5296
+ try {
5297
+ result = JSON.parse(value);
5298
+ } catch (error) {
5299
+ console.error("Error parsing localStorage value", error);
5300
+ }
5301
+ }
5302
+ return result;
5303
+ });
5304
+ const setLocalStorageState = (value) => {
5305
+ localStorage?.setItem(key, JSON.stringify(value));
5306
+ setState(value);
5307
+ };
5308
+ return [state, setLocalStorageState];
5309
+ }
5310
+
5311
+ // src/inspector/StateInspector.tsx
5312
+ var StateInspector = memoGeneric(function StateInspector2({
5313
+ noyaManager,
5314
+ colorScheme = "light",
5315
+ anchor = "right",
5316
+ unstyled = false,
5317
+ ...props
5318
+ }) {
5319
+ const {
5320
+ multiplayerStateManager,
5321
+ assetManager,
5322
+ connectedUsersManager,
5323
+ secretManager,
5324
+ ephemeralUserDataManager,
5325
+ connectionEventManager,
5326
+ taskManager,
5327
+ ioManager
5328
+ } = noyaManager;
5329
+ const [didMount, setDidMount] = React12.useState(false);
5330
+ const tasks = useObservable(taskManager.tasks$);
5331
+ const inputs = useObservable(ioManager.inputs$);
5332
+ const outputTransforms = useObservable(ioManager.outputTransforms$);
5333
+ useLayoutEffect(() => {
5334
+ setDidMount(true);
5335
+ }, []);
5336
+ const eventsContainerRef = React12.useRef(null);
5337
+ const [showInspector, setShowInspector] = useLocalStorageState(
5338
+ "noya-multiplayer-react-show-inspector",
5339
+ true
5340
+ );
5341
+ const [showEvents, setShowEvents] = useLocalStorageState(
5342
+ "noya-multiplayer-react-show-events",
5343
+ false
5344
+ );
5345
+ const [showHistory, setShowHistory] = useLocalStorageState(
5346
+ "noya-multiplayer-react-show-history",
5347
+ false
5348
+ );
5349
+ const [showUsers, setShowUsers] = useLocalStorageState(
5350
+ "noya-multiplayer-react-show-users",
5351
+ true
5352
+ );
5353
+ const [showData, setShowData] = useLocalStorageState(
5354
+ "noya-multiplayer-react-show-data",
5355
+ true
5356
+ );
5357
+ const [showTasks, setShowTasks] = useLocalStorageState(
5358
+ "noya-multiplayer-react-show-tasks",
5359
+ false
5360
+ );
5361
+ const [showEphemeral, setShowEphemeral] = useLocalStorageState(
5362
+ "noya-multiplayer-react-show-ephemeral",
5363
+ false
5364
+ );
5365
+ const [showAssets, setShowAssets] = useLocalStorageState(
5366
+ "noya-multiplayer-react-show-assets",
5367
+ false
5368
+ );
5369
+ const [showSecrets, setShowSecrets] = useLocalStorageState(
5370
+ "noya-multiplayer-react-show-secrets",
5371
+ false
4328
5372
  );
4329
5373
  const [showInputs, setShowInputs] = useLocalStorageState(
4330
5374
  "noya-multiplayer-react-show-inputs",
@@ -4335,7 +5379,7 @@ var StateInspector = memoGeneric(function StateInspector2({
4335
5379
  false
4336
5380
  );
4337
5381
  const connectionEvents = useObservable(connectionEventManager.events$);
4338
- useEffect(() => {
5382
+ useEffect2(() => {
4339
5383
  if (eventsContainerRef.current) {
4340
5384
  eventsContainerRef.current.scrollTop = eventsContainerRef.current.scrollHeight;
4341
5385
  }
@@ -4356,25 +5400,8 @@ var StateInspector = memoGeneric(function StateInspector2({
4356
5400
  const outputTransformsInitialized = useObservable(
4357
5401
  ioManager.outputTransformsInitialized$
4358
5402
  );
4359
- useEffect(() => {
4360
- if (historyContainerRef.current) {
4361
- historyContainerRef.current.scrollTop = historyContainerRef.current.scrollHeight;
4362
- }
4363
- }, [historySnapshot]);
4364
- useEffect(() => {
4365
- if (!historyContainerRef.current) return;
4366
- const historyEntry = historyContainerRef.current.querySelector(
4367
- `#${HISTORY_ELEMENT_PREFIX}${historySnapshot.historyIndex}`
4368
- );
4369
- if (historyEntry) {
4370
- historyEntry.scrollIntoView({
4371
- block: "nearest",
4372
- inline: "nearest"
4373
- });
4374
- }
4375
- }, [historySnapshot.historyIndex]);
4376
- const theme = colorScheme === "light" ? lightTheme : darkTheme;
4377
- const solidBorderColor = colorScheme === "light" ? "rgb(223 223 223)" : "rgb(29 29 29)";
5403
+ const theme = getStateInspectorTheme(colorScheme);
5404
+ const solidBorderColor = getStateInspectorBorderColor(colorScheme);
4378
5405
  const baseStyle = {
4379
5406
  position: "fixed",
4380
5407
  top: 12,
@@ -4394,9 +5421,24 @@ var StateInspector = memoGeneric(function StateInspector2({
4394
5421
  zIndex: 9999,
4395
5422
  lineHeight: "13px"
4396
5423
  };
5424
+ const handleExportAll = useCallback(async () => {
5425
+ const buffer = await exportAll(noyaManager);
5426
+ downloadBlob(new Blob([buffer]), "state.zip");
5427
+ }, [noyaManager]);
5428
+ const handleImportAll = useCallback(async () => {
5429
+ const file = await uploadFile();
5430
+ const buffer = await file.arrayBuffer();
5431
+ importAll(buffer, noyaManager, {
5432
+ shouldAllowSchemaChange: () => {
5433
+ return confirm(
5434
+ `The imported state uses a different schema than the schema this app expects. Do you want to continue?`
5435
+ );
5436
+ }
5437
+ });
5438
+ }, [noyaManager]);
4397
5439
  if (!didMount) return null;
4398
5440
  if (!showInspector) {
4399
- return /* @__PURE__ */ React3.createElement(
5441
+ return /* @__PURE__ */ React12.createElement(
4400
5442
  "div",
4401
5443
  {
4402
5444
  ...props,
@@ -4408,8 +5450,8 @@ var StateInspector = memoGeneric(function StateInspector2({
4408
5450
  },
4409
5451
  onClick: () => setShowInspector(true)
4410
5452
  },
4411
- /* @__PURE__ */ React3.createElement(
4412
- ToggleButton,
5453
+ /* @__PURE__ */ React12.createElement(
5454
+ StateInspectorToggleButton,
4413
5455
  {
4414
5456
  showInspector,
4415
5457
  setShowInspector,
@@ -4419,7 +5461,7 @@ var StateInspector = memoGeneric(function StateInspector2({
4419
5461
  )
4420
5462
  );
4421
5463
  }
4422
- return /* @__PURE__ */ React3.createElement(
5464
+ return /* @__PURE__ */ React12.createElement(
4423
5465
  "div",
4424
5466
  {
4425
5467
  ...props,
@@ -4428,8 +5470,8 @@ var StateInspector = memoGeneric(function StateInspector2({
4428
5470
  ...props.style
4429
5471
  }
4430
5472
  },
4431
- /* @__PURE__ */ React3.createElement(
4432
- DisclosureSection,
5473
+ /* @__PURE__ */ React12.createElement(
5474
+ StateInspectorDisclosureSection,
4433
5475
  {
4434
5476
  isFirst: true,
4435
5477
  open: showUsers,
@@ -4440,8 +5482,8 @@ var StateInspector = memoGeneric(function StateInspector2({
4440
5482
  flex: "0 0 auto",
4441
5483
  maxHeight: "200px"
4442
5484
  },
4443
- right: /* @__PURE__ */ React3.createElement(
4444
- ToggleButton,
5485
+ right: /* @__PURE__ */ React12.createElement(
5486
+ StateInspectorToggleButton,
4445
5487
  {
4446
5488
  showInspector,
4447
5489
  setShowInspector,
@@ -4450,14 +5492,14 @@ var StateInspector = memoGeneric(function StateInspector2({
4450
5492
  }
4451
5493
  )
4452
5494
  },
4453
- /* @__PURE__ */ React3.createElement("div", { style: { ...styles.sectionInner, flex: "0 0 auto" } }, connectedUsers?.map((user) => /* @__PURE__ */ React3.createElement(
4454
- InspectorRow,
5495
+ /* @__PURE__ */ React12.createElement(StateInspectorDisclosureRowInner, { style: { flex: "0 0 auto" } }, connectedUsers?.map((user) => /* @__PURE__ */ React12.createElement(
5496
+ StateInspectorRow,
4455
5497
  {
4456
5498
  key: user.id,
4457
5499
  colorScheme,
4458
5500
  variant: user.id === userId ? "up" : void 0
4459
5501
  },
4460
- user.image && /* @__PURE__ */ React3.createElement(
5502
+ user.image && /* @__PURE__ */ React12.createElement(
4461
5503
  "img",
4462
5504
  {
4463
5505
  src: user.image,
@@ -4478,7 +5520,7 @@ var StateInspector = memoGeneric(function StateInspector2({
4478
5520
  " (",
4479
5521
  ellipsis(user.id.toString(), 8, "middle"),
4480
5522
  ")"
4481
- )), !connectedUsers && /* @__PURE__ */ React3.createElement(
5523
+ )), !connectedUsers && /* @__PURE__ */ React12.createElement(
4482
5524
  "div",
4483
5525
  {
4484
5526
  style: {
@@ -4489,13 +5531,13 @@ var StateInspector = memoGeneric(function StateInspector2({
4489
5531
  gap: "4px"
4490
5532
  }
4491
5533
  },
4492
- /* @__PURE__ */ React3.createElement("span", null, "No connected users")
5534
+ /* @__PURE__ */ React12.createElement("span", null, "No connected users")
4493
5535
  ))
4494
5536
  ),
4495
- /* @__PURE__ */ React3.createElement(
4496
- DisclosureSection,
5537
+ /* @__PURE__ */ React12.createElement(
5538
+ StateInspectorDisclosureSection,
4497
5539
  {
4498
- title: /* @__PURE__ */ React3.createElement(TitleLabel, null, "Data", /* @__PURE__ */ React3.createElement(
5540
+ title: /* @__PURE__ */ React12.createElement(TitleLabel, null, "Data", /* @__PURE__ */ React12.createElement(
4499
5541
  ColoredDot,
4500
5542
  {
4501
5543
  type: multipeerStateInitialized ? "success" : "error"
@@ -4504,16 +5546,18 @@ var StateInspector = memoGeneric(function StateInspector2({
4504
5546
  colorScheme,
4505
5547
  setOpen: setShowData,
4506
5548
  open: showData,
4507
- right: /* @__PURE__ */ React3.createElement(
5549
+ right: /* @__PURE__ */ React12.createElement(
4508
5550
  "span",
4509
5551
  {
4510
5552
  style: {
4511
5553
  display: "flex",
4512
- gap: "4px"
5554
+ gap: "12px"
4513
5555
  }
4514
5556
  },
4515
- /* @__PURE__ */ React3.createElement(
4516
- SmallButton,
5557
+ /* @__PURE__ */ React12.createElement(StateInspectorButton, { theme, onClick: handleImportAll }, "Import"),
5558
+ /* @__PURE__ */ React12.createElement(StateInspectorButton, { theme, onClick: handleExportAll }, "Export"),
5559
+ /* @__PURE__ */ React12.createElement(
5560
+ StateInspectorButton,
4517
5561
  {
4518
5562
  theme,
4519
5563
  onClick: () => {
@@ -4524,15 +5568,15 @@ var StateInspector = memoGeneric(function StateInspector2({
4524
5568
  )
4525
5569
  )
4526
5570
  },
4527
- /* @__PURE__ */ React3.createElement("div", { style: styles.sectionInner }, /* @__PURE__ */ React3.createElement(InspectorRow, { colorScheme }, /* @__PURE__ */ React3.createElement(
4528
- ObjectInspector,
5571
+ /* @__PURE__ */ React12.createElement(StateInspectorDisclosureRowInner, null, /* @__PURE__ */ React12.createElement(StateInspectorRow, { colorScheme }, /* @__PURE__ */ React12.createElement(
5572
+ ObjectInspector3,
4529
5573
  {
4530
5574
  name: multiplayerStateManager.schema ? "state" : void 0,
4531
5575
  data: state,
4532
5576
  theme
4533
5577
  }
4534
- )), multiplayerStateManager.schema && /* @__PURE__ */ React3.createElement(InspectorRow, { colorScheme }, /* @__PURE__ */ React3.createElement(
4535
- ObjectInspector,
5578
+ )), multiplayerStateManager.schema && /* @__PURE__ */ React12.createElement(StateInspectorRow, { colorScheme }, /* @__PURE__ */ React12.createElement(
5579
+ ObjectInspector3,
4536
5580
  {
4537
5581
  name: "schema",
4538
5582
  data: multiplayerStateManager.schema,
@@ -4540,151 +5584,25 @@ var StateInspector = memoGeneric(function StateInspector2({
4540
5584
  }
4541
5585
  )))
4542
5586
  ),
4543
- /* @__PURE__ */ React3.createElement(
4544
- DisclosureSection,
5587
+ /* @__PURE__ */ React12.createElement(
5588
+ HistorySection,
4545
5589
  {
4546
- open: showHistory,
4547
- setOpen: setShowHistory,
4548
- title: "History",
5590
+ showHistory,
5591
+ setShowHistory,
4549
5592
  colorScheme,
4550
- right: /* @__PURE__ */ React3.createElement(
4551
- "span",
4552
- {
4553
- style: {
4554
- display: "flex",
4555
- gap: "4px"
4556
- }
4557
- },
4558
- /* @__PURE__ */ React3.createElement(
4559
- SmallButton,
4560
- {
4561
- disabled: !historySnapshot.canUndo,
4562
- theme,
4563
- onClick: () => {
4564
- multiplayerStateManager.undo();
4565
- }
4566
- },
4567
- /* @__PURE__ */ React3.createElement("span", { style: { width: "12px", height: "12px" } }, /* @__PURE__ */ React3.createElement(
4568
- "svg",
4569
- {
4570
- xmlns: "http://www.w3.org/2000/svg",
4571
- width: "1em",
4572
- height: "1em",
4573
- viewBox: "0 0 20 20"
4574
- },
4575
- /* @__PURE__ */ React3.createElement(
4576
- "path",
4577
- {
4578
- fill: "currentColor",
4579
- 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"
4580
- }
4581
- )
4582
- ))
4583
- ),
4584
- /* @__PURE__ */ React3.createElement(
4585
- SmallButton,
4586
- {
4587
- disabled: !historySnapshot.canRedo,
4588
- theme,
4589
- onClick: () => {
4590
- multiplayerStateManager.redo();
4591
- }
4592
- },
4593
- /* @__PURE__ */ React3.createElement("span", { style: { width: "12px", height: "12px" } }, /* @__PURE__ */ React3.createElement(
4594
- "svg",
4595
- {
4596
- xmlns: "http://www.w3.org/2000/svg",
4597
- width: "1em",
4598
- height: "1em",
4599
- viewBox: "0 0 20 20"
4600
- },
4601
- /* @__PURE__ */ React3.createElement(
4602
- "path",
4603
- {
4604
- fill: "currentColor",
4605
- 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"
4606
- }
4607
- )
4608
- ))
4609
- )
4610
- )
4611
- },
4612
- /* @__PURE__ */ React3.createElement("div", { ref: historyContainerRef, style: styles.sectionInner }, /* @__PURE__ */ React3.createElement(
4613
- "div",
4614
- {
4615
- id: `${HISTORY_ELEMENT_PREFIX}0`,
4616
- style: {
4617
- borderBottom: `1px solid ${solidBorderColor}`,
4618
- fontSize: "12px",
4619
- fontFamily: "Menlo, monospace",
4620
- padding: "2px 12px 1px",
4621
- display: "flex",
4622
- alignItems: "center",
4623
- background: historySnapshot.historyIndex === 0 ? "rgb(59 130 246 / 15%)" : void 0
4624
- }
4625
- },
4626
- /* @__PURE__ */ React3.createElement(
4627
- "span",
4628
- {
4629
- style: {
4630
- fontFamily: "Menlo, monospace",
4631
- fontSize: "11px",
4632
- borderRadius: 4,
4633
- display: "inline-block"
4634
- }
4635
- },
4636
- "Initial state"
4637
- )
4638
- ), historySnapshot.history.map((entry, index) => /* @__PURE__ */ React3.createElement(
4639
- "div",
4640
- {
4641
- id: `${HISTORY_ELEMENT_PREFIX}${index + 1}`,
4642
- key: index,
4643
- style: {
4644
- padding: "0px 12px 1px",
4645
- borderBottom: `1px solid ${solidBorderColor}`,
4646
- background: index + 1 === historySnapshot.historyIndex ? "rgb(59 130 246 / 15%)" : void 0
4647
- }
4648
- },
4649
- "name" in entry.metadata && typeof entry.metadata.name === "string" && /* @__PURE__ */ React3.createElement(
4650
- "pre",
4651
- {
4652
- style: {
4653
- fontSize: "11px",
4654
- display: "flex",
4655
- flexWrap: "wrap",
4656
- margin: 0
4657
- }
4658
- },
4659
- entry.metadata.name
4660
- ),
4661
- entry.redoPatches?.map((patch, j) => /* @__PURE__ */ React3.createElement(
4662
- "pre",
4663
- {
4664
- key: j,
4665
- style: {
4666
- fontSize: "11px",
4667
- display: "flex",
4668
- flexWrap: "wrap",
4669
- margin: 0
4670
- }
4671
- },
4672
- patch.op === "add" && /* @__PURE__ */ React3.createElement(React3.Fragment, null, /* @__PURE__ */ React3.createElement("span", { style: { fontStyle: "italic" } }, pathToString(patch.path)), " = ", /* @__PURE__ */ React3.createElement(ObjectInspector, { data: patch.value, theme })),
4673
- patch.op === "replace" && /* @__PURE__ */ React3.createElement(React3.Fragment, null, /* @__PURE__ */ React3.createElement("span", { style: { fontStyle: "italic" } }, pathToString(patch.path)), " = ", /* @__PURE__ */ React3.createElement(ObjectInspector, { data: patch.value, theme })),
4674
- patch.op === "remove" && /* @__PURE__ */ React3.createElement(React3.Fragment, null, /* @__PURE__ */ React3.createElement("span", { style: { color: theme.OBJECT_VALUE_STRING_COLOR } }, "delete", " "), /* @__PURE__ */ React3.createElement("span", { style: { fontStyle: "italic" } }, pathToString(patch.path))),
4675
- patch.op === "move" && /* @__PURE__ */ React3.createElement(React3.Fragment, null, /* @__PURE__ */ React3.createElement("span", { style: { fontStyle: "italic" } }, pathToString(patch.from)), " -> ", /* @__PURE__ */ React3.createElement("span", { style: { fontStyle: "italic" } }, pathToString(patch.path)))
4676
- ))
4677
- )))
5593
+ historySnapshot,
5594
+ multiplayerStateManager
5595
+ }
4678
5596
  ),
4679
- /* @__PURE__ */ React3.createElement(
4680
- DisclosureSection,
5597
+ /* @__PURE__ */ React12.createElement(
5598
+ StateInspectorDisclosureSection,
4681
5599
  {
4682
5600
  open: showAssets,
4683
5601
  setOpen: setShowAssets,
4684
- title: /* @__PURE__ */ React3.createElement(TitleLabel, null, "Assets", /* @__PURE__ */ React3.createElement(ColoredDot, { type: assetsInitialized ? "success" : "error" })),
5602
+ title: /* @__PURE__ */ React12.createElement(TitleLabel, null, "Assets (", assets.length, ")", /* @__PURE__ */ React12.createElement(ColoredDot, { type: assetsInitialized ? "success" : "error" })),
4685
5603
  colorScheme,
4686
- right: /* @__PURE__ */ React3.createElement("span", { style: { display: "flex", gap: "10px" } }, /* @__PURE__ */ React3.createElement(
4687
- SmallButton,
5604
+ right: /* @__PURE__ */ React12.createElement("span", { style: { display: "flex", gap: "10px" } }, /* @__PURE__ */ React12.createElement(
5605
+ StateInspectorButton,
4688
5606
  {
4689
5607
  theme,
4690
5608
  onClick: () => {
@@ -4696,8 +5614,8 @@ var StateInspector = memoGeneric(function StateInspector2({
4696
5614
  }
4697
5615
  },
4698
5616
  "Reload"
4699
- ), /* @__PURE__ */ React3.createElement(
4700
- SmallButton,
5617
+ ), /* @__PURE__ */ React12.createElement(
5618
+ StateInspectorButton,
4701
5619
  {
4702
5620
  theme,
4703
5621
  onClick: () => {
@@ -4720,8 +5638,8 @@ var StateInspector = memoGeneric(function StateInspector2({
4720
5638
  "Upload"
4721
5639
  ))
4722
5640
  },
4723
- /* @__PURE__ */ React3.createElement("div", { style: styles.sectionInner }, assets.map((asset) => /* @__PURE__ */ React3.createElement(InspectorRow, { key: asset.id, colorScheme }, /* @__PURE__ */ React3.createElement(ObjectInspector, { name: asset.id, data: asset, theme }), /* @__PURE__ */ React3.createElement(
4724
- SmallButton,
5641
+ /* @__PURE__ */ React12.createElement(StateInspectorDisclosureRowInner, null, assets.map((asset) => /* @__PURE__ */ React12.createElement(StateInspectorRow, { key: asset.id, colorScheme }, /* @__PURE__ */ React12.createElement(ObjectInspector3, { name: asset.id, data: asset, theme }), /* @__PURE__ */ React12.createElement(
5642
+ StateInspectorButton,
4725
5643
  {
4726
5644
  theme,
4727
5645
  onClick: () => assetManager.delete(asset.id)
@@ -4729,15 +5647,15 @@ var StateInspector = memoGeneric(function StateInspector2({
4729
5647
  "Delete"
4730
5648
  ))))
4731
5649
  ),
4732
- /* @__PURE__ */ React3.createElement(
4733
- DisclosureSection,
5650
+ /* @__PURE__ */ React12.createElement(
5651
+ StateInspectorDisclosureSection,
4734
5652
  {
4735
- title: /* @__PURE__ */ React3.createElement(TitleLabel, null, "Secrets", /* @__PURE__ */ React3.createElement(ColoredDot, { type: secretsInitialized ? "success" : "error" })),
5653
+ title: /* @__PURE__ */ React12.createElement(TitleLabel, null, "Secrets", /* @__PURE__ */ React12.createElement(ColoredDot, { type: secretsInitialized ? "success" : "error" })),
4736
5654
  colorScheme,
4737
5655
  open: showSecrets,
4738
5656
  setOpen: setShowSecrets,
4739
- right: /* @__PURE__ */ React3.createElement(
4740
- SmallButton,
5657
+ right: /* @__PURE__ */ React12.createElement(
5658
+ StateInspectorButton,
4741
5659
  {
4742
5660
  theme,
4743
5661
  onClick: () => {
@@ -4751,8 +5669,8 @@ var StateInspector = memoGeneric(function StateInspector2({
4751
5669
  "Create"
4752
5670
  )
4753
5671
  },
4754
- /* @__PURE__ */ React3.createElement("div", { style: styles.sectionInner }, secrets.map((secret) => /* @__PURE__ */ React3.createElement(InspectorRow, { key: secret.id, colorScheme }, /* @__PURE__ */ React3.createElement(ObjectInspector, { data: secret, theme }), /* @__PURE__ */ React3.createElement(
4755
- SmallButton,
5672
+ /* @__PURE__ */ React12.createElement(StateInspectorDisclosureRowInner, null, secrets.map((secret) => /* @__PURE__ */ React12.createElement(StateInspectorRow, { key: secret.id, colorScheme }, /* @__PURE__ */ React12.createElement(ObjectInspector3, { data: secret, theme }), /* @__PURE__ */ React12.createElement(
5673
+ StateInspectorButton,
4756
5674
  {
4757
5675
  theme,
4758
5676
  onClick: () => secretManager.deleteSecret(secret.id)
@@ -4761,15 +5679,15 @@ var StateInspector = memoGeneric(function StateInspector2({
4761
5679
  ))))
4762
5680
  ),
4763
5681
  " ",
4764
- /* @__PURE__ */ React3.createElement(
4765
- DisclosureSection,
5682
+ /* @__PURE__ */ React12.createElement(
5683
+ StateInspectorDisclosureSection,
4766
5684
  {
4767
5685
  open: showInputs,
4768
5686
  setOpen: setShowInputs,
4769
- title: /* @__PURE__ */ React3.createElement(TitleLabel, null, "Inputs", /* @__PURE__ */ React3.createElement(ColoredDot, { type: inputsInitialized ? "success" : "error" })),
5687
+ title: /* @__PURE__ */ React12.createElement(TitleLabel, null, "Inputs", /* @__PURE__ */ React12.createElement(ColoredDot, { type: inputsInitialized ? "success" : "error" })),
4770
5688
  colorScheme
4771
5689
  },
4772
- /* @__PURE__ */ React3.createElement("div", { style: styles.sectionInner }, inputs?.map((input) => /* @__PURE__ */ React3.createElement(InspectorRow, { key: input.id, colorScheme }, /* @__PURE__ */ React3.createElement(ObjectInspector, { data: input, theme }))), !inputs?.length && /* @__PURE__ */ React3.createElement(
5690
+ /* @__PURE__ */ React12.createElement(StateInspectorDisclosureRowInner, null, inputs?.map((input) => /* @__PURE__ */ React12.createElement(StateInspectorRow, { key: input.id, colorScheme }, /* @__PURE__ */ React12.createElement(ObjectInspector3, { data: input, theme }))), !inputs?.length && /* @__PURE__ */ React12.createElement(
4773
5691
  "div",
4774
5692
  {
4775
5693
  style: {
@@ -4780,15 +5698,15 @@ var StateInspector = memoGeneric(function StateInspector2({
4780
5698
  gap: "4px"
4781
5699
  }
4782
5700
  },
4783
- /* @__PURE__ */ React3.createElement("span", null, "No inputs")
5701
+ /* @__PURE__ */ React12.createElement("span", null, "No inputs")
4784
5702
  ))
4785
5703
  ),
4786
- /* @__PURE__ */ React3.createElement(
4787
- DisclosureSection,
5704
+ /* @__PURE__ */ React12.createElement(
5705
+ StateInspectorDisclosureSection,
4788
5706
  {
4789
5707
  open: showOutputTransforms,
4790
5708
  setOpen: setShowOutputTransforms,
4791
- title: /* @__PURE__ */ React3.createElement(TitleLabel, null, "Output Transforms", /* @__PURE__ */ React3.createElement(
5709
+ title: /* @__PURE__ */ React12.createElement(TitleLabel, null, "Output Transforms", /* @__PURE__ */ React12.createElement(
4792
5710
  ColoredDot,
4793
5711
  {
4794
5712
  type: outputTransformsInitialized ? "success" : "error"
@@ -4796,7 +5714,7 @@ var StateInspector = memoGeneric(function StateInspector2({
4796
5714
  )),
4797
5715
  colorScheme
4798
5716
  },
4799
- /* @__PURE__ */ React3.createElement("div", { style: styles.sectionInner }, outputTransforms?.map((transform) => /* @__PURE__ */ React3.createElement(InspectorRow, { key: transform.id, colorScheme }, /* @__PURE__ */ React3.createElement(ObjectInspector, { data: transform, theme }))), !outputTransforms?.length && /* @__PURE__ */ React3.createElement(
5717
+ /* @__PURE__ */ React12.createElement(StateInspectorDisclosureRowInner, null, outputTransforms?.map((transform) => /* @__PURE__ */ React12.createElement(StateInspectorRow, { key: transform.id, colorScheme }, /* @__PURE__ */ React12.createElement(ObjectInspector3, { data: transform, theme }))), !outputTransforms?.length && /* @__PURE__ */ React12.createElement(
4800
5718
  "div",
4801
5719
  {
4802
5720
  style: {
@@ -4807,19 +5725,19 @@ var StateInspector = memoGeneric(function StateInspector2({
4807
5725
  gap: "4px"
4808
5726
  }
4809
5727
  },
4810
- /* @__PURE__ */ React3.createElement("span", null, "No output transforms")
5728
+ /* @__PURE__ */ React12.createElement("span", null, "No output transforms")
4811
5729
  ))
4812
5730
  ),
4813
- /* @__PURE__ */ React3.createElement(
4814
- DisclosureSection,
5731
+ /* @__PURE__ */ React12.createElement(
5732
+ StateInspectorDisclosureSection,
4815
5733
  {
4816
5734
  title: "Tasks",
4817
5735
  colorScheme,
4818
5736
  open: showTasks,
4819
5737
  setOpen: setShowTasks
4820
5738
  },
4821
- /* @__PURE__ */ React3.createElement("div", { style: styles.sectionInner }, tasks?.map((task) => /* @__PURE__ */ React3.createElement(
4822
- InspectorRow,
5739
+ /* @__PURE__ */ React12.createElement(StateInspectorDisclosureRowInner, null, tasks?.map((task) => /* @__PURE__ */ React12.createElement(
5740
+ StateInspectorRow,
4823
5741
  {
4824
5742
  key: task.id,
4825
5743
  colorScheme,
@@ -4827,8 +5745,8 @@ var StateInspector = memoGeneric(function StateInspector2({
4827
5745
  backgroundColor: task.status === "done" ? "rgba(0,255,0,0.2)" : task.status === "error" ? "rgba(255,0,0,0.2)" : void 0
4828
5746
  }
4829
5747
  },
4830
- /* @__PURE__ */ React3.createElement(
4831
- ObjectInspector,
5748
+ /* @__PURE__ */ React12.createElement(
5749
+ ObjectInspector3,
4832
5750
  {
4833
5751
  name: task.name,
4834
5752
  data: task.payload,
@@ -4837,16 +5755,16 @@ var StateInspector = memoGeneric(function StateInspector2({
4837
5755
  )
4838
5756
  )))
4839
5757
  ),
4840
- /* @__PURE__ */ React3.createElement(
4841
- DisclosureSection,
5758
+ /* @__PURE__ */ React12.createElement(
5759
+ StateInspectorDisclosureSection,
4842
5760
  {
4843
5761
  open: showEphemeral,
4844
5762
  setOpen: setShowEphemeral,
4845
5763
  title: "Ephemeral User Data",
4846
5764
  colorScheme
4847
5765
  },
4848
- /* @__PURE__ */ React3.createElement("div", { style: styles.sectionInner }, Object.entries(ephemeral).map(([key, value]) => /* @__PURE__ */ React3.createElement(InspectorRow, { key, colorScheme }, /* @__PURE__ */ React3.createElement(
4849
- ObjectInspector,
5766
+ /* @__PURE__ */ React12.createElement(StateInspectorDisclosureRowInner, null, Object.entries(ephemeral).map(([key, value]) => /* @__PURE__ */ React12.createElement(StateInspectorRow, { key, colorScheme }, /* @__PURE__ */ React12.createElement(
5767
+ ObjectInspector3,
4850
5768
  {
4851
5769
  name: key,
4852
5770
  data: value,
@@ -4855,187 +5773,20 @@ var StateInspector = memoGeneric(function StateInspector2({
4855
5773
  }
4856
5774
  ))))
4857
5775
  ),
4858
- /* @__PURE__ */ React3.createElement(
4859
- DisclosureSection,
5776
+ /* @__PURE__ */ React12.createElement(
5777
+ EventsSection,
4860
5778
  {
4861
- open: showEvents,
4862
- setOpen: setShowEvents,
4863
- title: "Events",
4864
- colorScheme
4865
- },
4866
- /* @__PURE__ */ React3.createElement("div", { ref: eventsContainerRef, style: styles.sectionInner }, connectionEvents?.map(
4867
- (event, index) => event.type === "stateChange" ? /* @__PURE__ */ React3.createElement(InspectorRow, { key: index, colorScheme }, "connection:", " ", /* @__PURE__ */ React3.createElement("span", { style: { fontStyle: "italic" } }, event.state.toLowerCase())) : /* @__PURE__ */ React3.createElement(
4868
- InspectorRow,
4869
- {
4870
- key: index,
4871
- colorScheme,
4872
- variant: event.type === "send" ? "up" : "down",
4873
- style: {
4874
- padding: "0px 12px 1px"
4875
- }
4876
- },
4877
- /* @__PURE__ */ React3.createElement(
4878
- ObjectInspector,
4879
- {
4880
- data: event.type === "error" ? event.error : event.message,
4881
- theme,
4882
- nodeRenderer: ({
4883
- depth,
4884
- name,
4885
- data,
4886
- isNonenumerable
4887
- }) => {
4888
- const direction = event.type === "send" ? "up" : "down";
4889
- return depth === 0 ? /* @__PURE__ */ React3.createElement(ObjectRootLabel, { direction, data }) : /* @__PURE__ */ React3.createElement(
4890
- ObjectLabel,
4891
- {
4892
- direction,
4893
- name,
4894
- data,
4895
- isNonenumerable
4896
- }
4897
- );
4898
- }
4899
- }
4900
- )
4901
- )
4902
- ), !connectionEvents && /* @__PURE__ */ React3.createElement(
4903
- "div",
4904
- {
4905
- style: {
4906
- padding: "12px",
4907
- fontSize: "12px",
4908
- display: "flex",
4909
- flexDirection: "column",
4910
- gap: "4px"
4911
- }
4912
- },
4913
- /* @__PURE__ */ React3.createElement("span", null, "No recorded events")
4914
- ))
5779
+ connectionEvents,
5780
+ colorScheme,
5781
+ eventsContainerRef,
5782
+ showEvents,
5783
+ setShowEvents
5784
+ }
4915
5785
  )
4916
5786
  );
4917
5787
  });
4918
- var ObjectRootLabel = ({ name, data, direction }) => {
4919
- if (typeof name === "string") {
4920
- return /* @__PURE__ */ React3.createElement("span", null, /* @__PURE__ */ React3.createElement(ObjectName, { name }), /* @__PURE__ */ React3.createElement("span", null, ": "), /* @__PURE__ */ React3.createElement(ObjectPreview, { data }));
4921
- }
4922
- if (direction === "up" || direction === "down") {
4923
- const arrow = direction === "up" ? "\u2191" : "\u2193";
4924
- return /* @__PURE__ */ React3.createElement("span", null, /* @__PURE__ */ React3.createElement(
4925
- "span",
4926
- {
4927
- style: {
4928
- background: direction === "up" ? "rgba(0,255,0,0.2)" : "rgba(255,0,0,0.2)",
4929
- // color: "white",
4930
- width: 12,
4931
- height: 12,
4932
- borderRadius: 2,
4933
- display: "inline-flex",
4934
- justifyContent: "center",
4935
- alignItems: "center",
4936
- marginRight: 4,
4937
- lineHeight: "12px"
4938
- }
4939
- },
4940
- arrow
4941
- ), /* @__PURE__ */ React3.createElement(ObjectPreview, { data }));
4942
- } else {
4943
- return /* @__PURE__ */ React3.createElement(ObjectPreview, { data });
4944
- }
4945
- };
4946
- function Arrow({
4947
- expanded,
4948
- onClick,
4949
- style
4950
- }) {
4951
- return /* @__PURE__ */ React3.createElement(
4952
- "span",
4953
- {
4954
- role: "button",
4955
- onClick,
4956
- style: {
4957
- display: "inline-block",
4958
- textAlign: "center",
4959
- transform: expanded ? "rotate(0deg)" : "rotate(-90deg)",
4960
- fontFamily: "Menlo, monospace",
4961
- ...style
4962
- }
4963
- },
4964
- "\u25BC"
4965
- );
4966
- }
4967
- function pathToString(extendedPath) {
4968
- return extendedPath.map(
4969
- (key) => typeof key === "object" ? `:${ellipsis(key.id.toString(), 8, "middle")}` : key
4970
- ).map(
4971
- (key, index) => index === 0 || typeof key === "string" && key.startsWith(":") ? key : `.${key}`
4972
- ).join("");
4973
- }
4974
- function ellipsis(str, maxLength, position = "end") {
4975
- if (str.length <= maxLength) return str;
4976
- switch (position) {
4977
- case "start":
4978
- return `\u2026${str.slice(str.length - maxLength)}`;
4979
- case "middle":
4980
- const halfLength = Math.floor(maxLength / 2);
4981
- return `${str.slice(0, halfLength)}\u2026${str.slice(str.length - halfLength)}`;
4982
- case "end":
4983
- return `${str.slice(0, maxLength)}\u2026`;
4984
- }
4985
- }
4986
- function SmallButton({
4987
- children,
4988
- onClick,
4989
- style,
4990
- theme,
4991
- disabled
4992
- }) {
4993
- return /* @__PURE__ */ React3.createElement(
4994
- "button",
4995
- {
4996
- type: "button",
4997
- disabled,
4998
- onClick: (event) => {
4999
- event.stopPropagation();
5000
- onClick();
5001
- },
5002
- style: {
5003
- flex: "0",
5004
- appearance: "none",
5005
- background: "none",
5006
- color: theme.BASE_COLOR,
5007
- opacity: disabled ? 0.25 : 1,
5008
- border: "none",
5009
- fontSize: "12px",
5010
- whiteSpace: "nowrap",
5011
- display: "inline-flex",
5012
- alignItems: "center",
5013
- justifyContent: "center",
5014
- padding: "0",
5015
- cursor: "pointer",
5016
- ...style
5017
- }
5018
- },
5019
- children
5020
- );
5021
- }
5022
- function ColoredDot({ type }) {
5023
- return /* @__PURE__ */ React3.createElement(
5024
- "span",
5025
- {
5026
- style: {
5027
- display: "inline-block",
5028
- width: 6,
5029
- height: 6,
5030
- background: type === "success" ? "rgba(0,200,0,0.8)" : "rgba(200,0,0,0.8)",
5031
- borderRadius: "50%",
5032
- verticalAlign: "middle"
5033
- }
5034
- }
5035
- );
5036
- }
5037
5788
  function TitleLabel({ children }) {
5038
- return /* @__PURE__ */ React3.createElement("span", { style: { display: "flex", alignItems: "center", gap: "4px" } }, children);
5789
+ return /* @__PURE__ */ React12.createElement("span", { style: { display: "flex", alignItems: "center", gap: "4px" } }, children);
5039
5790
  }
5040
5791
 
5041
5792
  // src/inspector/useStateInspector.tsx
@@ -5053,7 +5804,7 @@ function useStateInspector({
5053
5804
  colorScheme,
5054
5805
  anchor
5055
5806
  }) {
5056
- const [root, setRoot] = React4.useState(null);
5807
+ const [root, setRoot] = React13.useState(null);
5057
5808
  useIsomorphicLayoutEffect(() => {
5058
5809
  if (disabled) return;
5059
5810
  const { host, container } = createShadowRootElement();
@@ -5067,7 +5818,7 @@ function useStateInspector({
5067
5818
  useIsomorphicLayoutEffect(() => {
5068
5819
  if (!root) return;
5069
5820
  root.render(
5070
- /* @__PURE__ */ React4.createElement(
5821
+ /* @__PURE__ */ React13.createElement(
5071
5822
  StateInspector,
5072
5823
  {
5073
5824
  colorScheme,
@@ -5081,7 +5832,7 @@ function useStateInspector({
5081
5832
 
5082
5833
  // src/hooks.ts
5083
5834
  function useSyncStateManager(stateManager, path) {
5084
- const getSnapshot = useCallback(
5835
+ const getSnapshot = useCallback2(
5085
5836
  () => typeof path === "string" ? stateManager.getState(path) : stateManager.getState(),
5086
5837
  [stateManager, path]
5087
5838
  );
@@ -5096,7 +5847,7 @@ function useManagedState(createInitialState, options) {
5096
5847
  () => new StateManager(createInitialState(), options)
5097
5848
  );
5098
5849
  const state = useSyncStateManager(stateManager);
5099
- const setState = useCallback(
5850
+ const setState = useCallback2(
5100
5851
  (...args) => {
5101
5852
  const [metadata, action] = args.length === 1 ? [void 0, args[0]] : args;
5102
5853
  if (metadata === void 0) {
@@ -5145,7 +5896,7 @@ function useMultiplayerState(initialState, options) {
5145
5896
  ...rest
5146
5897
  } = options ?? {};
5147
5898
  const [noyaManager] = useState(
5148
- () => new NoyaManager(initialState, rest)
5899
+ () => new NoyaManager2(initialState, rest)
5149
5900
  );
5150
5901
  const state = useObservable(
5151
5902
  noyaManager.multiplayerStateManager.optimisticState$
@@ -5170,8 +5921,8 @@ function useMultiplayerState(initialState, options) {
5170
5921
  assets
5171
5922
  };
5172
5923
  }, [noyaManager, assets]);
5173
- const syncRef = useRef(sync);
5174
- useEffect2(() => {
5924
+ const syncRef = useRef2(sync);
5925
+ useEffect3(() => {
5175
5926
  if (syncRef.current) {
5176
5927
  return syncRef.current({ noyaManager });
5177
5928
  }
@@ -5186,7 +5937,7 @@ function useMultiplayerState(initialState, options) {
5186
5937
  }
5187
5938
  function useErrorOverlay(noyaManager) {
5188
5939
  const unrecoverableError = useObservable(noyaManager.unrecoverableError);
5189
- useEffect2(() => {
5940
+ useEffect3(() => {
5190
5941
  if (!unrecoverableError) return;
5191
5942
  const el = document.createElement("div");
5192
5943
  el.id = "noya-multiplayer-react-error-overlay";
@@ -5337,7 +6088,7 @@ function useSecret(name) {
5337
6088
  const { noyaManager } = useAnyNoyaStateContext();
5338
6089
  return useObservable(
5339
6090
  noyaManager.secretManager.secrets$,
5340
- useCallback2((secrets) => secrets.find((s) => s.name === name), [name])
6091
+ useCallback3((secrets) => secrets.find((s) => s.name === name), [name])
5341
6092
  );
5342
6093
  }
5343
6094
  function useInputs() {
@@ -5355,7 +6106,7 @@ function useIOManager() {
5355
6106
  function usePublish(callback) {
5356
6107
  const { noyaManager } = useAnyNoyaStateContext();
5357
6108
  const stableCallback = useStableCallback(callback);
5358
- useEffect3(() => {
6109
+ useEffect4(() => {
5359
6110
  noyaManager.publishingManager.setPublishingEnabled(true);
5360
6111
  noyaManager.publishingManager.getFilesToPublish = stableCallback;
5361
6112
  return () => {
@@ -5435,17 +6186,17 @@ function createNoyaContext({
5435
6186
  }),
5436
6187
  [noyaManager, theme, viewType]
5437
6188
  );
5438
- return /* @__PURE__ */ React5.createElement(AnyNoyaStateContext.Provider, { value: contextValue }, /* @__PURE__ */ React5.createElement(
6189
+ return /* @__PURE__ */ React14.createElement(AnyNoyaStateContext.Provider, { value: contextValue }, /* @__PURE__ */ React14.createElement(
5439
6190
  ConnectedUsersContext.Provider,
5440
6191
  {
5441
6192
  value: noyaManager.connectedUsersManager
5442
6193
  },
5443
- /* @__PURE__ */ React5.createElement(
6194
+ /* @__PURE__ */ React14.createElement(
5444
6195
  EphemeralUserDataManagerContext.Provider,
5445
6196
  {
5446
6197
  value: noyaManager.ephemeralUserDataManager
5447
6198
  },
5448
- options.fallback !== void 0 ? /* @__PURE__ */ React5.createElement(FallbackUntilInitialized, { fallback: options.fallback }, children) : children
6199
+ options.fallback !== void 0 ? /* @__PURE__ */ React14.createElement(FallbackUntilInitialized, { fallback: options.fallback }, children) : children
5449
6200
  )
5450
6201
  ));
5451
6202
  }
@@ -5465,7 +6216,7 @@ function createNoyaContext({
5465
6216
  }
5466
6217
  function useSetValue(path) {
5467
6218
  const { noyaManager } = useAnyNoyaStateContext();
5468
- const setValue = useCallback2(
6219
+ const setValue = useCallback3(
5469
6220
  (...args) => {
5470
6221
  const [metadata, value] = args.length === 2 ? args : [{}, args[0]];
5471
6222
  noyaManager.multiplayerStateManager.setStateAtPath(
@@ -5506,7 +6257,7 @@ function createNoyaContext({
5506
6257
  }
5507
6258
  function useSetLeftMenuItems(leftMenuItems) {
5508
6259
  const { menuManager } = useNoyaManager();
5509
- useEffect3(() => {
6260
+ useEffect4(() => {
5510
6261
  if (leftMenuItems) {
5511
6262
  menuManager.setLeftMenuItems(leftMenuItems);
5512
6263
  }
@@ -5518,7 +6269,7 @@ function createNoyaContext({
5518
6269
  }
5519
6270
  function useSetRightMenuItems(rightMenuItems) {
5520
6271
  const { menuManager } = useNoyaManager();
5521
- useEffect3(() => {
6272
+ useEffect4(() => {
5522
6273
  if (rightMenuItems) {
5523
6274
  menuManager.setRightMenuItems(rightMenuItems);
5524
6275
  }
@@ -5526,19 +6277,19 @@ function createNoyaContext({
5526
6277
  }
5527
6278
  function useHandleMenuItem(callback) {
5528
6279
  const { menuManager } = useNoyaManager();
5529
- useEffect3(() => {
6280
+ useEffect4(() => {
5530
6281
  return menuManager.addListener(callback);
5531
6282
  }, [menuManager, callback]);
5532
6283
  }
5533
6284
  function useOnSelectMenuItemCallback() {
5534
6285
  const { menuManager } = useNoyaManager();
5535
- return useCallback2((type) => menuManager.emit(type), [menuManager]);
6286
+ return useCallback3((type) => menuManager.emit(type), [menuManager]);
5536
6287
  }
5537
6288
  function useMenu(options) {
5538
6289
  const { leftMenuItems, rightMenuItems, onSelectMenuItem } = options;
5539
6290
  useSetLeftMenuItems(leftMenuItems);
5540
6291
  useSetRightMenuItems(rightMenuItems);
5541
- const noop = useCallback2(() => {
6292
+ const noop = useCallback3(() => {
5542
6293
  }, []);
5543
6294
  useHandleMenuItem(onSelectMenuItem ?? noop);
5544
6295
  }
@@ -5564,7 +6315,7 @@ function createNoyaContext({
5564
6315
  // src/ai.ts
5565
6316
  function useRegisterAITool(tool) {
5566
6317
  const aiManager = useAIManager();
5567
- useEffect4(() => {
6318
+ useEffect5(() => {
5568
6319
  return aiManager.registerTool(tool);
5569
6320
  }, [aiManager, tool]);
5570
6321
  }
@@ -5576,7 +6327,7 @@ function useCallableAITools() {
5576
6327
  // src/components/UserPointersOverlay.tsx
5577
6328
  import { Observable as Observable2 } from "@noya-app/observable";
5578
6329
  import { memoGeneric as memoGeneric2 } from "@noya-app/react-utils";
5579
- import React6, { useEffect as useEffect5, useMemo as useMemo5, useState as useState3 } from "react";
6330
+ import React15, { useEffect as useEffect6, useMemo as useMemo5, useState as useState3 } from "react";
5580
6331
  function shouldShow(hideAfter, updatedAt) {
5581
6332
  return !!updatedAt && Date.now() - updatedAt < hideAfter;
5582
6333
  }
@@ -5597,7 +6348,7 @@ var UserPointerInternal = memoGeneric2(function UserPointerInternal2({
5597
6348
  const [, setForceUpdate] = useState3(0);
5598
6349
  const updatedAt = metadata?.updatedAt ?? 0;
5599
6350
  const show = shouldShow(hideAfter, updatedAt);
5600
- useEffect5(() => {
6351
+ useEffect6(() => {
5601
6352
  if (!show) return;
5602
6353
  const timeoutId = setTimeout(() => {
5603
6354
  setForceUpdate((prev) => prev + 1);
@@ -5619,9 +6370,9 @@ var UserPointersOverlay = memoGeneric2(function UserPointers({
5619
6370
  }) {
5620
6371
  const currentUserId = useObservable(ephemeralUserDataManager.currentUserId$);
5621
6372
  const connectedUsers = useObservable(connectedUsersManager.connectedUsers$);
5622
- return /* @__PURE__ */ React6.createElement(React6.Fragment, null, connectedUsers.map((user) => {
6373
+ return /* @__PURE__ */ React15.createElement(React15.Fragment, null, connectedUsers.map((user) => {
5623
6374
  if (user.id === currentUserId) return null;
5624
- return /* @__PURE__ */ React6.createElement(
6375
+ return /* @__PURE__ */ React15.createElement(
5625
6376
  UserPointerInternal,
5626
6377
  {
5627
6378
  key: user.id,
@@ -5723,9 +6474,15 @@ export {
5723
6474
  WebSocketConnection,
5724
6475
  createDefaultAppData,
5725
6476
  createNoyaContext,
6477
+ decodeAll,
6478
+ encodeAll,
5726
6479
  enforceSchema,
6480
+ exportAll,
6481
+ fetchAssetMap,
5727
6482
  getAppData,
6483
+ importAll,
5728
6484
  parseAppDataParameter,
6485
+ replaceDeep,
5729
6486
  useAIManager,
5730
6487
  useAnyEphemeralUserData,
5731
6488
  useAnyNoyaManager,