@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/.eslintrc.js +1 -5
- package/.turbo/turbo-build.log +11 -11
- package/CHANGELOG.md +19 -0
- package/dist/index.bundle.js +50 -17
- package/dist/index.d.mts +62 -22
- package/dist/index.d.ts +62 -22
- package/dist/index.js +1352 -585
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1350 -593
- package/dist/index.mjs.map +1 -1
- package/package.json +4 -4
- package/src/NoyaStateContext.tsx +23 -9
- package/src/__tests__/serialize.test.ts +126 -0
- package/src/ai.ts +2 -0
- package/src/index.ts +1 -0
- package/src/inspector/ColoredDot.tsx +17 -0
- package/src/inspector/ObjectRootLabel.tsx +48 -0
- package/src/inspector/StateInspector.tsx +125 -721
- package/src/inspector/StateInspectorArrow.tsx +28 -0
- package/src/inspector/StateInspectorButton.tsx +45 -0
- package/src/inspector/StateInspectorDisclosureSection.tsx +97 -0
- package/src/inspector/StateInspectorRow.tsx +57 -0
- package/src/inspector/StateInspectorToggleButton.tsx +82 -0
- package/src/inspector/inspectorTheme.ts +21 -0
- package/src/inspector/sections/EventsSection.tsx +89 -0
- package/src/inspector/sections/HistorySection.tsx +236 -0
- package/src/inspector/serialization.ts +202 -0
- package/src/inspector/utils.ts +60 -0
- package/src/inspector/zip/TinyZip.ts +464 -0
- package/src/inspector/zip/crc32.ts +16 -0
- package/src/inspector/zip/struct.ts +117 -0
- package/src/noyaApp.ts +2 -1
- package/src/useObservable.ts +2 -0
|
@@ -1,246 +1,35 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
3
|
import { Input, OutputTransform } from "@noya-app/noya-schemas";
|
|
4
|
-
import { memoGeneric } from "@noya-app/react-utils";
|
|
5
|
-
import {
|
|
4
|
+
import { downloadBlob, memoGeneric } from "@noya-app/react-utils";
|
|
5
|
+
import { NoyaManager } from "@noya-app/state-manager";
|
|
6
6
|
import React, {
|
|
7
7
|
CSSProperties,
|
|
8
8
|
ComponentPropsWithoutRef,
|
|
9
|
-
|
|
9
|
+
useCallback,
|
|
10
10
|
useEffect,
|
|
11
11
|
useLayoutEffect,
|
|
12
12
|
} from "react";
|
|
13
|
-
import {
|
|
14
|
-
ObjectInspector,
|
|
15
|
-
ObjectLabel,
|
|
16
|
-
ObjectName,
|
|
17
|
-
ObjectPreview,
|
|
18
|
-
chromeDark,
|
|
19
|
-
chromeLight,
|
|
20
|
-
} from "react-inspector";
|
|
13
|
+
import { ObjectInspector } from "react-inspector";
|
|
21
14
|
import { useManagedHistory } from "../hooks";
|
|
22
15
|
import { useObservable } from "../useObservable";
|
|
16
|
+
import { ColoredDot } from "./ColoredDot";
|
|
17
|
+
import {
|
|
18
|
+
getStateInspectorBorderColor,
|
|
19
|
+
getStateInspectorTheme,
|
|
20
|
+
} from "./inspectorTheme";
|
|
21
|
+
import { EventsSection } from "./sections/EventsSection";
|
|
22
|
+
import { HistorySection } from "./sections/HistorySection";
|
|
23
|
+
import { exportAll, importAll } from "./serialization";
|
|
24
|
+
import { StateInspectorButton } from "./StateInspectorButton";
|
|
25
|
+
import {
|
|
26
|
+
StateInspectorDisclosureRowInner,
|
|
27
|
+
StateInspectorDisclosureSection,
|
|
28
|
+
} from "./StateInspectorDisclosureSection";
|
|
29
|
+
import { StateInspectorRow } from "./StateInspectorRow";
|
|
30
|
+
import { StateInspectorToggleButton } from "./StateInspectorToggleButton";
|
|
23
31
|
import { useLocalStorageState } from "./useLocalStorageState";
|
|
24
|
-
|
|
25
|
-
const lightTheme = {
|
|
26
|
-
...chromeLight,
|
|
27
|
-
BASE_BACKGROUND_COLOR: "transparent",
|
|
28
|
-
OBJECT_NAME_COLOR: "rgba(0,0,0,0.7)",
|
|
29
|
-
} as any;
|
|
30
|
-
|
|
31
|
-
const darkTheme = {
|
|
32
|
-
...chromeDark,
|
|
33
|
-
BASE_BACKGROUND_COLOR: "transparent",
|
|
34
|
-
OBJECT_NAME_COLOR: "rgba(255,255,255,0.7)",
|
|
35
|
-
} as any;
|
|
36
|
-
|
|
37
|
-
const styles = {
|
|
38
|
-
sectionInner: {
|
|
39
|
-
flex: "1 1 0",
|
|
40
|
-
overflowY: "auto",
|
|
41
|
-
overflowX: "hidden",
|
|
42
|
-
display: "flex",
|
|
43
|
-
flexDirection: "column",
|
|
44
|
-
},
|
|
45
|
-
} as const;
|
|
46
|
-
|
|
47
|
-
function ToggleButton({
|
|
48
|
-
showInspector,
|
|
49
|
-
setShowInspector,
|
|
50
|
-
theme,
|
|
51
|
-
anchor,
|
|
52
|
-
}: {
|
|
53
|
-
showInspector: boolean;
|
|
54
|
-
setShowInspector: (value: boolean) => void;
|
|
55
|
-
theme: typeof chromeLight;
|
|
56
|
-
anchor: StateInspectorAnchor;
|
|
57
|
-
}) {
|
|
58
|
-
const isRightAnchor = anchor === "right";
|
|
59
|
-
|
|
60
|
-
const rightIcon = (
|
|
61
|
-
<svg
|
|
62
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
63
|
-
fill="none"
|
|
64
|
-
viewBox="0 0 24 24"
|
|
65
|
-
strokeWidth={1.5}
|
|
66
|
-
stroke="currentColor"
|
|
67
|
-
className="size-6"
|
|
68
|
-
>
|
|
69
|
-
<path
|
|
70
|
-
strokeLinecap="round"
|
|
71
|
-
strokeLinejoin="round"
|
|
72
|
-
d="m5.25 4.5 7.5 7.5-7.5 7.5m6-15 7.5 7.5-7.5 7.5"
|
|
73
|
-
/>
|
|
74
|
-
</svg>
|
|
75
|
-
);
|
|
76
|
-
|
|
77
|
-
const leftIcon = (
|
|
78
|
-
<svg
|
|
79
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
80
|
-
fill="none"
|
|
81
|
-
viewBox="0 0 24 24"
|
|
82
|
-
strokeWidth={1.5}
|
|
83
|
-
stroke="currentColor"
|
|
84
|
-
className="size-6"
|
|
85
|
-
>
|
|
86
|
-
<path
|
|
87
|
-
strokeLinecap="round"
|
|
88
|
-
strokeLinejoin="round"
|
|
89
|
-
d="m18.75 4.5-7.5 7.5 7.5 7.5m-6-15L5.25 12l7.5 7.5"
|
|
90
|
-
/>
|
|
91
|
-
</svg>
|
|
92
|
-
);
|
|
93
|
-
|
|
94
|
-
return (
|
|
95
|
-
<span
|
|
96
|
-
role="button"
|
|
97
|
-
style={{
|
|
98
|
-
flex: "0",
|
|
99
|
-
appearance: "none",
|
|
100
|
-
color: theme.BASE_COLOR,
|
|
101
|
-
border: "none",
|
|
102
|
-
fontSize: "12px",
|
|
103
|
-
whiteSpace: "nowrap",
|
|
104
|
-
display: "inline-flex",
|
|
105
|
-
alignItems: "center",
|
|
106
|
-
justifyContent: "center",
|
|
107
|
-
padding: "2px 0",
|
|
108
|
-
}}
|
|
109
|
-
onClick={(event) => {
|
|
110
|
-
event.stopPropagation();
|
|
111
|
-
|
|
112
|
-
setShowInspector(!showInspector);
|
|
113
|
-
}}
|
|
114
|
-
>
|
|
115
|
-
{showInspector ? (
|
|
116
|
-
"Hide Inspector"
|
|
117
|
-
) : (
|
|
118
|
-
<span style={{ width: "12px", height: "12px" }}>
|
|
119
|
-
{isRightAnchor ? rightIcon : leftIcon}
|
|
120
|
-
</span>
|
|
121
|
-
)}
|
|
122
|
-
</span>
|
|
123
|
-
);
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
function DisclosureSection({
|
|
127
|
-
open,
|
|
128
|
-
setOpen,
|
|
129
|
-
title,
|
|
130
|
-
right,
|
|
131
|
-
children,
|
|
132
|
-
colorScheme,
|
|
133
|
-
isFirst,
|
|
134
|
-
style,
|
|
135
|
-
}: {
|
|
136
|
-
open: boolean;
|
|
137
|
-
setOpen?: (value: boolean) => void;
|
|
138
|
-
title: React.ReactNode;
|
|
139
|
-
right?: React.ReactNode;
|
|
140
|
-
children: React.ReactNode;
|
|
141
|
-
colorScheme: "light" | "dark";
|
|
142
|
-
isFirst?: boolean;
|
|
143
|
-
style?: CSSProperties;
|
|
144
|
-
}) {
|
|
145
|
-
const theme = colorScheme === "light" ? lightTheme : darkTheme;
|
|
146
|
-
|
|
147
|
-
const borderColor =
|
|
148
|
-
colorScheme === "light" ? "rgba(0,0,0,0.1)" : "rgba(255,255,255,0.1)";
|
|
149
|
-
|
|
150
|
-
return (
|
|
151
|
-
<div
|
|
152
|
-
style={{
|
|
153
|
-
flex: open ? "1 1 0" : "0",
|
|
154
|
-
display: "flex",
|
|
155
|
-
flexDirection: "column",
|
|
156
|
-
...style,
|
|
157
|
-
}}
|
|
158
|
-
>
|
|
159
|
-
<div
|
|
160
|
-
onClick={() => setOpen?.(!open)}
|
|
161
|
-
style={{
|
|
162
|
-
cursor: "default",
|
|
163
|
-
fontSize: "12px",
|
|
164
|
-
padding: "4px 10px",
|
|
165
|
-
display: "flex",
|
|
166
|
-
alignItems: "center",
|
|
167
|
-
...(!isFirst && { borderTop: `1px solid ${borderColor}` }),
|
|
168
|
-
...(open && { borderBottom: `1px solid ${borderColor}` }),
|
|
169
|
-
}}
|
|
170
|
-
>
|
|
171
|
-
{setOpen && (
|
|
172
|
-
<Arrow
|
|
173
|
-
expanded={open}
|
|
174
|
-
style={{
|
|
175
|
-
fontSize: theme.ARROW_FONT_SIZE,
|
|
176
|
-
marginRight: theme.ARROW_MARGIN_RIGHT + 1,
|
|
177
|
-
color: theme.ARROW_COLOR,
|
|
178
|
-
}}
|
|
179
|
-
/>
|
|
180
|
-
)}
|
|
181
|
-
<span style={{ flex: "1 1 0", userSelect: "none" }}>{title}</span>
|
|
182
|
-
{right}
|
|
183
|
-
</div>
|
|
184
|
-
{open && children}
|
|
185
|
-
</div>
|
|
186
|
-
);
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
function InspectorRow({
|
|
190
|
-
children,
|
|
191
|
-
colorScheme,
|
|
192
|
-
selected,
|
|
193
|
-
style,
|
|
194
|
-
variant,
|
|
195
|
-
}: {
|
|
196
|
-
children: React.ReactNode;
|
|
197
|
-
colorScheme: "light" | "dark";
|
|
198
|
-
selected?: boolean;
|
|
199
|
-
style?: CSSProperties;
|
|
200
|
-
variant?: "up" | "down";
|
|
201
|
-
}) {
|
|
202
|
-
const solidBorderColor =
|
|
203
|
-
colorScheme === "light" ? "rgb(223 223 223)" : "rgb(29 29 29)";
|
|
204
|
-
|
|
205
|
-
return (
|
|
206
|
-
<div
|
|
207
|
-
style={{
|
|
208
|
-
borderBottom: `1px solid ${solidBorderColor}`,
|
|
209
|
-
fontSize: "12px",
|
|
210
|
-
fontFamily: "Menlo, monospace",
|
|
211
|
-
padding: "2px 12px 1px",
|
|
212
|
-
display: "flex",
|
|
213
|
-
alignItems: "center",
|
|
214
|
-
background:
|
|
215
|
-
variant === "up"
|
|
216
|
-
? "rgba(0,255,0,0.1)"
|
|
217
|
-
: variant === "down"
|
|
218
|
-
? "transparent"
|
|
219
|
-
: selected
|
|
220
|
-
? "rgb(59 130 246 / 15%)"
|
|
221
|
-
: undefined,
|
|
222
|
-
// background:
|
|
223
|
-
// colorScheme === "light"
|
|
224
|
-
// ? "rgba(0,0,0,0.05)"
|
|
225
|
-
// : "rgba(255,255,255,0.05)",
|
|
226
|
-
...style,
|
|
227
|
-
}}
|
|
228
|
-
>
|
|
229
|
-
<span
|
|
230
|
-
style={{
|
|
231
|
-
fontFamily: "Menlo, monospace",
|
|
232
|
-
fontSize: "11px",
|
|
233
|
-
borderRadius: 4,
|
|
234
|
-
display: "inline-block",
|
|
235
|
-
}}
|
|
236
|
-
>
|
|
237
|
-
{children}
|
|
238
|
-
</span>
|
|
239
|
-
</div>
|
|
240
|
-
);
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
const HISTORY_ELEMENT_PREFIX = "noya-multiplayer-history-";
|
|
32
|
+
import { ellipsis, uploadFile } from "./utils";
|
|
244
33
|
|
|
245
34
|
export type StateInspectorAnchor =
|
|
246
35
|
| "left"
|
|
@@ -290,7 +79,6 @@ export const StateInspector = memoGeneric(function StateInspector<
|
|
|
290
79
|
}, []);
|
|
291
80
|
|
|
292
81
|
const eventsContainerRef = React.useRef<HTMLDivElement>(null);
|
|
293
|
-
const historyContainerRef = React.useRef<HTMLDivElement>(null);
|
|
294
82
|
|
|
295
83
|
const [showInspector, setShowInspector] = useLocalStorageState(
|
|
296
84
|
"noya-multiplayer-react-show-inspector",
|
|
@@ -363,34 +151,8 @@ export const StateInspector = memoGeneric(function StateInspector<
|
|
|
363
151
|
ioManager.outputTransformsInitialized$
|
|
364
152
|
);
|
|
365
153
|
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
historyContainerRef.current.scrollTop =
|
|
369
|
-
historyContainerRef.current.scrollHeight;
|
|
370
|
-
}
|
|
371
|
-
}, [historySnapshot]);
|
|
372
|
-
|
|
373
|
-
// If history index changes, scroll to the new history entry into view
|
|
374
|
-
useEffect(() => {
|
|
375
|
-
if (!historyContainerRef.current) return;
|
|
376
|
-
|
|
377
|
-
const historyEntry = historyContainerRef.current.querySelector(
|
|
378
|
-
`#${HISTORY_ELEMENT_PREFIX}${historySnapshot.historyIndex}`
|
|
379
|
-
);
|
|
380
|
-
|
|
381
|
-
if (historyEntry) {
|
|
382
|
-
historyEntry.scrollIntoView({
|
|
383
|
-
block: "nearest",
|
|
384
|
-
inline: "nearest",
|
|
385
|
-
});
|
|
386
|
-
}
|
|
387
|
-
}, [historySnapshot.historyIndex]);
|
|
388
|
-
|
|
389
|
-
const theme = colorScheme === "light" ? lightTheme : darkTheme;
|
|
390
|
-
// const borderColor =
|
|
391
|
-
// colorScheme === "light" ? "rgba(0,0,0,0.1)" : "rgba(255,255,255,0.1)";
|
|
392
|
-
const solidBorderColor =
|
|
393
|
-
colorScheme === "light" ? "rgb(223 223 223)" : "rgb(29 29 29)";
|
|
154
|
+
const theme = getStateInspectorTheme(colorScheme);
|
|
155
|
+
const solidBorderColor = getStateInspectorBorderColor(colorScheme);
|
|
394
156
|
|
|
395
157
|
const baseStyle: CSSProperties = {
|
|
396
158
|
position: "fixed",
|
|
@@ -413,6 +175,25 @@ export const StateInspector = memoGeneric(function StateInspector<
|
|
|
413
175
|
lineHeight: "13px",
|
|
414
176
|
};
|
|
415
177
|
|
|
178
|
+
const handleExportAll = useCallback(async () => {
|
|
179
|
+
const buffer = await exportAll(noyaManager);
|
|
180
|
+
|
|
181
|
+
downloadBlob(new Blob([buffer]), "state.zip");
|
|
182
|
+
}, [noyaManager]);
|
|
183
|
+
|
|
184
|
+
const handleImportAll = useCallback(async () => {
|
|
185
|
+
const file = await uploadFile();
|
|
186
|
+
const buffer = await file.arrayBuffer();
|
|
187
|
+
|
|
188
|
+
importAll(buffer, noyaManager, {
|
|
189
|
+
shouldAllowSchemaChange: () => {
|
|
190
|
+
return confirm(
|
|
191
|
+
`The imported state uses a different schema than the schema this app expects. Do you want to continue?`
|
|
192
|
+
);
|
|
193
|
+
},
|
|
194
|
+
});
|
|
195
|
+
}, [noyaManager]);
|
|
196
|
+
|
|
416
197
|
if (!didMount) return null;
|
|
417
198
|
|
|
418
199
|
if (!showInspector) {
|
|
@@ -429,7 +210,7 @@ export const StateInspector = memoGeneric(function StateInspector<
|
|
|
429
210
|
}}
|
|
430
211
|
onClick={() => setShowInspector(true)}
|
|
431
212
|
>
|
|
432
|
-
<
|
|
213
|
+
<StateInspectorToggleButton
|
|
433
214
|
showInspector={showInspector}
|
|
434
215
|
setShowInspector={setShowInspector}
|
|
435
216
|
theme={theme}
|
|
@@ -447,7 +228,7 @@ export const StateInspector = memoGeneric(function StateInspector<
|
|
|
447
228
|
...props.style,
|
|
448
229
|
}}
|
|
449
230
|
>
|
|
450
|
-
<
|
|
231
|
+
<StateInspectorDisclosureSection
|
|
451
232
|
isFirst
|
|
452
233
|
open={showUsers}
|
|
453
234
|
setOpen={setShowUsers}
|
|
@@ -458,7 +239,7 @@ export const StateInspector = memoGeneric(function StateInspector<
|
|
|
458
239
|
maxHeight: "200px",
|
|
459
240
|
}}
|
|
460
241
|
right={
|
|
461
|
-
<
|
|
242
|
+
<StateInspectorToggleButton
|
|
462
243
|
showInspector={showInspector}
|
|
463
244
|
setShowInspector={setShowInspector}
|
|
464
245
|
theme={theme}
|
|
@@ -466,9 +247,9 @@ export const StateInspector = memoGeneric(function StateInspector<
|
|
|
466
247
|
/>
|
|
467
248
|
}
|
|
468
249
|
>
|
|
469
|
-
<
|
|
250
|
+
<StateInspectorDisclosureRowInner style={{ flex: "0 0 auto" }}>
|
|
470
251
|
{connectedUsers?.map((user) => (
|
|
471
|
-
<
|
|
252
|
+
<StateInspectorRow
|
|
472
253
|
key={user.id}
|
|
473
254
|
colorScheme={colorScheme}
|
|
474
255
|
variant={user.id === userId ? "up" : undefined}
|
|
@@ -490,7 +271,7 @@ export const StateInspector = memoGeneric(function StateInspector<
|
|
|
490
271
|
/>
|
|
491
272
|
)}
|
|
492
273
|
{user.name} ({ellipsis(user.id.toString(), 8, "middle")})
|
|
493
|
-
</
|
|
274
|
+
</StateInspectorRow>
|
|
494
275
|
))}
|
|
495
276
|
{!connectedUsers && (
|
|
496
277
|
<div
|
|
@@ -505,9 +286,9 @@ export const StateInspector = memoGeneric(function StateInspector<
|
|
|
505
286
|
<span>No connected users</span>
|
|
506
287
|
</div>
|
|
507
288
|
)}
|
|
508
|
-
</
|
|
509
|
-
</
|
|
510
|
-
<
|
|
289
|
+
</StateInspectorDisclosureRowInner>
|
|
290
|
+
</StateInspectorDisclosureSection>
|
|
291
|
+
<StateInspectorDisclosureSection
|
|
511
292
|
title={
|
|
512
293
|
<TitleLabel>
|
|
513
294
|
Data
|
|
@@ -523,10 +304,16 @@ export const StateInspector = memoGeneric(function StateInspector<
|
|
|
523
304
|
<span
|
|
524
305
|
style={{
|
|
525
306
|
display: "flex",
|
|
526
|
-
gap: "
|
|
307
|
+
gap: "12px",
|
|
527
308
|
}}
|
|
528
309
|
>
|
|
529
|
-
<
|
|
310
|
+
<StateInspectorButton theme={theme} onClick={handleImportAll}>
|
|
311
|
+
Import
|
|
312
|
+
</StateInspectorButton>
|
|
313
|
+
<StateInspectorButton theme={theme} onClick={handleExportAll}>
|
|
314
|
+
Export
|
|
315
|
+
</StateInspectorButton>
|
|
316
|
+
<StateInspectorButton
|
|
530
317
|
// disabled={!historySnapshot.canUndo}
|
|
531
318
|
theme={theme}
|
|
532
319
|
onClick={() => {
|
|
@@ -534,207 +321,49 @@ export const StateInspector = memoGeneric(function StateInspector<
|
|
|
534
321
|
}}
|
|
535
322
|
>
|
|
536
323
|
Reset
|
|
537
|
-
</
|
|
324
|
+
</StateInspectorButton>
|
|
538
325
|
</span>
|
|
539
326
|
}
|
|
540
327
|
>
|
|
541
|
-
<
|
|
542
|
-
<
|
|
328
|
+
<StateInspectorDisclosureRowInner>
|
|
329
|
+
<StateInspectorRow colorScheme={colorScheme}>
|
|
543
330
|
<ObjectInspector
|
|
544
331
|
name={multiplayerStateManager.schema ? "state" : undefined}
|
|
545
332
|
data={state}
|
|
546
333
|
theme={theme}
|
|
547
334
|
/>
|
|
548
|
-
</
|
|
335
|
+
</StateInspectorRow>
|
|
549
336
|
{multiplayerStateManager.schema && (
|
|
550
|
-
<
|
|
337
|
+
<StateInspectorRow colorScheme={colorScheme}>
|
|
551
338
|
<ObjectInspector
|
|
552
339
|
name="schema"
|
|
553
340
|
data={multiplayerStateManager.schema}
|
|
554
341
|
theme={theme}
|
|
555
342
|
/>
|
|
556
|
-
</
|
|
343
|
+
</StateInspectorRow>
|
|
557
344
|
)}
|
|
558
|
-
</
|
|
559
|
-
</
|
|
560
|
-
<
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
title="History"
|
|
345
|
+
</StateInspectorDisclosureRowInner>
|
|
346
|
+
</StateInspectorDisclosureSection>
|
|
347
|
+
<HistorySection
|
|
348
|
+
showHistory={showHistory}
|
|
349
|
+
setShowHistory={setShowHistory}
|
|
564
350
|
colorScheme={colorScheme}
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
gap: "4px",
|
|
570
|
-
}}
|
|
571
|
-
>
|
|
572
|
-
<SmallButton
|
|
573
|
-
disabled={!historySnapshot.canUndo}
|
|
574
|
-
theme={theme}
|
|
575
|
-
onClick={() => {
|
|
576
|
-
multiplayerStateManager.undo();
|
|
577
|
-
}}
|
|
578
|
-
>
|
|
579
|
-
<span style={{ width: "12px", height: "12px" }}>
|
|
580
|
-
<svg
|
|
581
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
582
|
-
width="1em"
|
|
583
|
-
height="1em"
|
|
584
|
-
viewBox="0 0 20 20"
|
|
585
|
-
>
|
|
586
|
-
<path
|
|
587
|
-
fill="currentColor"
|
|
588
|
-
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"
|
|
589
|
-
/>
|
|
590
|
-
</svg>
|
|
591
|
-
</span>
|
|
592
|
-
</SmallButton>
|
|
593
|
-
<SmallButton
|
|
594
|
-
disabled={!historySnapshot.canRedo}
|
|
595
|
-
theme={theme}
|
|
596
|
-
onClick={() => {
|
|
597
|
-
multiplayerStateManager.redo();
|
|
598
|
-
}}
|
|
599
|
-
>
|
|
600
|
-
<span style={{ width: "12px", height: "12px" }}>
|
|
601
|
-
<svg
|
|
602
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
603
|
-
width="1em"
|
|
604
|
-
height="1em"
|
|
605
|
-
viewBox="0 0 20 20"
|
|
606
|
-
>
|
|
607
|
-
<path
|
|
608
|
-
fill="currentColor"
|
|
609
|
-
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"
|
|
610
|
-
/>
|
|
611
|
-
</svg>
|
|
612
|
-
</span>
|
|
613
|
-
</SmallButton>
|
|
614
|
-
</span>
|
|
615
|
-
}
|
|
616
|
-
>
|
|
617
|
-
<div ref={historyContainerRef} style={styles.sectionInner}>
|
|
618
|
-
<div
|
|
619
|
-
id={`${HISTORY_ELEMENT_PREFIX}0`}
|
|
620
|
-
style={{
|
|
621
|
-
borderBottom: `1px solid ${solidBorderColor}`,
|
|
622
|
-
fontSize: "12px",
|
|
623
|
-
fontFamily: "Menlo, monospace",
|
|
624
|
-
padding: "2px 12px 1px",
|
|
625
|
-
display: "flex",
|
|
626
|
-
alignItems: "center",
|
|
627
|
-
background:
|
|
628
|
-
historySnapshot.historyIndex === 0
|
|
629
|
-
? "rgb(59 130 246 / 15%)"
|
|
630
|
-
: undefined,
|
|
631
|
-
}}
|
|
632
|
-
>
|
|
633
|
-
<span
|
|
634
|
-
style={{
|
|
635
|
-
fontFamily: "Menlo, monospace",
|
|
636
|
-
fontSize: "11px",
|
|
637
|
-
borderRadius: 4,
|
|
638
|
-
display: "inline-block",
|
|
639
|
-
}}
|
|
640
|
-
>
|
|
641
|
-
Initial state
|
|
642
|
-
</span>
|
|
643
|
-
</div>
|
|
644
|
-
{historySnapshot.history.map((entry, index) => (
|
|
645
|
-
<div
|
|
646
|
-
id={`${HISTORY_ELEMENT_PREFIX}${index + 1}`}
|
|
647
|
-
key={index}
|
|
648
|
-
style={{
|
|
649
|
-
padding: "0px 12px 1px",
|
|
650
|
-
borderBottom: `1px solid ${solidBorderColor}`,
|
|
651
|
-
background:
|
|
652
|
-
index + 1 === historySnapshot.historyIndex
|
|
653
|
-
? "rgb(59 130 246 / 15%)"
|
|
654
|
-
: undefined,
|
|
655
|
-
}}
|
|
656
|
-
>
|
|
657
|
-
{"name" in entry.metadata &&
|
|
658
|
-
typeof entry.metadata.name === "string" && (
|
|
659
|
-
<pre
|
|
660
|
-
style={{
|
|
661
|
-
fontSize: "11px",
|
|
662
|
-
display: "flex",
|
|
663
|
-
flexWrap: "wrap",
|
|
664
|
-
margin: 0,
|
|
665
|
-
}}
|
|
666
|
-
>
|
|
667
|
-
{entry.metadata.name}
|
|
668
|
-
</pre>
|
|
669
|
-
)}
|
|
670
|
-
{entry.redoPatches?.map((patch, j) => (
|
|
671
|
-
<pre
|
|
672
|
-
key={j}
|
|
673
|
-
style={{
|
|
674
|
-
fontSize: "11px",
|
|
675
|
-
display: "flex",
|
|
676
|
-
flexWrap: "wrap",
|
|
677
|
-
margin: 0,
|
|
678
|
-
}}
|
|
679
|
-
>
|
|
680
|
-
{patch.op === "add" && (
|
|
681
|
-
<>
|
|
682
|
-
<span style={{ fontStyle: "italic" }}>
|
|
683
|
-
{pathToString(patch.path)}
|
|
684
|
-
</span>
|
|
685
|
-
{" = "}
|
|
686
|
-
<ObjectInspector data={patch.value} theme={theme} />
|
|
687
|
-
</>
|
|
688
|
-
)}
|
|
689
|
-
{patch.op === "replace" && (
|
|
690
|
-
<>
|
|
691
|
-
<span style={{ fontStyle: "italic" }}>
|
|
692
|
-
{pathToString(patch.path)}
|
|
693
|
-
</span>
|
|
694
|
-
{" = "}
|
|
695
|
-
<ObjectInspector data={patch.value} theme={theme} />
|
|
696
|
-
</>
|
|
697
|
-
)}
|
|
698
|
-
{patch.op === "remove" && (
|
|
699
|
-
<>
|
|
700
|
-
<span style={{ color: theme.OBJECT_VALUE_STRING_COLOR }}>
|
|
701
|
-
delete{" "}
|
|
702
|
-
</span>
|
|
703
|
-
<span style={{ fontStyle: "italic" }}>
|
|
704
|
-
{pathToString(patch.path)}
|
|
705
|
-
</span>
|
|
706
|
-
</>
|
|
707
|
-
)}
|
|
708
|
-
{patch.op === "move" && (
|
|
709
|
-
<>
|
|
710
|
-
<span style={{ fontStyle: "italic" }}>
|
|
711
|
-
{pathToString(patch.from!)}
|
|
712
|
-
</span>
|
|
713
|
-
{" -> "}
|
|
714
|
-
<span style={{ fontStyle: "italic" }}>
|
|
715
|
-
{pathToString(patch.path)}
|
|
716
|
-
</span>
|
|
717
|
-
</>
|
|
718
|
-
)}
|
|
719
|
-
</pre>
|
|
720
|
-
))}
|
|
721
|
-
</div>
|
|
722
|
-
))}
|
|
723
|
-
</div>
|
|
724
|
-
</DisclosureSection>
|
|
725
|
-
<DisclosureSection
|
|
351
|
+
historySnapshot={historySnapshot}
|
|
352
|
+
multiplayerStateManager={multiplayerStateManager}
|
|
353
|
+
/>
|
|
354
|
+
<StateInspectorDisclosureSection
|
|
726
355
|
open={showAssets}
|
|
727
356
|
setOpen={setShowAssets}
|
|
728
357
|
title={
|
|
729
358
|
<TitleLabel>
|
|
730
|
-
Assets
|
|
359
|
+
Assets ({assets.length})
|
|
731
360
|
<ColoredDot type={assetsInitialized ? "success" : "error"} />
|
|
732
361
|
</TitleLabel>
|
|
733
362
|
}
|
|
734
363
|
colorScheme={colorScheme}
|
|
735
364
|
right={
|
|
736
365
|
<span style={{ display: "flex", gap: "10px" }}>
|
|
737
|
-
<
|
|
366
|
+
<StateInspectorButton
|
|
738
367
|
theme={theme}
|
|
739
368
|
onClick={() => {
|
|
740
369
|
console.info(
|
|
@@ -745,8 +374,8 @@ export const StateInspector = memoGeneric(function StateInspector<
|
|
|
745
374
|
}}
|
|
746
375
|
>
|
|
747
376
|
Reload
|
|
748
|
-
</
|
|
749
|
-
<
|
|
377
|
+
</StateInspectorButton>
|
|
378
|
+
<StateInspectorButton
|
|
750
379
|
theme={theme}
|
|
751
380
|
onClick={() => {
|
|
752
381
|
// get file input
|
|
@@ -767,25 +396,25 @@ export const StateInspector = memoGeneric(function StateInspector<
|
|
|
767
396
|
}}
|
|
768
397
|
>
|
|
769
398
|
Upload
|
|
770
|
-
</
|
|
399
|
+
</StateInspectorButton>
|
|
771
400
|
</span>
|
|
772
401
|
}
|
|
773
402
|
>
|
|
774
|
-
<
|
|
403
|
+
<StateInspectorDisclosureRowInner>
|
|
775
404
|
{assets.map((asset) => (
|
|
776
|
-
<
|
|
405
|
+
<StateInspectorRow key={asset.id} colorScheme={colorScheme}>
|
|
777
406
|
<ObjectInspector name={asset.id} data={asset} theme={theme} />
|
|
778
|
-
<
|
|
407
|
+
<StateInspectorButton
|
|
779
408
|
theme={theme}
|
|
780
409
|
onClick={() => assetManager.delete(asset.id)}
|
|
781
410
|
>
|
|
782
411
|
Delete
|
|
783
|
-
</
|
|
784
|
-
</
|
|
412
|
+
</StateInspectorButton>
|
|
413
|
+
</StateInspectorRow>
|
|
785
414
|
))}
|
|
786
|
-
</
|
|
787
|
-
</
|
|
788
|
-
<
|
|
415
|
+
</StateInspectorDisclosureRowInner>
|
|
416
|
+
</StateInspectorDisclosureSection>
|
|
417
|
+
<StateInspectorDisclosureSection
|
|
789
418
|
title={
|
|
790
419
|
<TitleLabel>
|
|
791
420
|
Secrets
|
|
@@ -796,7 +425,7 @@ export const StateInspector = memoGeneric(function StateInspector<
|
|
|
796
425
|
open={showSecrets}
|
|
797
426
|
setOpen={setShowSecrets}
|
|
798
427
|
right={
|
|
799
|
-
<
|
|
428
|
+
<StateInspectorButton
|
|
800
429
|
theme={theme}
|
|
801
430
|
onClick={() => {
|
|
802
431
|
const name = prompt("Enter secret name");
|
|
@@ -807,24 +436,24 @@ export const StateInspector = memoGeneric(function StateInspector<
|
|
|
807
436
|
}}
|
|
808
437
|
>
|
|
809
438
|
Create
|
|
810
|
-
</
|
|
439
|
+
</StateInspectorButton>
|
|
811
440
|
}
|
|
812
441
|
>
|
|
813
|
-
<
|
|
442
|
+
<StateInspectorDisclosureRowInner>
|
|
814
443
|
{secrets.map((secret) => (
|
|
815
|
-
<
|
|
444
|
+
<StateInspectorRow key={secret.id} colorScheme={colorScheme}>
|
|
816
445
|
<ObjectInspector data={secret} theme={theme} />
|
|
817
|
-
<
|
|
446
|
+
<StateInspectorButton
|
|
818
447
|
theme={theme}
|
|
819
448
|
onClick={() => secretManager.deleteSecret(secret.id)}
|
|
820
449
|
>
|
|
821
450
|
Delete
|
|
822
|
-
</
|
|
823
|
-
</
|
|
451
|
+
</StateInspectorButton>
|
|
452
|
+
</StateInspectorRow>
|
|
824
453
|
))}
|
|
825
|
-
</
|
|
826
|
-
</
|
|
827
|
-
<
|
|
454
|
+
</StateInspectorDisclosureRowInner>
|
|
455
|
+
</StateInspectorDisclosureSection>{" "}
|
|
456
|
+
<StateInspectorDisclosureSection
|
|
828
457
|
open={showInputs}
|
|
829
458
|
setOpen={setShowInputs}
|
|
830
459
|
title={
|
|
@@ -835,11 +464,11 @@ export const StateInspector = memoGeneric(function StateInspector<
|
|
|
835
464
|
}
|
|
836
465
|
colorScheme={colorScheme}
|
|
837
466
|
>
|
|
838
|
-
<
|
|
467
|
+
<StateInspectorDisclosureRowInner>
|
|
839
468
|
{inputs?.map((input: Input) => (
|
|
840
|
-
<
|
|
469
|
+
<StateInspectorRow key={input.id} colorScheme={colorScheme}>
|
|
841
470
|
<ObjectInspector data={input} theme={theme} />
|
|
842
|
-
</
|
|
471
|
+
</StateInspectorRow>
|
|
843
472
|
))}
|
|
844
473
|
{!inputs?.length && (
|
|
845
474
|
<div
|
|
@@ -854,9 +483,9 @@ export const StateInspector = memoGeneric(function StateInspector<
|
|
|
854
483
|
<span>No inputs</span>
|
|
855
484
|
</div>
|
|
856
485
|
)}
|
|
857
|
-
</
|
|
858
|
-
</
|
|
859
|
-
<
|
|
486
|
+
</StateInspectorDisclosureRowInner>
|
|
487
|
+
</StateInspectorDisclosureSection>
|
|
488
|
+
<StateInspectorDisclosureSection
|
|
860
489
|
open={showOutputTransforms}
|
|
861
490
|
setOpen={setShowOutputTransforms}
|
|
862
491
|
title={
|
|
@@ -869,11 +498,11 @@ export const StateInspector = memoGeneric(function StateInspector<
|
|
|
869
498
|
}
|
|
870
499
|
colorScheme={colorScheme}
|
|
871
500
|
>
|
|
872
|
-
<
|
|
501
|
+
<StateInspectorDisclosureRowInner>
|
|
873
502
|
{outputTransforms?.map((transform: OutputTransform) => (
|
|
874
|
-
<
|
|
503
|
+
<StateInspectorRow key={transform.id} colorScheme={colorScheme}>
|
|
875
504
|
<ObjectInspector data={transform} theme={theme} />
|
|
876
|
-
</
|
|
505
|
+
</StateInspectorRow>
|
|
877
506
|
))}
|
|
878
507
|
{!outputTransforms?.length && (
|
|
879
508
|
<div
|
|
@@ -888,17 +517,17 @@ export const StateInspector = memoGeneric(function StateInspector<
|
|
|
888
517
|
<span>No output transforms</span>
|
|
889
518
|
</div>
|
|
890
519
|
)}
|
|
891
|
-
</
|
|
892
|
-
</
|
|
893
|
-
<
|
|
520
|
+
</StateInspectorDisclosureRowInner>
|
|
521
|
+
</StateInspectorDisclosureSection>
|
|
522
|
+
<StateInspectorDisclosureSection
|
|
894
523
|
title="Tasks"
|
|
895
524
|
colorScheme={colorScheme}
|
|
896
525
|
open={showTasks}
|
|
897
526
|
setOpen={setShowTasks}
|
|
898
527
|
>
|
|
899
|
-
<
|
|
528
|
+
<StateInspectorDisclosureRowInner>
|
|
900
529
|
{tasks?.map((task) => (
|
|
901
|
-
<
|
|
530
|
+
<StateInspectorRow
|
|
902
531
|
key={task.id}
|
|
903
532
|
colorScheme={colorScheme}
|
|
904
533
|
style={{
|
|
@@ -915,265 +544,40 @@ export const StateInspector = memoGeneric(function StateInspector<
|
|
|
915
544
|
data={task.payload}
|
|
916
545
|
theme={theme}
|
|
917
546
|
/>
|
|
918
|
-
</
|
|
547
|
+
</StateInspectorRow>
|
|
919
548
|
))}
|
|
920
|
-
</
|
|
921
|
-
</
|
|
922
|
-
<
|
|
549
|
+
</StateInspectorDisclosureRowInner>
|
|
550
|
+
</StateInspectorDisclosureSection>
|
|
551
|
+
<StateInspectorDisclosureSection
|
|
923
552
|
open={showEphemeral}
|
|
924
553
|
setOpen={setShowEphemeral}
|
|
925
554
|
title="Ephemeral User Data"
|
|
926
555
|
colorScheme={colorScheme}
|
|
927
556
|
>
|
|
928
|
-
<
|
|
557
|
+
<StateInspectorDisclosureRowInner>
|
|
929
558
|
{Object.entries(ephemeral).map(([key, value]) => (
|
|
930
|
-
<
|
|
559
|
+
<StateInspectorRow key={key} colorScheme={colorScheme}>
|
|
931
560
|
<ObjectInspector
|
|
932
561
|
name={key}
|
|
933
562
|
data={value}
|
|
934
563
|
theme={theme}
|
|
935
564
|
expandLevel={10}
|
|
936
565
|
/>
|
|
937
|
-
</
|
|
566
|
+
</StateInspectorRow>
|
|
938
567
|
))}
|
|
939
|
-
</
|
|
940
|
-
</
|
|
941
|
-
<
|
|
942
|
-
|
|
943
|
-
setOpen={setShowEvents}
|
|
944
|
-
title="Events"
|
|
568
|
+
</StateInspectorDisclosureRowInner>
|
|
569
|
+
</StateInspectorDisclosureSection>
|
|
570
|
+
<EventsSection<S>
|
|
571
|
+
connectionEvents={connectionEvents}
|
|
945
572
|
colorScheme={colorScheme}
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
<InspectorRow key={index} colorScheme={colorScheme}>
|
|
951
|
-
connection:{" "}
|
|
952
|
-
<span style={{ fontStyle: "italic" }}>
|
|
953
|
-
{event.state.toLowerCase()}
|
|
954
|
-
</span>
|
|
955
|
-
</InspectorRow>
|
|
956
|
-
) : (
|
|
957
|
-
<InspectorRow
|
|
958
|
-
key={index}
|
|
959
|
-
colorScheme={colorScheme}
|
|
960
|
-
variant={event.type === "send" ? "up" : "down"}
|
|
961
|
-
style={{
|
|
962
|
-
padding: "0px 12px 1px",
|
|
963
|
-
}}
|
|
964
|
-
>
|
|
965
|
-
<ObjectInspector
|
|
966
|
-
data={event.type === "error" ? event.error : event.message}
|
|
967
|
-
theme={theme}
|
|
968
|
-
nodeRenderer={({
|
|
969
|
-
depth,
|
|
970
|
-
name,
|
|
971
|
-
data,
|
|
972
|
-
isNonenumerable,
|
|
973
|
-
}: any) => {
|
|
974
|
-
const direction = event.type === "send" ? "up" : "down";
|
|
975
|
-
|
|
976
|
-
return depth === 0 ? (
|
|
977
|
-
<ObjectRootLabel direction={direction} data={data} />
|
|
978
|
-
) : (
|
|
979
|
-
<ObjectLabel
|
|
980
|
-
direction={direction}
|
|
981
|
-
name={name}
|
|
982
|
-
data={data}
|
|
983
|
-
isNonenumerable={isNonenumerable}
|
|
984
|
-
/>
|
|
985
|
-
);
|
|
986
|
-
}}
|
|
987
|
-
/>
|
|
988
|
-
</InspectorRow>
|
|
989
|
-
)
|
|
990
|
-
)}
|
|
991
|
-
{!connectionEvents && (
|
|
992
|
-
<div
|
|
993
|
-
style={{
|
|
994
|
-
padding: "12px",
|
|
995
|
-
fontSize: "12px",
|
|
996
|
-
display: "flex",
|
|
997
|
-
flexDirection: "column",
|
|
998
|
-
gap: "4px",
|
|
999
|
-
}}
|
|
1000
|
-
>
|
|
1001
|
-
<span>No recorded events</span>
|
|
1002
|
-
</div>
|
|
1003
|
-
)}
|
|
1004
|
-
</div>
|
|
1005
|
-
</DisclosureSection>
|
|
573
|
+
eventsContainerRef={eventsContainerRef}
|
|
574
|
+
showEvents={showEvents}
|
|
575
|
+
setShowEvents={setShowEvents}
|
|
576
|
+
/>
|
|
1006
577
|
</div>
|
|
1007
578
|
);
|
|
1008
579
|
});
|
|
1009
580
|
|
|
1010
|
-
type MessageDirection = "up" | "down";
|
|
1011
|
-
|
|
1012
|
-
const ObjectRootLabel: FC<{
|
|
1013
|
-
name?: string;
|
|
1014
|
-
data: any;
|
|
1015
|
-
direction?: MessageDirection;
|
|
1016
|
-
}> = ({ name, data, direction }) => {
|
|
1017
|
-
if (typeof name === "string") {
|
|
1018
|
-
return (
|
|
1019
|
-
<span>
|
|
1020
|
-
<ObjectName name={name} />
|
|
1021
|
-
<span>: </span>
|
|
1022
|
-
<ObjectPreview data={data} />
|
|
1023
|
-
</span>
|
|
1024
|
-
);
|
|
1025
|
-
}
|
|
1026
|
-
if (direction === "up" || direction === "down") {
|
|
1027
|
-
const arrow = direction === "up" ? "↑" : "↓";
|
|
1028
|
-
|
|
1029
|
-
return (
|
|
1030
|
-
<span>
|
|
1031
|
-
<span
|
|
1032
|
-
style={{
|
|
1033
|
-
background:
|
|
1034
|
-
direction === "up" ? "rgba(0,255,0,0.2)" : "rgba(255,0,0,0.2)",
|
|
1035
|
-
// color: "white",
|
|
1036
|
-
width: 12,
|
|
1037
|
-
height: 12,
|
|
1038
|
-
borderRadius: 2,
|
|
1039
|
-
display: "inline-flex",
|
|
1040
|
-
justifyContent: "center",
|
|
1041
|
-
alignItems: "center",
|
|
1042
|
-
marginRight: 4,
|
|
1043
|
-
lineHeight: "12px",
|
|
1044
|
-
}}
|
|
1045
|
-
>
|
|
1046
|
-
{arrow}
|
|
1047
|
-
</span>
|
|
1048
|
-
<ObjectPreview data={data} />
|
|
1049
|
-
</span>
|
|
1050
|
-
);
|
|
1051
|
-
} else {
|
|
1052
|
-
return <ObjectPreview data={data} />;
|
|
1053
|
-
}
|
|
1054
|
-
};
|
|
1055
|
-
|
|
1056
|
-
// Rotates ▶️ or ▼ based on expanded state
|
|
1057
|
-
function Arrow({
|
|
1058
|
-
expanded,
|
|
1059
|
-
onClick,
|
|
1060
|
-
style,
|
|
1061
|
-
}: {
|
|
1062
|
-
expanded: boolean;
|
|
1063
|
-
onClick?: () => void;
|
|
1064
|
-
style?: CSSProperties;
|
|
1065
|
-
}) {
|
|
1066
|
-
return (
|
|
1067
|
-
<span
|
|
1068
|
-
role="button"
|
|
1069
|
-
onClick={onClick}
|
|
1070
|
-
style={{
|
|
1071
|
-
display: "inline-block",
|
|
1072
|
-
textAlign: "center",
|
|
1073
|
-
transform: expanded ? "rotate(0deg)" : "rotate(-90deg)",
|
|
1074
|
-
fontFamily: "Menlo, monospace",
|
|
1075
|
-
...style,
|
|
1076
|
-
}}
|
|
1077
|
-
>
|
|
1078
|
-
{"▼"}
|
|
1079
|
-
</span>
|
|
1080
|
-
);
|
|
1081
|
-
}
|
|
1082
|
-
|
|
1083
|
-
function pathToString(extendedPath: ExtendedPathKey[]) {
|
|
1084
|
-
return extendedPath
|
|
1085
|
-
.map((key) =>
|
|
1086
|
-
typeof key === "object"
|
|
1087
|
-
? `:${ellipsis(key.id.toString(), 8, "middle")}`
|
|
1088
|
-
: key
|
|
1089
|
-
)
|
|
1090
|
-
.map((key, index) =>
|
|
1091
|
-
index === 0 || (typeof key === "string" && key.startsWith(":"))
|
|
1092
|
-
? key
|
|
1093
|
-
: `.${key}`
|
|
1094
|
-
)
|
|
1095
|
-
.join("");
|
|
1096
|
-
}
|
|
1097
|
-
|
|
1098
|
-
type EllipsisPosition = "start" | "middle" | "end";
|
|
1099
|
-
|
|
1100
|
-
function ellipsis(
|
|
1101
|
-
str: string,
|
|
1102
|
-
maxLength: number,
|
|
1103
|
-
position: EllipsisPosition = "end"
|
|
1104
|
-
) {
|
|
1105
|
-
if (str.length <= maxLength) return str;
|
|
1106
|
-
|
|
1107
|
-
switch (position) {
|
|
1108
|
-
case "start":
|
|
1109
|
-
return `…${str.slice(str.length - maxLength)}`;
|
|
1110
|
-
case "middle":
|
|
1111
|
-
const halfLength = Math.floor(maxLength / 2);
|
|
1112
|
-
return `${str.slice(0, halfLength)}…${str.slice(str.length - halfLength)}`;
|
|
1113
|
-
case "end":
|
|
1114
|
-
return `${str.slice(0, maxLength)}…`;
|
|
1115
|
-
}
|
|
1116
|
-
}
|
|
1117
|
-
|
|
1118
|
-
function SmallButton({
|
|
1119
|
-
children,
|
|
1120
|
-
onClick,
|
|
1121
|
-
style,
|
|
1122
|
-
theme,
|
|
1123
|
-
disabled,
|
|
1124
|
-
}: {
|
|
1125
|
-
children: React.ReactNode;
|
|
1126
|
-
onClick: () => void;
|
|
1127
|
-
style?: CSSProperties;
|
|
1128
|
-
theme: typeof chromeLight;
|
|
1129
|
-
disabled?: boolean;
|
|
1130
|
-
}) {
|
|
1131
|
-
return (
|
|
1132
|
-
<button
|
|
1133
|
-
type="button"
|
|
1134
|
-
disabled={disabled}
|
|
1135
|
-
onClick={(event) => {
|
|
1136
|
-
event.stopPropagation();
|
|
1137
|
-
onClick();
|
|
1138
|
-
}}
|
|
1139
|
-
style={{
|
|
1140
|
-
flex: "0",
|
|
1141
|
-
appearance: "none",
|
|
1142
|
-
background: "none",
|
|
1143
|
-
color: theme.BASE_COLOR,
|
|
1144
|
-
opacity: disabled ? 0.25 : 1,
|
|
1145
|
-
border: "none",
|
|
1146
|
-
fontSize: "12px",
|
|
1147
|
-
whiteSpace: "nowrap",
|
|
1148
|
-
display: "inline-flex",
|
|
1149
|
-
alignItems: "center",
|
|
1150
|
-
justifyContent: "center",
|
|
1151
|
-
padding: "0",
|
|
1152
|
-
cursor: "pointer",
|
|
1153
|
-
...style,
|
|
1154
|
-
}}
|
|
1155
|
-
>
|
|
1156
|
-
{children}
|
|
1157
|
-
</button>
|
|
1158
|
-
);
|
|
1159
|
-
}
|
|
1160
|
-
|
|
1161
|
-
function ColoredDot({ type }: { type: "success" | "error" }) {
|
|
1162
|
-
return (
|
|
1163
|
-
<span
|
|
1164
|
-
style={{
|
|
1165
|
-
display: "inline-block",
|
|
1166
|
-
width: 6,
|
|
1167
|
-
height: 6,
|
|
1168
|
-
background:
|
|
1169
|
-
type === "success" ? "rgba(0,200,0,0.8)" : "rgba(200,0,0,0.8)",
|
|
1170
|
-
borderRadius: "50%",
|
|
1171
|
-
verticalAlign: "middle",
|
|
1172
|
-
}}
|
|
1173
|
-
/>
|
|
1174
|
-
);
|
|
1175
|
-
}
|
|
1176
|
-
|
|
1177
581
|
function TitleLabel({ children }: { children: React.ReactNode }) {
|
|
1178
582
|
return (
|
|
1179
583
|
<span style={{ display: "flex", alignItems: "center", gap: "4px" }}>
|