@noya-app/noya-multiplayer-react 0.1.76 → 0.1.78
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/.turbo/turbo-build.log +11 -11
- package/CHANGELOG.md +17 -0
- package/dist/index.bundle.js +28 -28
- package/dist/index.d.mts +12 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.js +672 -210
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +649 -187
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -3
- package/src/inspector/StateInspector.tsx +77 -37
- package/src/inspector/sections/GitSection.tsx +500 -0
- package/src/noyaApp.ts +9 -0
- package/src/singleton.tsx +2 -0
|
@@ -0,0 +1,500 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { GitFileEditHandle, GitManager } from "@noya-app/state-manager";
|
|
4
|
+
import React, { useEffect, useRef, useState } from "react";
|
|
5
|
+
import { useObservable } from "../../useObservable";
|
|
6
|
+
import { ColoredDot } from "../ColoredDot";
|
|
7
|
+
import { getStateInspectorTheme } from "../inspectorTheme";
|
|
8
|
+
import { StateInspectorButton } from "../StateInspectorButton";
|
|
9
|
+
import {
|
|
10
|
+
StateInspectorDisclosureRowInner,
|
|
11
|
+
StateInspectorDisclosureSection,
|
|
12
|
+
} from "../StateInspectorDisclosureSection";
|
|
13
|
+
import { StateInspectorTitleLabel } from "../StateInspectorTitleLabel";
|
|
14
|
+
|
|
15
|
+
export function GitSection({
|
|
16
|
+
showGit,
|
|
17
|
+
setShowGit,
|
|
18
|
+
colorScheme,
|
|
19
|
+
gitManager,
|
|
20
|
+
}: {
|
|
21
|
+
showGit: boolean;
|
|
22
|
+
setShowGit: (value: boolean) => void;
|
|
23
|
+
colorScheme: "light" | "dark";
|
|
24
|
+
gitManager: GitManager;
|
|
25
|
+
}) {
|
|
26
|
+
const theme = getStateInspectorTheme(colorScheme);
|
|
27
|
+
const repos = useObservable(gitManager.repos$);
|
|
28
|
+
const isInitialized = useObservable(gitManager.isInitialized$);
|
|
29
|
+
const repoFiles = useObservable(gitManager.repoFiles$);
|
|
30
|
+
|
|
31
|
+
// Local state for focused repo (debugging UI only)
|
|
32
|
+
const [focusedRepoId, setFocusedRepoId] = useState<string | null>(null);
|
|
33
|
+
|
|
34
|
+
// State for editing a file
|
|
35
|
+
const [editingFile, setEditingFile] = useState<string | null>(null);
|
|
36
|
+
const editHandleRef = useRef<GitFileEditHandle | null>(null);
|
|
37
|
+
const [editContent, setEditContent] = useState<string>("");
|
|
38
|
+
const [isEditInitialized, setIsEditInitialized] = useState(false);
|
|
39
|
+
|
|
40
|
+
const focusedRepo = focusedRepoId
|
|
41
|
+
? repos.find((r) => r.id === focusedRepoId)
|
|
42
|
+
: null;
|
|
43
|
+
const focusedRepoFilesState = focusedRepoId ? repoFiles[focusedRepoId] : null;
|
|
44
|
+
|
|
45
|
+
const handleFocusRepo = async (repoId: string | null) => {
|
|
46
|
+
// Close any open edit session when switching repos
|
|
47
|
+
if (editHandleRef.current) {
|
|
48
|
+
editHandleRef.current.close();
|
|
49
|
+
editHandleRef.current = null;
|
|
50
|
+
setEditingFile(null);
|
|
51
|
+
setEditContent("");
|
|
52
|
+
setIsEditInitialized(false);
|
|
53
|
+
}
|
|
54
|
+
setFocusedRepoId(repoId);
|
|
55
|
+
if (repoId && !repoFiles[repoId]) {
|
|
56
|
+
await gitManager.fetchRepoFiles(repoId);
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const handleEditFile = (filePath: string) => {
|
|
61
|
+
if (!focusedRepoId || !focusedRepoFilesState) return;
|
|
62
|
+
|
|
63
|
+
// Close existing session
|
|
64
|
+
if (editHandleRef.current) {
|
|
65
|
+
editHandleRef.current.close();
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const handle = gitManager.openEditingHandle(
|
|
69
|
+
focusedRepoId,
|
|
70
|
+
filePath,
|
|
71
|
+
focusedRepoFilesState.ref
|
|
72
|
+
);
|
|
73
|
+
editHandleRef.current = handle;
|
|
74
|
+
setEditingFile(filePath);
|
|
75
|
+
setIsEditInitialized(false);
|
|
76
|
+
setEditContent("");
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
const handleCloseEdit = () => {
|
|
80
|
+
if (editHandleRef.current) {
|
|
81
|
+
editHandleRef.current.close();
|
|
82
|
+
editHandleRef.current = null;
|
|
83
|
+
}
|
|
84
|
+
setEditingFile(null);
|
|
85
|
+
setEditContent("");
|
|
86
|
+
setIsEditInitialized(false);
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
// Subscribe to content updates from the editing handle
|
|
90
|
+
useEffect(() => {
|
|
91
|
+
const handle = editHandleRef.current;
|
|
92
|
+
if (!handle) return;
|
|
93
|
+
|
|
94
|
+
const unsubContent = handle.content$.subscribe((content) => {
|
|
95
|
+
setEditContent(content);
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
const unsubInitialized = handle.isInitialized$.subscribe((initialized) => {
|
|
99
|
+
setIsEditInitialized(initialized);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
return () => {
|
|
103
|
+
unsubContent();
|
|
104
|
+
unsubInitialized();
|
|
105
|
+
};
|
|
106
|
+
}, [editingFile]);
|
|
107
|
+
|
|
108
|
+
const handleContentChange = (newContent: string) => {
|
|
109
|
+
const handle = editHandleRef.current;
|
|
110
|
+
if (!handle) return;
|
|
111
|
+
|
|
112
|
+
handle.setState(newContent);
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
const handleBranchChange = async (ref: string) => {
|
|
116
|
+
if (focusedRepoId) {
|
|
117
|
+
await gitManager.fetchRepoFiles(focusedRepoId, ref);
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
const handleAddFile = async () => {
|
|
122
|
+
if (!focusedRepoId || !focusedRepoFilesState) return;
|
|
123
|
+
|
|
124
|
+
const path = prompt("Enter file path (e.g., src/index.ts):");
|
|
125
|
+
if (!path) return;
|
|
126
|
+
|
|
127
|
+
try {
|
|
128
|
+
await gitManager.patchFiles(
|
|
129
|
+
focusedRepoId,
|
|
130
|
+
{ create: [{ path, content: "" }] },
|
|
131
|
+
focusedRepoFilesState.ref
|
|
132
|
+
);
|
|
133
|
+
} catch (error) {
|
|
134
|
+
alert(`Failed to create file: ${error}`);
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
const handleRenameFile = async (oldPath: string) => {
|
|
139
|
+
if (!focusedRepoId || !focusedRepoFilesState) return;
|
|
140
|
+
|
|
141
|
+
const newPath = prompt(`Rename "${oldPath}" to:`, oldPath);
|
|
142
|
+
if (!newPath || newPath === oldPath) return;
|
|
143
|
+
|
|
144
|
+
try {
|
|
145
|
+
await gitManager.patchFiles(
|
|
146
|
+
focusedRepoId,
|
|
147
|
+
{ rename: [{ oldPath, newPath }] },
|
|
148
|
+
focusedRepoFilesState.ref
|
|
149
|
+
);
|
|
150
|
+
} catch (error) {
|
|
151
|
+
alert(`Failed to rename file: ${error}`);
|
|
152
|
+
}
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
const handleDeleteFile = async (path: string) => {
|
|
156
|
+
if (!focusedRepoId || !focusedRepoFilesState) return;
|
|
157
|
+
|
|
158
|
+
// eslint-disable-next-line no-restricted-globals
|
|
159
|
+
const confirmed = confirm(`Delete "${path}"?`);
|
|
160
|
+
if (!confirmed) return;
|
|
161
|
+
|
|
162
|
+
try {
|
|
163
|
+
await gitManager.patchFiles(
|
|
164
|
+
focusedRepoId,
|
|
165
|
+
{ delete: [{ path }] },
|
|
166
|
+
focusedRepoFilesState.ref
|
|
167
|
+
);
|
|
168
|
+
|
|
169
|
+
// If we were editing this file, close the editor
|
|
170
|
+
if (editingFile === path) {
|
|
171
|
+
handleCloseEdit();
|
|
172
|
+
}
|
|
173
|
+
} catch (error) {
|
|
174
|
+
alert(`Failed to delete file: ${error}`);
|
|
175
|
+
}
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
return (
|
|
179
|
+
<StateInspectorDisclosureSection
|
|
180
|
+
open={showGit}
|
|
181
|
+
setOpen={setShowGit}
|
|
182
|
+
title={
|
|
183
|
+
<StateInspectorTitleLabel>
|
|
184
|
+
Git Repos ({repos.length})
|
|
185
|
+
<ColoredDot type={isInitialized ? "success" : "error"} />
|
|
186
|
+
</StateInspectorTitleLabel>
|
|
187
|
+
}
|
|
188
|
+
colorScheme={colorScheme}
|
|
189
|
+
right={
|
|
190
|
+
<span style={{ display: "flex", gap: "10px" }}>
|
|
191
|
+
<StateInspectorButton
|
|
192
|
+
theme={theme}
|
|
193
|
+
onClick={async () => {
|
|
194
|
+
try {
|
|
195
|
+
await gitManager.initRepo();
|
|
196
|
+
} catch (error) {
|
|
197
|
+
console.error("[GitSection] Failed to create repo:", error);
|
|
198
|
+
}
|
|
199
|
+
}}
|
|
200
|
+
>
|
|
201
|
+
Init Repo
|
|
202
|
+
</StateInspectorButton>
|
|
203
|
+
</span>
|
|
204
|
+
}
|
|
205
|
+
>
|
|
206
|
+
<StateInspectorDisclosureRowInner>
|
|
207
|
+
{/* Repo selector */}
|
|
208
|
+
<div style={{ padding: "4px 12px", display: "flex", gap: "4px" }}>
|
|
209
|
+
<select
|
|
210
|
+
name="repoSelector"
|
|
211
|
+
value={focusedRepoId ?? ""}
|
|
212
|
+
style={{ flex: "1 1 auto", height: "19px", minWidth: 0 }}
|
|
213
|
+
onChange={(e) => {
|
|
214
|
+
const value = e.target.value;
|
|
215
|
+
handleFocusRepo(value || null);
|
|
216
|
+
}}
|
|
217
|
+
>
|
|
218
|
+
<option value="">Select a repo...</option>
|
|
219
|
+
{repos.map((repo) => (
|
|
220
|
+
<option key={repo.id} value={repo.id}>
|
|
221
|
+
{repo.id}
|
|
222
|
+
</option>
|
|
223
|
+
))}
|
|
224
|
+
</select>
|
|
225
|
+
{focusedRepoId && (
|
|
226
|
+
<StateInspectorButton
|
|
227
|
+
theme={theme}
|
|
228
|
+
onClick={() => gitManager.fetchRepoFiles(focusedRepoId)}
|
|
229
|
+
>
|
|
230
|
+
Refresh
|
|
231
|
+
</StateInspectorButton>
|
|
232
|
+
)}
|
|
233
|
+
<label
|
|
234
|
+
style={{
|
|
235
|
+
display: "flex",
|
|
236
|
+
alignItems: "center",
|
|
237
|
+
gap: "4px",
|
|
238
|
+
fontSize: "11px",
|
|
239
|
+
whiteSpace: "nowrap",
|
|
240
|
+
}}
|
|
241
|
+
>
|
|
242
|
+
[Include Content = {gitManager.includeContent ? "true" : "false"}]
|
|
243
|
+
</label>
|
|
244
|
+
</div>
|
|
245
|
+
|
|
246
|
+
{/* Branch selector and files list for focused repo */}
|
|
247
|
+
{focusedRepo && focusedRepoFilesState && (
|
|
248
|
+
<>
|
|
249
|
+
{/* Branch selector */}
|
|
250
|
+
<div
|
|
251
|
+
style={{
|
|
252
|
+
padding: "4px 12px",
|
|
253
|
+
display: "flex",
|
|
254
|
+
gap: "8px",
|
|
255
|
+
alignItems: "center",
|
|
256
|
+
fontSize: "12px",
|
|
257
|
+
}}
|
|
258
|
+
>
|
|
259
|
+
<span style={{ color: theme.BASE_COLOR }}>Branch:</span>
|
|
260
|
+
{focusedRepoFilesState.branches.length > 0 ? (
|
|
261
|
+
<select
|
|
262
|
+
value={focusedRepoFilesState.ref}
|
|
263
|
+
style={{ flex: "1 1 auto", height: "19px" }}
|
|
264
|
+
onChange={(e) => handleBranchChange(e.target.value)}
|
|
265
|
+
>
|
|
266
|
+
{focusedRepoFilesState.branches.map((branch) => (
|
|
267
|
+
<option key={branch.name} value={branch.name}>
|
|
268
|
+
{branch.name}
|
|
269
|
+
</option>
|
|
270
|
+
))}
|
|
271
|
+
</select>
|
|
272
|
+
) : (
|
|
273
|
+
<span style={{ opacity: 0.6, fontStyle: "italic" }}>
|
|
274
|
+
{focusedRepoFilesState.ref || "No branches"}
|
|
275
|
+
</span>
|
|
276
|
+
)}
|
|
277
|
+
{focusedRepoFilesState.oid && (
|
|
278
|
+
<span
|
|
279
|
+
style={{
|
|
280
|
+
fontFamily: "monospace",
|
|
281
|
+
fontSize: "10px",
|
|
282
|
+
opacity: 0.6,
|
|
283
|
+
}}
|
|
284
|
+
title={focusedRepoFilesState.oid}
|
|
285
|
+
>
|
|
286
|
+
{focusedRepoFilesState.oid.slice(0, 7)}
|
|
287
|
+
{focusedRepoFilesState.fetchState === "refreshing" && "…"}
|
|
288
|
+
</span>
|
|
289
|
+
)}
|
|
290
|
+
<StateInspectorButton
|
|
291
|
+
theme={theme}
|
|
292
|
+
onClick={handleAddFile}
|
|
293
|
+
style={{ fontSize: "10px", padding: "2px 6px" }}
|
|
294
|
+
>
|
|
295
|
+
+ Add File
|
|
296
|
+
</StateInspectorButton>
|
|
297
|
+
</div>
|
|
298
|
+
|
|
299
|
+
{focusedRepoFilesState.fetchState === "loading" && (
|
|
300
|
+
<div
|
|
301
|
+
style={{
|
|
302
|
+
padding: "12px",
|
|
303
|
+
fontSize: "12px",
|
|
304
|
+
}}
|
|
305
|
+
>
|
|
306
|
+
Loading files...
|
|
307
|
+
</div>
|
|
308
|
+
)}
|
|
309
|
+
{focusedRepoFilesState.error && (
|
|
310
|
+
<div
|
|
311
|
+
style={{
|
|
312
|
+
padding: "12px",
|
|
313
|
+
fontSize: "12px",
|
|
314
|
+
color: "#f44",
|
|
315
|
+
}}
|
|
316
|
+
>
|
|
317
|
+
Error: {focusedRepoFilesState.error}
|
|
318
|
+
</div>
|
|
319
|
+
)}
|
|
320
|
+
{focusedRepoFilesState.fetchState !== "loading" &&
|
|
321
|
+
!focusedRepoFilesState.error &&
|
|
322
|
+
focusedRepoFilesState.files.length === 0 && (
|
|
323
|
+
<div
|
|
324
|
+
style={{
|
|
325
|
+
padding: "12px",
|
|
326
|
+
fontSize: "12px",
|
|
327
|
+
}}
|
|
328
|
+
>
|
|
329
|
+
No files (empty repo)
|
|
330
|
+
</div>
|
|
331
|
+
)}
|
|
332
|
+
{/* Hide file list when editing a file */}
|
|
333
|
+
{!editingFile &&
|
|
334
|
+
focusedRepoFilesState.files.map((file, index) => {
|
|
335
|
+
// Get content preview if available
|
|
336
|
+
const fileInfo = focusedRepoFilesState.fileContents?.find(
|
|
337
|
+
(f) => f.path === file
|
|
338
|
+
);
|
|
339
|
+
let preview = "";
|
|
340
|
+
if (fileInfo) {
|
|
341
|
+
try {
|
|
342
|
+
const decoded = atob(fileInfo.content);
|
|
343
|
+
// Take first 40 chars, replace newlines with spaces
|
|
344
|
+
preview = decoded
|
|
345
|
+
.slice(0, 40)
|
|
346
|
+
.replace(/\n/g, " ")
|
|
347
|
+
.replace(/\s+/g, " ");
|
|
348
|
+
if (decoded.length > 40) preview += "…";
|
|
349
|
+
} catch {
|
|
350
|
+
// ignore decode errors
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
return (
|
|
355
|
+
<div
|
|
356
|
+
key={index}
|
|
357
|
+
style={{
|
|
358
|
+
padding: "2px 12px",
|
|
359
|
+
display: "flex",
|
|
360
|
+
justifyContent: "space-between",
|
|
361
|
+
alignItems: "center",
|
|
362
|
+
gap: "8px",
|
|
363
|
+
borderBottom: `1px solid ${
|
|
364
|
+
colorScheme === "light"
|
|
365
|
+
? "rgb(223 223 223)"
|
|
366
|
+
: "rgb(29 29 29)"
|
|
367
|
+
}`,
|
|
368
|
+
}}
|
|
369
|
+
>
|
|
370
|
+
<span
|
|
371
|
+
style={{
|
|
372
|
+
fontFamily: "monospace",
|
|
373
|
+
fontSize: "11px",
|
|
374
|
+
overflow: "hidden",
|
|
375
|
+
textOverflow: "ellipsis",
|
|
376
|
+
whiteSpace: "nowrap",
|
|
377
|
+
minWidth: 0,
|
|
378
|
+
display: "flex",
|
|
379
|
+
gap: "8px",
|
|
380
|
+
}}
|
|
381
|
+
title={file}
|
|
382
|
+
>
|
|
383
|
+
<span>{file}</span>
|
|
384
|
+
{preview && (
|
|
385
|
+
<span style={{ opacity: 0.5 }}>{preview}</span>
|
|
386
|
+
)}
|
|
387
|
+
</span>
|
|
388
|
+
<span style={{ display: "flex", gap: "4px", flexShrink: 0 }}>
|
|
389
|
+
<StateInspectorButton
|
|
390
|
+
theme={theme}
|
|
391
|
+
onClick={() => handleEditFile(file)}
|
|
392
|
+
style={{ fontSize: "10px", padding: "2px 6px" }}
|
|
393
|
+
>
|
|
394
|
+
Edit
|
|
395
|
+
</StateInspectorButton>
|
|
396
|
+
<StateInspectorButton
|
|
397
|
+
theme={theme}
|
|
398
|
+
onClick={() => handleRenameFile(file)}
|
|
399
|
+
style={{ fontSize: "10px", padding: "2px 6px" }}
|
|
400
|
+
>
|
|
401
|
+
Rename
|
|
402
|
+
</StateInspectorButton>
|
|
403
|
+
<StateInspectorButton
|
|
404
|
+
theme={theme}
|
|
405
|
+
onClick={() => handleDeleteFile(file)}
|
|
406
|
+
style={{ fontSize: "10px", padding: "2px 6px" }}
|
|
407
|
+
>
|
|
408
|
+
Delete
|
|
409
|
+
</StateInspectorButton>
|
|
410
|
+
</span>
|
|
411
|
+
</div>
|
|
412
|
+
);
|
|
413
|
+
})}
|
|
414
|
+
|
|
415
|
+
{/* File editor textarea */}
|
|
416
|
+
{editingFile && (
|
|
417
|
+
<div
|
|
418
|
+
style={{
|
|
419
|
+
padding: "8px 12px",
|
|
420
|
+
borderTop: `1px solid ${theme.DIVIDER_COLOR}`,
|
|
421
|
+
flex: "1 1 auto",
|
|
422
|
+
display: "flex",
|
|
423
|
+
flexDirection: "column",
|
|
424
|
+
minHeight: 0,
|
|
425
|
+
}}
|
|
426
|
+
>
|
|
427
|
+
<div
|
|
428
|
+
style={{
|
|
429
|
+
display: "flex",
|
|
430
|
+
justifyContent: "space-between",
|
|
431
|
+
alignItems: "center",
|
|
432
|
+
marginBottom: "8px",
|
|
433
|
+
flexShrink: 0,
|
|
434
|
+
}}
|
|
435
|
+
>
|
|
436
|
+
<span
|
|
437
|
+
style={{
|
|
438
|
+
fontFamily: "monospace",
|
|
439
|
+
fontSize: "11px",
|
|
440
|
+
fontWeight: "bold",
|
|
441
|
+
}}
|
|
442
|
+
>
|
|
443
|
+
{editingFile}
|
|
444
|
+
{!isEditInitialized && (
|
|
445
|
+
<span style={{ opacity: 0.5, marginLeft: "8px" }}>
|
|
446
|
+
Loading...
|
|
447
|
+
</span>
|
|
448
|
+
)}
|
|
449
|
+
</span>
|
|
450
|
+
<StateInspectorButton
|
|
451
|
+
theme={theme}
|
|
452
|
+
onClick={handleCloseEdit}
|
|
453
|
+
style={{ fontSize: "10px", padding: "2px 6px" }}
|
|
454
|
+
>
|
|
455
|
+
Close
|
|
456
|
+
</StateInspectorButton>
|
|
457
|
+
</div>
|
|
458
|
+
<textarea
|
|
459
|
+
value={editContent}
|
|
460
|
+
onChange={(e) => handleContentChange(e.target.value)}
|
|
461
|
+
disabled={!isEditInitialized}
|
|
462
|
+
style={{
|
|
463
|
+
width: "100%",
|
|
464
|
+
flex: "1 1 auto",
|
|
465
|
+
minHeight: "100px",
|
|
466
|
+
fontFamily: "monospace",
|
|
467
|
+
fontSize: "11px",
|
|
468
|
+
padding: "8px",
|
|
469
|
+
border: `1px solid ${theme.DIVIDER_COLOR}`,
|
|
470
|
+
borderRadius: "4px",
|
|
471
|
+
backgroundColor: theme.ROW_BACKGROUND,
|
|
472
|
+
color: theme.BASE_COLOR,
|
|
473
|
+
resize: "none",
|
|
474
|
+
boxSizing: "border-box",
|
|
475
|
+
}}
|
|
476
|
+
/>
|
|
477
|
+
</div>
|
|
478
|
+
)}
|
|
479
|
+
|
|
480
|
+
</>
|
|
481
|
+
)}
|
|
482
|
+
|
|
483
|
+
{/* Empty state when no repos */}
|
|
484
|
+
{repos.length === 0 && (
|
|
485
|
+
<div
|
|
486
|
+
style={{
|
|
487
|
+
padding: "12px",
|
|
488
|
+
fontSize: "12px",
|
|
489
|
+
display: "flex",
|
|
490
|
+
flexDirection: "column",
|
|
491
|
+
gap: "4px",
|
|
492
|
+
}}
|
|
493
|
+
>
|
|
494
|
+
<span>No git repos</span>
|
|
495
|
+
</div>
|
|
496
|
+
)}
|
|
497
|
+
</StateInspectorDisclosureRowInner>
|
|
498
|
+
</StateInspectorDisclosureSection>
|
|
499
|
+
);
|
|
500
|
+
}
|
package/src/noyaApp.ts
CHANGED
|
@@ -149,6 +149,7 @@ export function getSyncAdapter<
|
|
|
149
149
|
debug?: boolean;
|
|
150
150
|
policyAuthContext?: PolicyAuthContext;
|
|
151
151
|
serverScripts?: LocalStorageSyncOptions["serverScripts"];
|
|
152
|
+
git?: LocalStorageSyncOptions["git"];
|
|
152
153
|
}): SyncAdapter<S, M, E, MenuT, I> {
|
|
153
154
|
const {
|
|
154
155
|
multiplayerUrl,
|
|
@@ -156,6 +157,7 @@ export function getSyncAdapter<
|
|
|
156
157
|
debug,
|
|
157
158
|
policyAuthContext,
|
|
158
159
|
serverScripts,
|
|
160
|
+
git,
|
|
159
161
|
} = params ?? {};
|
|
160
162
|
|
|
161
163
|
return isEmbeddedApp()
|
|
@@ -166,6 +168,7 @@ export function getSyncAdapter<
|
|
|
166
168
|
? localStorageSync<S, M, E, MenuT, I>({
|
|
167
169
|
key: offlineStorageKey,
|
|
168
170
|
...(serverScripts ? { serverScripts } : {}),
|
|
171
|
+
...(git ? { git } : {}),
|
|
169
172
|
})
|
|
170
173
|
: stubSync<S, M, E, MenuT, I>();
|
|
171
174
|
}
|
|
@@ -178,6 +181,8 @@ export type UseNoyaStateOptions<
|
|
|
178
181
|
I extends Record<string, any> = Record<string, unknown>,
|
|
179
182
|
> = UseMultiplayerStateOptions<S, M, E, MenuT, I> & {
|
|
180
183
|
offlineStorageKey?: string;
|
|
184
|
+
serverScripts?: LocalStorageSyncOptions["serverScripts"];
|
|
185
|
+
git?: LocalStorageSyncOptions["git"];
|
|
181
186
|
};
|
|
182
187
|
|
|
183
188
|
export type UseNoyaStateResult<
|
|
@@ -282,12 +287,16 @@ export function useNoyaStateInternal<
|
|
|
282
287
|
offlineStorageKey: options?.offlineStorageKey,
|
|
283
288
|
debug: options?.debug,
|
|
284
289
|
policyAuthContext: basePolicyAuthContext,
|
|
290
|
+
serverScripts: options?.serverScripts,
|
|
291
|
+
git: options?.git,
|
|
285
292
|
});
|
|
286
293
|
}, [
|
|
287
294
|
multiplayerUrl,
|
|
288
295
|
options?.offlineStorageKey,
|
|
289
296
|
options?.debug,
|
|
290
297
|
basePolicyAuthContext,
|
|
298
|
+
options?.serverScripts,
|
|
299
|
+
options?.git,
|
|
291
300
|
]);
|
|
292
301
|
|
|
293
302
|
const [noyaManager] = useState(
|
package/src/singleton.tsx
CHANGED
|
@@ -44,6 +44,7 @@ export type AnyNoyaSingletonOptions<
|
|
|
44
44
|
offlineStorageKey?: string;
|
|
45
45
|
multiplayerUrl?: string;
|
|
46
46
|
serverScripts?: LocalStorageSyncOptions["serverScripts"];
|
|
47
|
+
git?: LocalStorageSyncOptions["git"];
|
|
47
48
|
};
|
|
48
49
|
|
|
49
50
|
export function initializeNoyaSingleton<
|
|
@@ -106,6 +107,7 @@ export function initializeNoyaSingleton<
|
|
|
106
107
|
offlineStorageKey,
|
|
107
108
|
debug: managerOptions?.debug,
|
|
108
109
|
serverScripts: managerOptions?.serverScripts,
|
|
110
|
+
git: managerOptions?.git,
|
|
109
111
|
policyAuthContext,
|
|
110
112
|
});
|
|
111
113
|
|