@visual-json/react 0.1.1 → 0.2.0
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/README.md +0 -4
- package/dist/index.d.mts +11 -7
- package/dist/index.d.ts +11 -7
- package/dist/index.js +630 -667
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +626 -669
- package/dist/index.mjs.map +1 -1
- package/package.json +10 -10
- package/LICENSE +0 -201
package/dist/index.js
CHANGED
|
@@ -25,7 +25,6 @@ __export(index_exports, {
|
|
|
25
25
|
DiffView: () => DiffView,
|
|
26
26
|
FormView: () => FormView,
|
|
27
27
|
JsonEditor: () => JsonEditor,
|
|
28
|
-
PropertyEditor: () => PropertyEditor,
|
|
29
28
|
SearchBar: () => SearchBar,
|
|
30
29
|
StudioContext: () => StudioContext,
|
|
31
30
|
TreeView: () => TreeView,
|
|
@@ -40,7 +39,7 @@ var import_react10 = require("react");
|
|
|
40
39
|
|
|
41
40
|
// src/visual-json.tsx
|
|
42
41
|
var import_react2 = require("react");
|
|
43
|
-
var
|
|
42
|
+
var import_core2 = require("@visual-json/core");
|
|
44
43
|
|
|
45
44
|
// src/context.ts
|
|
46
45
|
var import_react = require("react");
|
|
@@ -53,6 +52,91 @@ function useStudio() {
|
|
|
53
52
|
return ctx;
|
|
54
53
|
}
|
|
55
54
|
|
|
55
|
+
// src/get-visible-nodes.ts
|
|
56
|
+
function getVisibleNodes(root, isExpanded) {
|
|
57
|
+
const result = [];
|
|
58
|
+
function walk(node) {
|
|
59
|
+
result.push(node);
|
|
60
|
+
if (isExpanded(node.id) && (node.type === "object" || node.type === "array")) {
|
|
61
|
+
for (const child of node.children) {
|
|
62
|
+
walk(child);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
walk(root);
|
|
67
|
+
return result;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// src/selection-utils.ts
|
|
71
|
+
var import_core = require("@visual-json/core");
|
|
72
|
+
function computeSelectAllIds(tree, focusedNodeId, currentlySelected) {
|
|
73
|
+
if (!focusedNodeId) return null;
|
|
74
|
+
let node = tree.nodesById.get(focusedNodeId);
|
|
75
|
+
if (!node) return null;
|
|
76
|
+
while (node) {
|
|
77
|
+
const parent = node.parentId ? tree.nodesById.get(node.parentId) : void 0;
|
|
78
|
+
const siblings = parent ? parent.children : [tree.root];
|
|
79
|
+
const siblingIds = new Set(siblings.map((s) => s.id));
|
|
80
|
+
const allSelected = siblings.every((s) => currentlySelected.has(s.id));
|
|
81
|
+
if (!allSelected) {
|
|
82
|
+
return siblingIds;
|
|
83
|
+
}
|
|
84
|
+
if (!parent || !parent.parentId) return siblingIds;
|
|
85
|
+
node = parent;
|
|
86
|
+
}
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
function computeRangeIds(visibleNodes, anchorId, targetId) {
|
|
90
|
+
const anchorIdx = visibleNodes.findIndex((n) => n.id === anchorId);
|
|
91
|
+
const targetIdx = visibleNodes.findIndex((n) => n.id === targetId);
|
|
92
|
+
if (anchorIdx === -1 || targetIdx === -1) return null;
|
|
93
|
+
const start = Math.min(anchorIdx, targetIdx);
|
|
94
|
+
const end = Math.max(anchorIdx, targetIdx);
|
|
95
|
+
const ids = /* @__PURE__ */ new Set();
|
|
96
|
+
for (let i = start; i <= end; i++) {
|
|
97
|
+
ids.add(visibleNodes[i].id);
|
|
98
|
+
}
|
|
99
|
+
return ids;
|
|
100
|
+
}
|
|
101
|
+
function deleteSelectedNodes(tree, selectedIds, visibleNodes) {
|
|
102
|
+
const idsToDelete = [...selectedIds].filter((id) => {
|
|
103
|
+
const node = tree.nodesById.get(id);
|
|
104
|
+
if (!node || node.parentId === null) return false;
|
|
105
|
+
let cur = tree.nodesById.get(node.parentId);
|
|
106
|
+
while (cur) {
|
|
107
|
+
if (selectedIds.has(cur.id)) return false;
|
|
108
|
+
cur = cur.parentId ? tree.nodesById.get(cur.parentId) : void 0;
|
|
109
|
+
}
|
|
110
|
+
return true;
|
|
111
|
+
});
|
|
112
|
+
if (idsToDelete.length === 0) return { newTree: tree, nextFocusId: null };
|
|
113
|
+
const firstDeletedIdx = visibleNodes.findIndex((n) => selectedIds.has(n.id));
|
|
114
|
+
let newTree = tree;
|
|
115
|
+
for (const id of idsToDelete) {
|
|
116
|
+
if (newTree.nodesById.has(id)) {
|
|
117
|
+
newTree = (0, import_core.removeNode)(newTree, id);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
let nextFocusId = null;
|
|
121
|
+
for (let i = firstDeletedIdx; i < visibleNodes.length; i++) {
|
|
122
|
+
const id = visibleNodes[i].id;
|
|
123
|
+
if (!selectedIds.has(id) && newTree.nodesById.has(id)) {
|
|
124
|
+
nextFocusId = id;
|
|
125
|
+
break;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
if (!nextFocusId) {
|
|
129
|
+
for (let i = firstDeletedIdx - 1; i >= 0; i--) {
|
|
130
|
+
const id = visibleNodes[i].id;
|
|
131
|
+
if (!selectedIds.has(id) && newTree.nodesById.has(id)) {
|
|
132
|
+
nextFocusId = id;
|
|
133
|
+
break;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
return { newTree, nextFocusId };
|
|
138
|
+
}
|
|
139
|
+
|
|
56
140
|
// src/visual-json.tsx
|
|
57
141
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
58
142
|
function collectAllIds(node) {
|
|
@@ -68,12 +152,44 @@ function VisualJson({
|
|
|
68
152
|
schema,
|
|
69
153
|
children
|
|
70
154
|
}) {
|
|
71
|
-
const [tree, setTreeState] = (0, import_react2.useState)(() => (0,
|
|
72
|
-
const [
|
|
155
|
+
const [tree, setTreeState] = (0, import_react2.useState)(() => (0, import_core2.fromJson)(value));
|
|
156
|
+
const [focusedNodeId, setFocusedNodeId] = (0, import_react2.useState)(null);
|
|
157
|
+
const [selectedNodeIds, setSelectedNodeIdsState] = (0, import_react2.useState)(
|
|
158
|
+
() => /* @__PURE__ */ new Set()
|
|
159
|
+
);
|
|
160
|
+
const selectedNodeIdsRef = (0, import_react2.useRef)(/* @__PURE__ */ new Set());
|
|
161
|
+
const setSelectedNodeIds = (0, import_react2.useCallback)((ids) => {
|
|
162
|
+
selectedNodeIdsRef.current = ids;
|
|
163
|
+
setSelectedNodeIdsState(ids);
|
|
164
|
+
}, []);
|
|
165
|
+
const anchorNodeIdRef = (0, import_react2.useRef)(null);
|
|
166
|
+
const [anchorNodeId, setAnchorNodeIdState] = (0, import_react2.useState)(null);
|
|
167
|
+
const [drillDownNodeId, setDrillDownNodeId] = (0, import_react2.useState)(null);
|
|
73
168
|
const [expandedNodeIds, setExpandedNodeIds] = (0, import_react2.useState)(
|
|
74
169
|
() => /* @__PURE__ */ new Set([tree.root.id])
|
|
75
170
|
);
|
|
76
|
-
const
|
|
171
|
+
const setAnchorNodeId = (0, import_react2.useCallback)((id) => {
|
|
172
|
+
anchorNodeIdRef.current = id;
|
|
173
|
+
setAnchorNodeIdState(id);
|
|
174
|
+
}, []);
|
|
175
|
+
const focusSelectAndDrillDown = (0, import_react2.useCallback)(
|
|
176
|
+
(nodeId) => {
|
|
177
|
+
setFocusedNodeId(nodeId);
|
|
178
|
+
setSelectedNodeIds(nodeId ? /* @__PURE__ */ new Set([nodeId]) : /* @__PURE__ */ new Set());
|
|
179
|
+
setAnchorNodeId(nodeId);
|
|
180
|
+
setDrillDownNodeId(nodeId);
|
|
181
|
+
},
|
|
182
|
+
[setSelectedNodeIds, setAnchorNodeId]
|
|
183
|
+
);
|
|
184
|
+
const visibleNodes = (0, import_react2.useMemo)(
|
|
185
|
+
() => getVisibleNodes(tree.root, (id) => expandedNodeIds.has(id)),
|
|
186
|
+
[tree.root, expandedNodeIds]
|
|
187
|
+
);
|
|
188
|
+
const visibleNodesOverrideRef = (0, import_react2.useRef)(null);
|
|
189
|
+
const setVisibleNodesOverride = (0, import_react2.useCallback)((nodes) => {
|
|
190
|
+
visibleNodesOverrideRef.current = nodes;
|
|
191
|
+
}, []);
|
|
192
|
+
const historyRef = (0, import_react2.useRef)(new import_core2.History());
|
|
77
193
|
const isInternalChange = (0, import_react2.useRef)(false);
|
|
78
194
|
const hasMounted = (0, import_react2.useRef)(false);
|
|
79
195
|
const [canUndo, setCanUndo] = (0, import_react2.useState)(false);
|
|
@@ -98,11 +214,11 @@ function VisualJson({
|
|
|
98
214
|
isInternalChange.current = false;
|
|
99
215
|
return;
|
|
100
216
|
}
|
|
101
|
-
const newTree = (0,
|
|
217
|
+
const newTree = (0, import_core2.fromJson)(value);
|
|
102
218
|
setTreeState(newTree);
|
|
103
219
|
setExpandedNodeIds(/* @__PURE__ */ new Set([newTree.root.id]));
|
|
104
|
-
|
|
105
|
-
historyRef.current = new
|
|
220
|
+
focusSelectAndDrillDown(null);
|
|
221
|
+
historyRef.current = new import_core2.History();
|
|
106
222
|
historyRef.current.push(newTree);
|
|
107
223
|
setCanUndo(false);
|
|
108
224
|
setCanRedo(false);
|
|
@@ -118,7 +234,7 @@ function VisualJson({
|
|
|
118
234
|
setCanUndo(historyRef.current.canUndo);
|
|
119
235
|
setCanRedo(historyRef.current.canRedo);
|
|
120
236
|
isInternalChange.current = true;
|
|
121
|
-
onChange?.((0,
|
|
237
|
+
onChange?.((0, import_core2.toJson)(newTree.root));
|
|
122
238
|
},
|
|
123
239
|
[onChange]
|
|
124
240
|
);
|
|
@@ -129,7 +245,7 @@ function VisualJson({
|
|
|
129
245
|
setCanUndo(historyRef.current.canUndo);
|
|
130
246
|
setCanRedo(historyRef.current.canRedo);
|
|
131
247
|
isInternalChange.current = true;
|
|
132
|
-
onChange?.((0,
|
|
248
|
+
onChange?.((0, import_core2.toJson)(prev.root));
|
|
133
249
|
}
|
|
134
250
|
}, [onChange]);
|
|
135
251
|
const redo = (0, import_react2.useCallback)(() => {
|
|
@@ -139,7 +255,7 @@ function VisualJson({
|
|
|
139
255
|
setCanUndo(historyRef.current.canUndo);
|
|
140
256
|
setCanRedo(historyRef.current.canRedo);
|
|
141
257
|
isInternalChange.current = true;
|
|
142
|
-
onChange?.((0,
|
|
258
|
+
onChange?.((0, import_core2.toJson)(next.root));
|
|
143
259
|
}
|
|
144
260
|
}, [onChange]);
|
|
145
261
|
(0, import_react2.useEffect)(() => {
|
|
@@ -159,8 +275,66 @@ function VisualJson({
|
|
|
159
275
|
document.addEventListener("keydown", handleKeyDown);
|
|
160
276
|
return () => document.removeEventListener("keydown", handleKeyDown);
|
|
161
277
|
}, [undo, redo]);
|
|
162
|
-
const selectNode = (0, import_react2.useCallback)(
|
|
163
|
-
|
|
278
|
+
const selectNode = (0, import_react2.useCallback)(
|
|
279
|
+
(nodeId) => {
|
|
280
|
+
setFocusedNodeId(nodeId);
|
|
281
|
+
setSelectedNodeIds(nodeId ? /* @__PURE__ */ new Set([nodeId]) : /* @__PURE__ */ new Set());
|
|
282
|
+
setAnchorNodeId(nodeId);
|
|
283
|
+
},
|
|
284
|
+
[setSelectedNodeIds, setAnchorNodeId]
|
|
285
|
+
);
|
|
286
|
+
const selectAndDrillDown = focusSelectAndDrillDown;
|
|
287
|
+
const toggleNodeSelection = (0, import_react2.useCallback)(
|
|
288
|
+
(nodeId) => {
|
|
289
|
+
const next = new Set(selectedNodeIdsRef.current);
|
|
290
|
+
if (next.has(nodeId)) {
|
|
291
|
+
next.delete(nodeId);
|
|
292
|
+
} else {
|
|
293
|
+
next.add(nodeId);
|
|
294
|
+
}
|
|
295
|
+
setSelectedNodeIds(next);
|
|
296
|
+
if (next.size === 0) {
|
|
297
|
+
setFocusedNodeId(null);
|
|
298
|
+
setAnchorNodeId(null);
|
|
299
|
+
} else {
|
|
300
|
+
setFocusedNodeId(nodeId);
|
|
301
|
+
setAnchorNodeId(nodeId);
|
|
302
|
+
}
|
|
303
|
+
},
|
|
304
|
+
[setSelectedNodeIds, setAnchorNodeId]
|
|
305
|
+
);
|
|
306
|
+
const selectNodeRange = (0, import_react2.useCallback)(
|
|
307
|
+
(toNodeId) => {
|
|
308
|
+
const nodes = visibleNodesOverrideRef.current ?? visibleNodes;
|
|
309
|
+
const anchor = anchorNodeIdRef.current;
|
|
310
|
+
if (!anchor) {
|
|
311
|
+
setFocusedNodeId(toNodeId);
|
|
312
|
+
setSelectedNodeIds(/* @__PURE__ */ new Set([toNodeId]));
|
|
313
|
+
setAnchorNodeId(toNodeId);
|
|
314
|
+
return;
|
|
315
|
+
}
|
|
316
|
+
const rangeIds = computeRangeIds(nodes, anchor, toNodeId);
|
|
317
|
+
if (!rangeIds) {
|
|
318
|
+
setFocusedNodeId(toNodeId);
|
|
319
|
+
setSelectedNodeIds(/* @__PURE__ */ new Set([toNodeId]));
|
|
320
|
+
setAnchorNodeId(toNodeId);
|
|
321
|
+
return;
|
|
322
|
+
}
|
|
323
|
+
setSelectedNodeIds(rangeIds);
|
|
324
|
+
setFocusedNodeId(toNodeId);
|
|
325
|
+
},
|
|
326
|
+
[visibleNodes, setSelectedNodeIds, setAnchorNodeId]
|
|
327
|
+
);
|
|
328
|
+
const setSelection = (0, import_react2.useCallback)(
|
|
329
|
+
(focusedId, newSelectedIds, newAnchorId) => {
|
|
330
|
+
setFocusedNodeId(focusedId);
|
|
331
|
+
setSelectedNodeIds(newSelectedIds);
|
|
332
|
+
setAnchorNodeId(newAnchorId);
|
|
333
|
+
},
|
|
334
|
+
[setSelectedNodeIds, setAnchorNodeId]
|
|
335
|
+
);
|
|
336
|
+
const drillDown = (0, import_react2.useCallback)((nodeId) => {
|
|
337
|
+
setDrillDownNodeId(nodeId);
|
|
164
338
|
}, []);
|
|
165
339
|
const toggleExpand = (0, import_react2.useCallback)((nodeId) => {
|
|
166
340
|
setExpandedNodeIds((prev) => {
|
|
@@ -200,13 +374,14 @@ function VisualJson({
|
|
|
200
374
|
setSearchMatchNodeIds(/* @__PURE__ */ new Set());
|
|
201
375
|
return;
|
|
202
376
|
}
|
|
203
|
-
const matches = (0,
|
|
377
|
+
const matches = (0, import_core2.searchNodes)(tree, query);
|
|
204
378
|
setSearchMatches(matches);
|
|
205
379
|
setSearchMatchIndex(0);
|
|
206
380
|
const matchIds = new Set(matches.map((m) => m.nodeId));
|
|
207
381
|
setSearchMatchNodeIds(matchIds);
|
|
208
382
|
if (matches.length > 0) {
|
|
209
|
-
const
|
|
383
|
+
const firstId = matches[0].nodeId;
|
|
384
|
+
const ancestors = (0, import_core2.getAncestorIds)(
|
|
210
385
|
tree,
|
|
211
386
|
matches.map((m) => m.nodeId)
|
|
212
387
|
);
|
|
@@ -215,7 +390,7 @@ function VisualJson({
|
|
|
215
390
|
for (const id of ancestors) next.add(id);
|
|
216
391
|
return next;
|
|
217
392
|
});
|
|
218
|
-
|
|
393
|
+
focusSelectAndDrillDown(firstId);
|
|
219
394
|
}
|
|
220
395
|
},
|
|
221
396
|
[tree]
|
|
@@ -224,17 +399,17 @@ function VisualJson({
|
|
|
224
399
|
if (searchMatches.length === 0) return;
|
|
225
400
|
const nextIdx = (searchMatchIndex + 1) % searchMatches.length;
|
|
226
401
|
setSearchMatchIndex(nextIdx);
|
|
227
|
-
|
|
228
|
-
}, [searchMatches, searchMatchIndex]);
|
|
402
|
+
focusSelectAndDrillDown(searchMatches[nextIdx].nodeId);
|
|
403
|
+
}, [searchMatches, searchMatchIndex, focusSelectAndDrillDown]);
|
|
229
404
|
const prevSearchMatch = (0, import_react2.useCallback)(() => {
|
|
230
405
|
if (searchMatches.length === 0) return;
|
|
231
406
|
const prevIdx = (searchMatchIndex - 1 + searchMatches.length) % searchMatches.length;
|
|
232
407
|
setSearchMatchIndex(prevIdx);
|
|
233
|
-
|
|
234
|
-
}, [searchMatches, searchMatchIndex]);
|
|
408
|
+
focusSelectAndDrillDown(searchMatches[prevIdx].nodeId);
|
|
409
|
+
}, [searchMatches, searchMatchIndex, focusSelectAndDrillDown]);
|
|
235
410
|
(0, import_react2.useEffect)(() => {
|
|
236
411
|
if (!searchQuery.trim()) return;
|
|
237
|
-
const matches = (0,
|
|
412
|
+
const matches = (0, import_core2.searchNodes)(tree, searchQuery);
|
|
238
413
|
setSearchMatches(matches);
|
|
239
414
|
setSearchMatchIndex(
|
|
240
415
|
(prev) => Math.min(prev, Math.max(matches.length - 1, 0))
|
|
@@ -244,7 +419,10 @@ function VisualJson({
|
|
|
244
419
|
const state = (0, import_react2.useMemo)(
|
|
245
420
|
() => ({
|
|
246
421
|
tree,
|
|
247
|
-
|
|
422
|
+
focusedNodeId,
|
|
423
|
+
selectedNodeIds,
|
|
424
|
+
anchorNodeId,
|
|
425
|
+
drillDownNodeId,
|
|
248
426
|
expandedNodeIds,
|
|
249
427
|
schema: schema ?? null,
|
|
250
428
|
searchQuery,
|
|
@@ -254,7 +432,10 @@ function VisualJson({
|
|
|
254
432
|
}),
|
|
255
433
|
[
|
|
256
434
|
tree,
|
|
257
|
-
|
|
435
|
+
focusedNodeId,
|
|
436
|
+
selectedNodeIds,
|
|
437
|
+
anchorNodeId,
|
|
438
|
+
drillDownNodeId,
|
|
258
439
|
expandedNodeIds,
|
|
259
440
|
schema,
|
|
260
441
|
searchQuery,
|
|
@@ -267,6 +448,12 @@ function VisualJson({
|
|
|
267
448
|
() => ({
|
|
268
449
|
setTree,
|
|
269
450
|
selectNode,
|
|
451
|
+
selectAndDrillDown,
|
|
452
|
+
toggleNodeSelection,
|
|
453
|
+
selectNodeRange,
|
|
454
|
+
setSelection,
|
|
455
|
+
setVisibleNodesOverride,
|
|
456
|
+
drillDown,
|
|
270
457
|
toggleExpand,
|
|
271
458
|
expandNode,
|
|
272
459
|
collapseNode,
|
|
@@ -283,6 +470,12 @@ function VisualJson({
|
|
|
283
470
|
[
|
|
284
471
|
setTree,
|
|
285
472
|
selectNode,
|
|
473
|
+
selectAndDrillDown,
|
|
474
|
+
toggleNodeSelection,
|
|
475
|
+
selectNodeRange,
|
|
476
|
+
setSelection,
|
|
477
|
+
setVisibleNodesOverride,
|
|
478
|
+
drillDown,
|
|
286
479
|
toggleExpand,
|
|
287
480
|
expandNode,
|
|
288
481
|
collapseNode,
|
|
@@ -303,7 +496,7 @@ function VisualJson({
|
|
|
303
496
|
|
|
304
497
|
// src/tree-view.tsx
|
|
305
498
|
var import_react5 = require("react");
|
|
306
|
-
var
|
|
499
|
+
var import_core4 = require("@visual-json/core");
|
|
307
500
|
|
|
308
501
|
// src/context-menu.tsx
|
|
309
502
|
var import_react3 = require("react");
|
|
@@ -417,96 +610,194 @@ function getDisplayKey(node, state) {
|
|
|
417
610
|
return node.key;
|
|
418
611
|
}
|
|
419
612
|
|
|
420
|
-
// src/get-visible-nodes.ts
|
|
421
|
-
function getVisibleNodes(root, isExpanded) {
|
|
422
|
-
const result = [];
|
|
423
|
-
function walk(node) {
|
|
424
|
-
result.push(node);
|
|
425
|
-
if (isExpanded(node.id) && (node.type === "object" || node.type === "array")) {
|
|
426
|
-
for (const child of node.children) {
|
|
427
|
-
walk(child);
|
|
428
|
-
}
|
|
429
|
-
}
|
|
430
|
-
}
|
|
431
|
-
walk(root);
|
|
432
|
-
return result;
|
|
433
|
-
}
|
|
434
|
-
|
|
435
613
|
// src/use-drag-drop.ts
|
|
436
614
|
var import_react4 = require("react");
|
|
437
|
-
var
|
|
615
|
+
var import_core3 = require("@visual-json/core");
|
|
616
|
+
|
|
617
|
+
// src/theme.ts
|
|
618
|
+
var DEFAULT_CSS_VARS = {
|
|
619
|
+
"--vj-bg": "#1e1e1e",
|
|
620
|
+
"--vj-bg-panel": "#252526",
|
|
621
|
+
"--vj-bg-hover": "#2a2d2e",
|
|
622
|
+
"--vj-bg-selected": "#2a5a1e",
|
|
623
|
+
"--vj-bg-selected-muted": "#2a2d2e",
|
|
624
|
+
"--vj-bg-match": "#3a3520",
|
|
625
|
+
"--vj-bg-match-active": "#51502b",
|
|
626
|
+
"--vj-border": "#333333",
|
|
627
|
+
"--vj-border-subtle": "#2a2a2a",
|
|
628
|
+
"--vj-text": "#cccccc",
|
|
629
|
+
"--vj-text-muted": "#888888",
|
|
630
|
+
"--vj-text-dim": "#666666",
|
|
631
|
+
"--vj-text-dimmer": "#555555",
|
|
632
|
+
"--vj-string": "#ce9178",
|
|
633
|
+
"--vj-number": "#b5cea8",
|
|
634
|
+
"--vj-boolean": "#569cd6",
|
|
635
|
+
"--vj-accent": "#007acc",
|
|
636
|
+
"--vj-accent-muted": "#094771",
|
|
637
|
+
"--vj-input-bg": "#3c3c3c",
|
|
638
|
+
"--vj-input-border": "#555555",
|
|
639
|
+
"--vj-error": "#f48771",
|
|
640
|
+
"--vj-font": "monospace",
|
|
641
|
+
"--vj-input-font-size": "13px"
|
|
642
|
+
};
|
|
643
|
+
|
|
644
|
+
// src/use-drag-drop.ts
|
|
645
|
+
var EMPTY_SET = Object.freeze(/* @__PURE__ */ new Set());
|
|
438
646
|
var INITIAL_DRAG_STATE = {
|
|
439
|
-
|
|
647
|
+
draggedNodeIds: EMPTY_SET,
|
|
440
648
|
dropTargetNodeId: null,
|
|
441
649
|
dropPosition: null
|
|
442
650
|
};
|
|
443
|
-
function
|
|
651
|
+
function sortByTreeOrder(root, ids) {
|
|
652
|
+
const result = [];
|
|
653
|
+
function walk(node) {
|
|
654
|
+
if (ids.has(node.id)) result.push(node.id);
|
|
655
|
+
for (const child of node.children) walk(child);
|
|
656
|
+
}
|
|
657
|
+
walk(root);
|
|
658
|
+
return result;
|
|
659
|
+
}
|
|
660
|
+
function setMultiDragImage(e, count) {
|
|
661
|
+
const ghost = document.createElement("div");
|
|
662
|
+
ghost.textContent = `${count} selected`;
|
|
663
|
+
const root = document.querySelector("[data-form-container], [role='tree']");
|
|
664
|
+
const cs = root ? getComputedStyle(root) : null;
|
|
665
|
+
const bg = cs?.getPropertyValue("--vj-bg-selected").trim() || DEFAULT_CSS_VARS["--vj-bg-selected"];
|
|
666
|
+
const fg = cs?.getPropertyValue("--vj-text-selected").trim() || cs?.getPropertyValue("--vj-text").trim() || DEFAULT_CSS_VARS["--vj-text"];
|
|
667
|
+
const font = cs?.getPropertyValue("--vj-font").trim() || DEFAULT_CSS_VARS["--vj-font"];
|
|
668
|
+
ghost.style.cssText = [
|
|
669
|
+
"position:fixed",
|
|
670
|
+
"top:-1000px",
|
|
671
|
+
"left:-1000px",
|
|
672
|
+
"padding:4px 12px",
|
|
673
|
+
`background:${bg}`,
|
|
674
|
+
`color:${fg}`,
|
|
675
|
+
`font-family:${font}`,
|
|
676
|
+
"font-size:13px",
|
|
677
|
+
"border-radius:4px",
|
|
678
|
+
"white-space:nowrap",
|
|
679
|
+
"pointer-events:none"
|
|
680
|
+
].join(";");
|
|
681
|
+
document.body.appendChild(ghost);
|
|
682
|
+
e.dataTransfer.setDragImage(ghost, 0, 14);
|
|
683
|
+
requestAnimationFrame(() => ghost.remove());
|
|
684
|
+
}
|
|
685
|
+
function useDragDrop(visibleNodes, selectedNodeIds) {
|
|
444
686
|
const { state, actions } = useStudio();
|
|
445
687
|
const [dragState, setDragState] = (0, import_react4.useState)(INITIAL_DRAG_STATE);
|
|
446
688
|
const dragStateRef = (0, import_react4.useRef)(dragState);
|
|
447
689
|
dragStateRef.current = dragState;
|
|
448
|
-
const
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
690
|
+
const visibleNodeIndexMap = (0, import_react4.useMemo)(() => {
|
|
691
|
+
const map = /* @__PURE__ */ new Map();
|
|
692
|
+
visibleNodes.forEach((n, i) => map.set(n.id, i));
|
|
693
|
+
return map;
|
|
694
|
+
}, [visibleNodes]);
|
|
695
|
+
const handleDragStart = (0, import_react4.useCallback)(
|
|
696
|
+
(nodeId) => {
|
|
697
|
+
let ids;
|
|
698
|
+
if (selectedNodeIds.size > 0 && selectedNodeIds.has(nodeId)) {
|
|
699
|
+
ids = selectedNodeIds;
|
|
700
|
+
} else {
|
|
701
|
+
ids = /* @__PURE__ */ new Set([nodeId]);
|
|
702
|
+
}
|
|
703
|
+
setDragState({
|
|
704
|
+
draggedNodeIds: ids,
|
|
705
|
+
dropTargetNodeId: null,
|
|
706
|
+
dropPosition: null
|
|
707
|
+
});
|
|
708
|
+
},
|
|
709
|
+
[selectedNodeIds]
|
|
710
|
+
);
|
|
711
|
+
const rawDragOver = (0, import_react4.useCallback)(
|
|
456
712
|
(nodeId, position) => {
|
|
713
|
+
const draggedIds = dragStateRef.current.draggedNodeIds;
|
|
714
|
+
for (const draggedId of draggedIds) {
|
|
715
|
+
if (nodeId === draggedId || (0, import_core3.isDescendant)(state.tree, nodeId, draggedId)) {
|
|
716
|
+
return;
|
|
717
|
+
}
|
|
718
|
+
}
|
|
457
719
|
setDragState((prev) => ({
|
|
458
720
|
...prev,
|
|
459
721
|
dropTargetNodeId: nodeId,
|
|
460
722
|
dropPosition: position
|
|
461
723
|
}));
|
|
462
724
|
},
|
|
463
|
-
[]
|
|
725
|
+
[state.tree]
|
|
726
|
+
);
|
|
727
|
+
const handleDragOver = (0, import_react4.useCallback)(
|
|
728
|
+
(nodeId, position) => {
|
|
729
|
+
if (position === "before") {
|
|
730
|
+
const idx = visibleNodeIndexMap.get(nodeId);
|
|
731
|
+
if (idx !== void 0 && idx > 0) {
|
|
732
|
+
rawDragOver(visibleNodes[idx - 1].id, "after");
|
|
733
|
+
return;
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
rawDragOver(nodeId, position);
|
|
737
|
+
},
|
|
738
|
+
[visibleNodes, visibleNodeIndexMap, rawDragOver]
|
|
464
739
|
);
|
|
465
740
|
const handleDragEnd = (0, import_react4.useCallback)(() => {
|
|
466
741
|
setDragState(INITIAL_DRAG_STATE);
|
|
467
742
|
}, []);
|
|
468
743
|
const handleDrop = (0, import_react4.useCallback)(() => {
|
|
469
|
-
const {
|
|
470
|
-
if (
|
|
471
|
-
const draggedNode = state.tree.nodesById.get(draggedNodeId);
|
|
744
|
+
const { draggedNodeIds, dropTargetNodeId, dropPosition } = dragStateRef.current;
|
|
745
|
+
if (draggedNodeIds.size === 0 || !dropTargetNodeId || !dropPosition) return;
|
|
472
746
|
const targetNode = state.tree.nodesById.get(dropTargetNodeId);
|
|
473
|
-
if (!
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
747
|
+
if (!targetNode || !targetNode.parentId) return;
|
|
748
|
+
for (const id of draggedNodeIds) {
|
|
749
|
+
if ((0, import_core3.isDescendant)(state.tree, dropTargetNodeId, id)) return;
|
|
750
|
+
}
|
|
751
|
+
const targetParentId = targetNode.parentId;
|
|
752
|
+
const targetParent = state.tree.nodesById.get(targetParentId);
|
|
753
|
+
if (!targetParent) return;
|
|
754
|
+
const parentChildren = targetParent.children;
|
|
755
|
+
const orderedDragIds = parentChildren.filter((c) => draggedNodeIds.has(c.id)).map((c) => c.id);
|
|
756
|
+
const allSameParent = orderedDragIds.length === draggedNodeIds.size && [...draggedNodeIds].every((id) => {
|
|
757
|
+
const n = state.tree.nodesById.get(id);
|
|
758
|
+
return n?.parentId === targetParentId;
|
|
759
|
+
});
|
|
760
|
+
if (allSameParent) {
|
|
761
|
+
const newTree = (0, import_core3.reorderChildrenMulti)(
|
|
762
|
+
state.tree,
|
|
763
|
+
targetParentId,
|
|
764
|
+
orderedDragIds,
|
|
765
|
+
dropTargetNodeId,
|
|
766
|
+
dropPosition
|
|
767
|
+
);
|
|
768
|
+
actions.setTree(newTree);
|
|
769
|
+
} else {
|
|
770
|
+
const orderedIds = sortByTreeOrder(state.tree.root, draggedNodeIds);
|
|
771
|
+
const draggedNodes = orderedIds.map((id) => state.tree.nodesById.get(id)).filter((n) => !!n && n.parentId !== null).map((n) => structuredClone(n));
|
|
772
|
+
let newTree = state.tree;
|
|
773
|
+
for (const id of [...orderedIds].reverse()) {
|
|
774
|
+
if (newTree.nodesById.has(id)) {
|
|
775
|
+
newTree = (0, import_core3.removeNode)(newTree, id);
|
|
493
776
|
}
|
|
494
777
|
}
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
778
|
+
const updatedTarget = newTree.nodesById.get(dropTargetNodeId);
|
|
779
|
+
if (!updatedTarget || !updatedTarget.parentId) {
|
|
780
|
+
setDragState(INITIAL_DRAG_STATE);
|
|
781
|
+
return;
|
|
782
|
+
}
|
|
783
|
+
const updatedParent = newTree.nodesById.get(updatedTarget.parentId);
|
|
784
|
+
if (!updatedParent) {
|
|
785
|
+
setDragState(INITIAL_DRAG_STATE);
|
|
786
|
+
return;
|
|
787
|
+
}
|
|
788
|
+
let insertIdx = updatedParent.children.findIndex(
|
|
789
|
+
(c) => c.id === dropTargetNodeId
|
|
790
|
+
);
|
|
791
|
+
if (dropPosition === "after") insertIdx++;
|
|
792
|
+
for (let i = 0; i < draggedNodes.length; i++) {
|
|
793
|
+
newTree = (0, import_core3.insertNode)(
|
|
794
|
+
newTree,
|
|
795
|
+
updatedParent.id,
|
|
796
|
+
draggedNodes[i],
|
|
797
|
+
insertIdx + i
|
|
507
798
|
);
|
|
508
|
-
actions.setTree(newTree);
|
|
509
799
|
}
|
|
800
|
+
actions.setTree(newTree);
|
|
510
801
|
}
|
|
511
802
|
setDragState(INITIAL_DRAG_STATE);
|
|
512
803
|
}, [state.tree, actions]);
|
|
@@ -528,6 +819,7 @@ function TreeNodeRow({
|
|
|
528
819
|
showValues,
|
|
529
820
|
showCounts,
|
|
530
821
|
isFocused,
|
|
822
|
+
onSelectRange,
|
|
531
823
|
onDragStart,
|
|
532
824
|
onDragOver,
|
|
533
825
|
onDragEnd,
|
|
@@ -535,19 +827,15 @@ function TreeNodeRow({
|
|
|
535
827
|
onContextMenu
|
|
536
828
|
}) {
|
|
537
829
|
const { state, actions } = useStudio();
|
|
538
|
-
const isSelected = state.
|
|
830
|
+
const isSelected = state.selectedNodeIds.has(node.id);
|
|
539
831
|
const isExpanded = state.expandedNodeIds.has(node.id);
|
|
540
832
|
const isContainer = node.type === "object" || node.type === "array";
|
|
541
833
|
const [hovered, setHovered] = (0, import_react5.useState)(false);
|
|
542
834
|
const isRoot = node.parentId === null;
|
|
543
835
|
const isSearchMatch = state.searchMatchNodeIds.has(node.id);
|
|
544
836
|
const isActiveMatch = state.searchMatches.length > 0 && state.searchMatches[state.searchMatchIndex]?.nodeId === node.id;
|
|
545
|
-
const schema = state.schema;
|
|
546
|
-
const nodeSchema = schema ? (0, import_core3.getPropertySchema)(schema, node.path) : void 0;
|
|
547
|
-
const validation = nodeSchema ? (0, import_core3.validateNode)(node, nodeSchema) : null;
|
|
548
|
-
const hasError = validation ? !validation.valid : false;
|
|
549
837
|
const isDragTarget = dragState.dropTargetNodeId === node.id;
|
|
550
|
-
const isDraggedNode = dragState.
|
|
838
|
+
const isDraggedNode = dragState.draggedNodeIds.has(node.id);
|
|
551
839
|
function displayValue() {
|
|
552
840
|
if (isContainer) {
|
|
553
841
|
return node.type === "array" ? `[${node.children.length}]` : `{${node.children.length}}`;
|
|
@@ -563,12 +851,12 @@ function TreeNodeRow({
|
|
|
563
851
|
const position = e.clientY < midY ? "before" : "after";
|
|
564
852
|
onDragOver(node.id, position);
|
|
565
853
|
}
|
|
566
|
-
let
|
|
567
|
-
let
|
|
854
|
+
let borderTopColor = "transparent";
|
|
855
|
+
let borderBottomColor = "transparent";
|
|
568
856
|
if (isDragTarget && dragState.dropPosition === "before") {
|
|
569
|
-
|
|
857
|
+
borderTopColor = "var(--vj-accent, #007acc)";
|
|
570
858
|
} else if (isDragTarget && dragState.dropPosition === "after") {
|
|
571
|
-
|
|
859
|
+
borderBottomColor = "var(--vj-accent, #007acc)";
|
|
572
860
|
}
|
|
573
861
|
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
|
|
574
862
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
@@ -577,13 +865,24 @@ function TreeNodeRow({
|
|
|
577
865
|
role: "treeitem",
|
|
578
866
|
"aria-selected": isSelected,
|
|
579
867
|
"aria-expanded": isContainer ? isExpanded : void 0,
|
|
580
|
-
onClick: () =>
|
|
868
|
+
onClick: (e) => {
|
|
869
|
+
if (e.shiftKey) {
|
|
870
|
+
onSelectRange(node.id);
|
|
871
|
+
} else if (e.metaKey || e.ctrlKey) {
|
|
872
|
+
actions.toggleNodeSelection(node.id);
|
|
873
|
+
} else {
|
|
874
|
+
actions.selectAndDrillDown(node.id);
|
|
875
|
+
}
|
|
876
|
+
},
|
|
581
877
|
onMouseEnter: () => setHovered(true),
|
|
582
878
|
onMouseLeave: () => setHovered(false),
|
|
583
879
|
onContextMenu: (e) => onContextMenu(e, node),
|
|
584
880
|
draggable: !isRoot,
|
|
585
881
|
onDragStart: (e) => {
|
|
586
882
|
e.dataTransfer.effectAllowed = "move";
|
|
883
|
+
if (state.selectedNodeIds.size > 1 && state.selectedNodeIds.has(node.id)) {
|
|
884
|
+
setMultiDragImage(e, state.selectedNodeIds.size);
|
|
885
|
+
}
|
|
587
886
|
onDragStart(node.id);
|
|
588
887
|
},
|
|
589
888
|
onDragOver: handleDragOverEvent,
|
|
@@ -597,15 +896,16 @@ function TreeNodeRow({
|
|
|
597
896
|
display: "flex",
|
|
598
897
|
alignItems: "center",
|
|
599
898
|
gap: 6,
|
|
600
|
-
padding: "
|
|
899
|
+
padding: "1px 8px",
|
|
601
900
|
paddingLeft: 8 + depth * 16,
|
|
602
901
|
cursor: "pointer",
|
|
603
902
|
backgroundColor: isSelected ? isFocused ? "var(--vj-bg-selected, #2a5a1e)" : "var(--vj-bg-selected-muted, var(--vj-bg-hover, #2a2d2e))" : isActiveMatch ? "var(--vj-bg-match-active, #51502b)" : isSearchMatch ? "var(--vj-bg-match, #3a3520)" : hovered ? "var(--vj-bg-hover, #2a2d2e)" : "transparent",
|
|
604
903
|
minHeight: 28,
|
|
605
904
|
userSelect: "none",
|
|
606
905
|
opacity: isDraggedNode ? 0.4 : 1,
|
|
607
|
-
borderTop
|
|
608
|
-
borderBottom
|
|
906
|
+
borderTop: `2px solid ${borderTopColor}`,
|
|
907
|
+
borderBottom: `2px solid ${borderBottomColor}`,
|
|
908
|
+
boxSizing: "border-box",
|
|
609
909
|
color: isSelected && isFocused ? "var(--vj-text-selected, var(--vj-text, #cccccc))" : "var(--vj-text, #cccccc)"
|
|
610
910
|
},
|
|
611
911
|
children: [
|
|
@@ -688,6 +988,7 @@ function TreeNodeRow({
|
|
|
688
988
|
showValues,
|
|
689
989
|
showCounts,
|
|
690
990
|
isFocused,
|
|
991
|
+
onSelectRange,
|
|
691
992
|
onDragStart,
|
|
692
993
|
onDragOver,
|
|
693
994
|
onDragEnd,
|
|
@@ -705,25 +1006,34 @@ function TreeView({
|
|
|
705
1006
|
}) {
|
|
706
1007
|
const { state, actions } = useStudio();
|
|
707
1008
|
const containerRef = (0, import_react5.useRef)(null);
|
|
1009
|
+
const visibleNodes = (0, import_react5.useMemo)(
|
|
1010
|
+
() => getVisibleNodes(state.tree.root, (id) => state.expandedNodeIds.has(id)),
|
|
1011
|
+
[state.tree.root, state.expandedNodeIds]
|
|
1012
|
+
);
|
|
708
1013
|
const {
|
|
709
1014
|
dragState,
|
|
710
1015
|
handleDragStart,
|
|
711
1016
|
handleDragOver,
|
|
712
1017
|
handleDragEnd,
|
|
713
1018
|
handleDrop
|
|
714
|
-
} = useDragDrop();
|
|
715
|
-
const
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
1019
|
+
} = useDragDrop(visibleNodes, state.selectedNodeIds);
|
|
1020
|
+
const handleSelectRange = (0, import_react5.useCallback)(
|
|
1021
|
+
(nodeId) => {
|
|
1022
|
+
actions.setVisibleNodesOverride(visibleNodes);
|
|
1023
|
+
actions.selectNodeRange(nodeId);
|
|
1024
|
+
},
|
|
1025
|
+
[visibleNodes, actions]
|
|
719
1026
|
);
|
|
1027
|
+
const [contextMenu, setContextMenu] = (0, import_react5.useState)(null);
|
|
720
1028
|
const handleContextMenu = (0, import_react5.useCallback)(
|
|
721
1029
|
(e, node) => {
|
|
722
1030
|
e.preventDefault();
|
|
723
|
-
|
|
1031
|
+
if (!state.selectedNodeIds.has(node.id)) {
|
|
1032
|
+
actions.selectAndDrillDown(node.id);
|
|
1033
|
+
}
|
|
724
1034
|
setContextMenu({ x: e.clientX, y: e.clientY, node });
|
|
725
1035
|
},
|
|
726
|
-
[actions]
|
|
1036
|
+
[actions, state.selectedNodeIds]
|
|
727
1037
|
);
|
|
728
1038
|
const buildContextMenuItems = (0, import_react5.useCallback)(
|
|
729
1039
|
(node) => {
|
|
@@ -767,7 +1077,7 @@ function TreeView({
|
|
|
767
1077
|
items.push({
|
|
768
1078
|
label: "Copy value as JSON",
|
|
769
1079
|
action: () => {
|
|
770
|
-
const val = (0,
|
|
1080
|
+
const val = (0, import_core4.toJson)(node);
|
|
771
1081
|
const text = typeof val === "string" ? val : JSON.stringify(val, null, 2);
|
|
772
1082
|
navigator.clipboard.writeText(text).catch(() => {
|
|
773
1083
|
});
|
|
@@ -778,7 +1088,7 @@ function TreeView({
|
|
|
778
1088
|
items.push({
|
|
779
1089
|
label: "Duplicate",
|
|
780
1090
|
action: () => {
|
|
781
|
-
const newTree = (0,
|
|
1091
|
+
const newTree = (0, import_core4.duplicateNode)(state.tree, node.id);
|
|
782
1092
|
actions.setTree(newTree);
|
|
783
1093
|
}
|
|
784
1094
|
});
|
|
@@ -792,7 +1102,7 @@ function TreeView({
|
|
|
792
1102
|
].filter((t) => t !== node.type).map((t) => ({
|
|
793
1103
|
label: `Change to ${t}`,
|
|
794
1104
|
action: () => {
|
|
795
|
-
const newTree = (0,
|
|
1105
|
+
const newTree = (0, import_core4.changeType)(state.tree, node.id, t);
|
|
796
1106
|
actions.setTree(newTree);
|
|
797
1107
|
}
|
|
798
1108
|
}));
|
|
@@ -802,7 +1112,7 @@ function TreeView({
|
|
|
802
1112
|
items.push({
|
|
803
1113
|
label: "Delete",
|
|
804
1114
|
action: () => {
|
|
805
|
-
const newTree = (0,
|
|
1115
|
+
const newTree = (0, import_core4.removeNode)(state.tree, node.id);
|
|
806
1116
|
actions.setTree(newTree);
|
|
807
1117
|
}
|
|
808
1118
|
});
|
|
@@ -814,19 +1124,31 @@ function TreeView({
|
|
|
814
1124
|
const handleKeyDown = (0, import_react5.useCallback)(
|
|
815
1125
|
(e) => {
|
|
816
1126
|
const currentIndex = visibleNodes.findIndex(
|
|
817
|
-
(n) => n.id === state.
|
|
1127
|
+
(n) => n.id === state.focusedNodeId
|
|
818
1128
|
);
|
|
819
1129
|
switch (e.key) {
|
|
820
1130
|
case "ArrowDown": {
|
|
821
1131
|
e.preventDefault();
|
|
822
1132
|
const next = visibleNodes[currentIndex + 1];
|
|
823
|
-
if (next)
|
|
1133
|
+
if (next) {
|
|
1134
|
+
if (e.shiftKey) {
|
|
1135
|
+
handleSelectRange(next.id);
|
|
1136
|
+
} else {
|
|
1137
|
+
actions.selectNode(next.id);
|
|
1138
|
+
}
|
|
1139
|
+
}
|
|
824
1140
|
break;
|
|
825
1141
|
}
|
|
826
1142
|
case "ArrowUp": {
|
|
827
1143
|
e.preventDefault();
|
|
828
1144
|
const prev = visibleNodes[currentIndex - 1];
|
|
829
|
-
if (prev)
|
|
1145
|
+
if (prev) {
|
|
1146
|
+
if (e.shiftKey) {
|
|
1147
|
+
handleSelectRange(prev.id);
|
|
1148
|
+
} else {
|
|
1149
|
+
actions.selectNode(prev.id);
|
|
1150
|
+
}
|
|
1151
|
+
}
|
|
830
1152
|
break;
|
|
831
1153
|
}
|
|
832
1154
|
case "ArrowRight": {
|
|
@@ -853,17 +1175,47 @@ function TreeView({
|
|
|
853
1175
|
}
|
|
854
1176
|
break;
|
|
855
1177
|
}
|
|
1178
|
+
case "a": {
|
|
1179
|
+
if (e.metaKey || e.ctrlKey) {
|
|
1180
|
+
e.preventDefault();
|
|
1181
|
+
const ids = computeSelectAllIds(
|
|
1182
|
+
state.tree,
|
|
1183
|
+
state.focusedNodeId,
|
|
1184
|
+
state.selectedNodeIds
|
|
1185
|
+
);
|
|
1186
|
+
if (ids) {
|
|
1187
|
+
actions.setSelection(
|
|
1188
|
+
state.focusedNodeId,
|
|
1189
|
+
ids,
|
|
1190
|
+
state.focusedNodeId
|
|
1191
|
+
);
|
|
1192
|
+
}
|
|
1193
|
+
}
|
|
1194
|
+
break;
|
|
1195
|
+
}
|
|
1196
|
+
case "Escape": {
|
|
1197
|
+
e.preventDefault();
|
|
1198
|
+
if (state.selectedNodeIds.size > 1 && state.focusedNodeId) {
|
|
1199
|
+
actions.selectNode(state.focusedNodeId);
|
|
1200
|
+
} else {
|
|
1201
|
+
actions.setSelection(null, /* @__PURE__ */ new Set(), null);
|
|
1202
|
+
}
|
|
1203
|
+
break;
|
|
1204
|
+
}
|
|
856
1205
|
case "Delete":
|
|
857
1206
|
case "Backspace": {
|
|
858
1207
|
e.preventDefault();
|
|
859
|
-
const
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
1208
|
+
const { newTree, nextFocusId } = deleteSelectedNodes(
|
|
1209
|
+
state.tree,
|
|
1210
|
+
state.selectedNodeIds,
|
|
1211
|
+
visibleNodes
|
|
1212
|
+
);
|
|
1213
|
+
if (newTree === state.tree) break;
|
|
1214
|
+
actions.setTree(newTree);
|
|
1215
|
+
if (nextFocusId) {
|
|
1216
|
+
actions.selectNode(nextFocusId);
|
|
1217
|
+
} else {
|
|
1218
|
+
actions.setSelection(null, /* @__PURE__ */ new Set(), null);
|
|
867
1219
|
}
|
|
868
1220
|
break;
|
|
869
1221
|
}
|
|
@@ -871,23 +1223,25 @@ function TreeView({
|
|
|
871
1223
|
},
|
|
872
1224
|
[
|
|
873
1225
|
visibleNodes,
|
|
874
|
-
state.
|
|
1226
|
+
state.focusedNodeId,
|
|
1227
|
+
state.selectedNodeIds,
|
|
875
1228
|
state.expandedNodeIds,
|
|
876
1229
|
state.tree,
|
|
877
|
-
actions
|
|
1230
|
+
actions,
|
|
1231
|
+
handleSelectRange
|
|
878
1232
|
]
|
|
879
1233
|
);
|
|
880
1234
|
const [isFocused, setIsFocused] = (0, import_react5.useState)(false);
|
|
881
1235
|
(0, import_react5.useEffect)(() => {
|
|
882
|
-
if (state.
|
|
1236
|
+
if (state.focusedNodeId && containerRef.current) {
|
|
883
1237
|
const el = containerRef.current.querySelector(
|
|
884
|
-
`[data-node-id="${state.
|
|
1238
|
+
`[data-node-id="${state.focusedNodeId}"]`
|
|
885
1239
|
);
|
|
886
1240
|
if (el) {
|
|
887
1241
|
el.scrollIntoView({ block: "nearest" });
|
|
888
1242
|
}
|
|
889
1243
|
}
|
|
890
|
-
}, [state.
|
|
1244
|
+
}, [state.focusedNodeId]);
|
|
891
1245
|
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
|
|
892
1246
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
893
1247
|
"div",
|
|
@@ -917,6 +1271,7 @@ function TreeView({
|
|
|
917
1271
|
showValues,
|
|
918
1272
|
showCounts,
|
|
919
1273
|
isFocused,
|
|
1274
|
+
onSelectRange: handleSelectRange,
|
|
920
1275
|
onDragStart: handleDragStart,
|
|
921
1276
|
onDragOver: handleDragOver,
|
|
922
1277
|
onDragEnd: handleDragEnd,
|
|
@@ -940,7 +1295,7 @@ function TreeView({
|
|
|
940
1295
|
|
|
941
1296
|
// src/form-view.tsx
|
|
942
1297
|
var import_react8 = require("react");
|
|
943
|
-
var
|
|
1298
|
+
var import_core5 = require("@visual-json/core");
|
|
944
1299
|
|
|
945
1300
|
// src/breadcrumbs.tsx
|
|
946
1301
|
var import_react6 = require("react");
|
|
@@ -949,8 +1304,8 @@ var MAX_SUGGESTIONS = 20;
|
|
|
949
1304
|
var DROPDOWN_MAX_HEIGHT = 200;
|
|
950
1305
|
function Breadcrumbs({ className }) {
|
|
951
1306
|
const { state, actions } = useStudio();
|
|
952
|
-
const
|
|
953
|
-
const currentPath =
|
|
1307
|
+
const drillDownNode = state.drillDownNodeId ? state.tree.nodesById.get(state.drillDownNodeId) : null;
|
|
1308
|
+
const currentPath = drillDownNode?.path ?? "/";
|
|
954
1309
|
const [inputValue, setInputValue] = (0, import_react6.useState)(currentPath);
|
|
955
1310
|
const [open, setOpen] = (0, import_react6.useState)(false);
|
|
956
1311
|
const [highlightIndex, setHighlightIndex] = (0, import_react6.useState)(0);
|
|
@@ -980,7 +1335,7 @@ function Breadcrumbs({ className }) {
|
|
|
980
1335
|
(path) => {
|
|
981
1336
|
for (const [id, node] of state.tree.nodesById) {
|
|
982
1337
|
if (node.path === path) {
|
|
983
|
-
actions.
|
|
1338
|
+
actions.selectAndDrillDown(id);
|
|
984
1339
|
break;
|
|
985
1340
|
}
|
|
986
1341
|
}
|
|
@@ -1068,7 +1423,7 @@ function Breadcrumbs({ className }) {
|
|
|
1068
1423
|
style: {
|
|
1069
1424
|
width: "100%",
|
|
1070
1425
|
boxSizing: "border-box",
|
|
1071
|
-
padding: "
|
|
1426
|
+
padding: "3px 0",
|
|
1072
1427
|
fontSize: "var(--vj-input-font-size, 13px)",
|
|
1073
1428
|
fontFamily: "var(--vj-font, monospace)",
|
|
1074
1429
|
color: "var(--vj-text-muted, #999999)",
|
|
@@ -1295,9 +1650,9 @@ function EnumInput({
|
|
|
1295
1650
|
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
1296
1651
|
function getResolvedSchema(schema, rootSchema, path) {
|
|
1297
1652
|
if (!schema) return void 0;
|
|
1298
|
-
const raw = (0,
|
|
1653
|
+
const raw = (0, import_core5.getPropertySchema)(schema, path, rootSchema);
|
|
1299
1654
|
if (!raw) return void 0;
|
|
1300
|
-
return (0,
|
|
1655
|
+
return (0, import_core5.resolveRef)(raw, rootSchema ?? schema);
|
|
1301
1656
|
}
|
|
1302
1657
|
function getValueColor(node) {
|
|
1303
1658
|
if (node.type === "boolean" || node.type === "null")
|
|
@@ -1318,7 +1673,6 @@ function FormField({
|
|
|
1318
1673
|
depth,
|
|
1319
1674
|
showDescriptions,
|
|
1320
1675
|
showCounts,
|
|
1321
|
-
formSelectedNodeId,
|
|
1322
1676
|
editingNodeId,
|
|
1323
1677
|
collapsedIds,
|
|
1324
1678
|
maxKeyLength,
|
|
@@ -1336,26 +1690,26 @@ function FormField({
|
|
|
1336
1690
|
const { state, actions } = useStudio();
|
|
1337
1691
|
const isContainer = node.type === "object" || node.type === "array";
|
|
1338
1692
|
const collapsed = collapsedIds.has(node.id);
|
|
1339
|
-
const isSelected =
|
|
1693
|
+
const isSelected = state.selectedNodeIds.has(node.id);
|
|
1340
1694
|
const isEditing = editingNodeId === node.id;
|
|
1341
1695
|
const propSchema = getResolvedSchema(schema, rootSchema, node.path);
|
|
1342
1696
|
const isRequired = checkRequired(node, schema, rootSchema);
|
|
1343
1697
|
const [hovered, setHovered] = (0, import_react8.useState)(false);
|
|
1344
1698
|
const isRoot = node.parentId === null;
|
|
1345
1699
|
const isDragTarget = dragState.dropTargetNodeId === node.id;
|
|
1346
|
-
const isDraggedNode = dragState.
|
|
1700
|
+
const isDraggedNode = dragState.draggedNodeIds.has(node.id);
|
|
1347
1701
|
function handleDragOverEvent(e) {
|
|
1348
1702
|
e.preventDefault();
|
|
1349
1703
|
const rect = e.currentTarget.getBoundingClientRect();
|
|
1350
1704
|
const midY = rect.top + rect.height / 2;
|
|
1351
1705
|
onDragOver(node.id, e.clientY < midY ? "before" : "after");
|
|
1352
1706
|
}
|
|
1353
|
-
let
|
|
1354
|
-
let
|
|
1707
|
+
let borderTopColor = "transparent";
|
|
1708
|
+
let borderBottomColor = "transparent";
|
|
1355
1709
|
if (isDragTarget && dragState.dropPosition === "before") {
|
|
1356
|
-
|
|
1710
|
+
borderTopColor = "var(--vj-accent, #007acc)";
|
|
1357
1711
|
} else if (isDragTarget && dragState.dropPosition === "after") {
|
|
1358
|
-
|
|
1712
|
+
borderBottomColor = "var(--vj-accent, #007acc)";
|
|
1359
1713
|
}
|
|
1360
1714
|
const valueRef = (0, import_react8.useRef)(null);
|
|
1361
1715
|
const keyRef = (0, import_react8.useRef)(null);
|
|
@@ -1385,29 +1739,28 @@ function FormField({
|
|
|
1385
1739
|
} else {
|
|
1386
1740
|
parsed = newValue;
|
|
1387
1741
|
}
|
|
1388
|
-
const newTree = (0,
|
|
1742
|
+
const newTree = (0, import_core5.setValue)(state.tree, node.id, parsed);
|
|
1389
1743
|
actions.setTree(newTree);
|
|
1390
1744
|
},
|
|
1391
1745
|
[state.tree, node.id, node.type, actions, propSchema]
|
|
1392
1746
|
);
|
|
1393
1747
|
const handleKeyChange = (0, import_react8.useCallback)(
|
|
1394
1748
|
(newKey) => {
|
|
1395
|
-
const newTree = (0,
|
|
1749
|
+
const newTree = (0, import_core5.setKey)(state.tree, node.id, newKey);
|
|
1396
1750
|
actions.setTree(newTree);
|
|
1397
1751
|
},
|
|
1398
1752
|
[state.tree, node.id, actions]
|
|
1399
1753
|
);
|
|
1400
1754
|
const handleRemove = (0, import_react8.useCallback)(() => {
|
|
1401
|
-
const newTree = (0,
|
|
1755
|
+
const newTree = (0, import_core5.removeNode)(state.tree, node.id);
|
|
1402
1756
|
actions.setTree(newTree);
|
|
1403
1757
|
}, [state.tree, node.id, actions]);
|
|
1404
1758
|
const handleAddChild = (0, import_react8.useCallback)(() => {
|
|
1405
1759
|
const key = node.type === "array" ? String(node.children.length) : `newKey${node.children.length}`;
|
|
1406
|
-
const newTree = (0,
|
|
1760
|
+
const newTree = (0, import_core5.addProperty)(state.tree, node.id, key, "");
|
|
1407
1761
|
actions.setTree(newTree);
|
|
1408
1762
|
}, [state.tree, node.id, node.type, node.children.length, actions]);
|
|
1409
1763
|
const description = propSchema?.description;
|
|
1410
|
-
const defaultVal = propSchema?.default;
|
|
1411
1764
|
const isDeprecated = propSchema?.deprecated;
|
|
1412
1765
|
const fieldTitle = propSchema?.title;
|
|
1413
1766
|
const parentIsObject = node.parentId && state.tree.nodesById.get(node.parentId)?.type === "object";
|
|
@@ -1422,6 +1775,9 @@ function FormField({
|
|
|
1422
1775
|
draggable: !isRoot,
|
|
1423
1776
|
onDragStart: (e) => {
|
|
1424
1777
|
e.dataTransfer.effectAllowed = "move";
|
|
1778
|
+
if (state.selectedNodeIds.size > 1 && state.selectedNodeIds.has(node.id)) {
|
|
1779
|
+
setMultiDragImage(e, state.selectedNodeIds.size);
|
|
1780
|
+
}
|
|
1425
1781
|
onDragStart(node.id);
|
|
1426
1782
|
},
|
|
1427
1783
|
onDragOver: handleDragOverEvent,
|
|
@@ -1434,20 +1790,21 @@ function FormField({
|
|
|
1434
1790
|
display: "flex",
|
|
1435
1791
|
alignItems: "center",
|
|
1436
1792
|
gap: 6,
|
|
1437
|
-
padding: "
|
|
1793
|
+
padding: "1px 8px",
|
|
1438
1794
|
paddingLeft: 8 + depth * 16,
|
|
1439
1795
|
cursor: "pointer",
|
|
1440
1796
|
backgroundColor: rowBg,
|
|
1441
1797
|
color: rowColor,
|
|
1442
1798
|
height: 28,
|
|
1799
|
+
boxSizing: "border-box",
|
|
1443
1800
|
userSelect: "none",
|
|
1444
1801
|
opacity: isDeprecated ? 0.5 : isDraggedNode ? 0.4 : 1,
|
|
1445
|
-
borderTop
|
|
1446
|
-
borderBottom
|
|
1802
|
+
borderTop: `2px solid ${borderTopColor}`,
|
|
1803
|
+
borderBottom: `2px solid ${borderBottomColor}`
|
|
1447
1804
|
},
|
|
1448
1805
|
onClick: (e) => {
|
|
1449
1806
|
e.stopPropagation();
|
|
1450
|
-
onSelect(node.id);
|
|
1807
|
+
onSelect(node.id, e);
|
|
1451
1808
|
},
|
|
1452
1809
|
onDoubleClick: () => onToggleCollapse(node.id),
|
|
1453
1810
|
onMouseEnter: () => setHovered(true),
|
|
@@ -1503,7 +1860,7 @@ function FormField({
|
|
|
1503
1860
|
{
|
|
1504
1861
|
style: {
|
|
1505
1862
|
color: !isRoot && !parentIsObject && !isSelected ? "var(--vj-text-muted, #888888)" : "inherit",
|
|
1506
|
-
fontSize:
|
|
1863
|
+
fontSize: "var(--vj-input-font-size, 13px)",
|
|
1507
1864
|
fontFamily: "var(--vj-font, monospace)",
|
|
1508
1865
|
fontWeight: 500,
|
|
1509
1866
|
flexShrink: 0,
|
|
@@ -1604,7 +1961,6 @@ function FormField({
|
|
|
1604
1961
|
depth: depth + 1,
|
|
1605
1962
|
showDescriptions,
|
|
1606
1963
|
showCounts,
|
|
1607
|
-
formSelectedNodeId,
|
|
1608
1964
|
editingNodeId,
|
|
1609
1965
|
collapsedIds,
|
|
1610
1966
|
maxKeyLength,
|
|
@@ -1632,6 +1988,9 @@ function FormField({
|
|
|
1632
1988
|
draggable: !isRoot,
|
|
1633
1989
|
onDragStart: (e) => {
|
|
1634
1990
|
e.dataTransfer.effectAllowed = "move";
|
|
1991
|
+
if (state.selectedNodeIds.size > 1 && state.selectedNodeIds.has(node.id)) {
|
|
1992
|
+
setMultiDragImage(e, state.selectedNodeIds.size);
|
|
1993
|
+
}
|
|
1635
1994
|
onDragStart(node.id);
|
|
1636
1995
|
},
|
|
1637
1996
|
onDragOver: handleDragOverEvent,
|
|
@@ -1644,20 +2003,21 @@ function FormField({
|
|
|
1644
2003
|
display: "flex",
|
|
1645
2004
|
alignItems: "center",
|
|
1646
2005
|
gap: 6,
|
|
1647
|
-
padding: "
|
|
2006
|
+
padding: "1px 8px",
|
|
1648
2007
|
paddingLeft: 8 + depth * 16,
|
|
1649
2008
|
cursor: "pointer",
|
|
1650
2009
|
backgroundColor: rowBg,
|
|
1651
2010
|
color: rowColor,
|
|
1652
2011
|
height: 28,
|
|
2012
|
+
boxSizing: "border-box",
|
|
1653
2013
|
userSelect: "none",
|
|
1654
2014
|
opacity: isDeprecated ? 0.5 : isDraggedNode ? 0.4 : 1,
|
|
1655
|
-
borderTop
|
|
1656
|
-
borderBottom
|
|
2015
|
+
borderTop: `2px solid ${borderTopColor}`,
|
|
2016
|
+
borderBottom: `2px solid ${borderBottomColor}`
|
|
1657
2017
|
},
|
|
1658
2018
|
onClick: (e) => {
|
|
1659
2019
|
e.stopPropagation();
|
|
1660
|
-
onSelect(node.id);
|
|
2020
|
+
onSelect(node.id, e);
|
|
1661
2021
|
},
|
|
1662
2022
|
onDoubleClick: () => onStartEditing(node.id),
|
|
1663
2023
|
onMouseEnter: () => setHovered(true),
|
|
@@ -1694,7 +2054,7 @@ function FormField({
|
|
|
1694
2054
|
{
|
|
1695
2055
|
style: {
|
|
1696
2056
|
color: !parentIsObject && !isSelected ? "var(--vj-text-muted, #888888)" : "inherit",
|
|
1697
|
-
fontSize:
|
|
2057
|
+
fontSize: "var(--vj-input-font-size, 13px)",
|
|
1698
2058
|
fontFamily: "var(--vj-font, monospace)",
|
|
1699
2059
|
flexShrink: 0,
|
|
1700
2060
|
display: "inline-block",
|
|
@@ -1738,7 +2098,7 @@ function FormField({
|
|
|
1738
2098
|
{
|
|
1739
2099
|
style: {
|
|
1740
2100
|
color: valueColor,
|
|
1741
|
-
fontSize:
|
|
2101
|
+
fontSize: "var(--vj-input-font-size, 13px)",
|
|
1742
2102
|
fontFamily: "var(--vj-font, monospace)",
|
|
1743
2103
|
overflow: "hidden",
|
|
1744
2104
|
textOverflow: "ellipsis",
|
|
@@ -1816,7 +2176,7 @@ function renderEditInput(node, propSchema, displayValue, handleValueChange, inpu
|
|
|
1816
2176
|
style: {
|
|
1817
2177
|
color: "var(--vj-boolean, #569cd6)",
|
|
1818
2178
|
fontFamily: "var(--vj-font, monospace)",
|
|
1819
|
-
fontSize:
|
|
2179
|
+
fontSize: "var(--vj-input-font-size, 13px)",
|
|
1820
2180
|
fontStyle: "italic",
|
|
1821
2181
|
flex: 1
|
|
1822
2182
|
},
|
|
@@ -1849,11 +2209,8 @@ function FormView({
|
|
|
1849
2209
|
}) {
|
|
1850
2210
|
const { state, actions } = useStudio();
|
|
1851
2211
|
const rootSchema = state.schema ?? void 0;
|
|
1852
|
-
const
|
|
1853
|
-
const displayNode =
|
|
1854
|
-
const [formSelectedNodeId, setFormSelectedNodeId] = (0, import_react8.useState)(
|
|
1855
|
-
null
|
|
1856
|
-
);
|
|
2212
|
+
const drillDownNode = state.drillDownNodeId ? state.tree.nodesById.get(state.drillDownNodeId) : null;
|
|
2213
|
+
const displayNode = drillDownNode ?? state.tree.root;
|
|
1857
2214
|
const [editingNodeId, setEditingNodeId] = (0, import_react8.useState)(null);
|
|
1858
2215
|
const preEditTreeRef = (0, import_react8.useRef)(null);
|
|
1859
2216
|
const [collapsedIds, setCollapsedIds] = (0, import_react8.useState)(
|
|
@@ -1861,15 +2218,7 @@ function FormView({
|
|
|
1861
2218
|
);
|
|
1862
2219
|
const containerRef = (0, import_react8.useRef)(null);
|
|
1863
2220
|
const [isFocused, setIsFocused] = (0, import_react8.useState)(false);
|
|
1864
|
-
const {
|
|
1865
|
-
dragState,
|
|
1866
|
-
handleDragStart,
|
|
1867
|
-
handleDragOver,
|
|
1868
|
-
handleDragEnd,
|
|
1869
|
-
handleDrop
|
|
1870
|
-
} = useDragDrop();
|
|
1871
2221
|
(0, import_react8.useEffect)(() => {
|
|
1872
|
-
setFormSelectedNodeId(null);
|
|
1873
2222
|
setEditingNodeId(null);
|
|
1874
2223
|
setCollapsedIds(/* @__PURE__ */ new Set());
|
|
1875
2224
|
}, [displayNode.id]);
|
|
@@ -1877,6 +2226,17 @@ function FormView({
|
|
|
1877
2226
|
() => getVisibleNodes(displayNode, (id) => !collapsedIds.has(id)),
|
|
1878
2227
|
[displayNode, collapsedIds]
|
|
1879
2228
|
);
|
|
2229
|
+
const {
|
|
2230
|
+
dragState,
|
|
2231
|
+
handleDragStart,
|
|
2232
|
+
handleDragOver,
|
|
2233
|
+
handleDragEnd,
|
|
2234
|
+
handleDrop
|
|
2235
|
+
} = useDragDrop(visibleNodes, state.selectedNodeIds);
|
|
2236
|
+
(0, import_react8.useEffect)(() => {
|
|
2237
|
+
actions.setVisibleNodesOverride(visibleNodes);
|
|
2238
|
+
return () => actions.setVisibleNodesOverride(null);
|
|
2239
|
+
}, [visibleNodes, actions]);
|
|
1880
2240
|
const { maxKeyLength, maxDepth } = (0, import_react8.useMemo)(() => {
|
|
1881
2241
|
let maxKey = 1;
|
|
1882
2242
|
let maxD = 0;
|
|
@@ -1890,10 +2250,20 @@ function FormView({
|
|
|
1890
2250
|
}
|
|
1891
2251
|
return { maxKeyLength: maxKey, maxDepth: maxD };
|
|
1892
2252
|
}, [visibleNodes, displayNode.path, state.tree]);
|
|
1893
|
-
const handleSelect = (0, import_react8.useCallback)(
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
2253
|
+
const handleSelect = (0, import_react8.useCallback)(
|
|
2254
|
+
(nodeId, e) => {
|
|
2255
|
+
setEditingNodeId(null);
|
|
2256
|
+
if (e.shiftKey) {
|
|
2257
|
+
actions.setVisibleNodesOverride(visibleNodes);
|
|
2258
|
+
actions.selectNodeRange(nodeId);
|
|
2259
|
+
} else if (e.metaKey || e.ctrlKey) {
|
|
2260
|
+
actions.toggleNodeSelection(nodeId);
|
|
2261
|
+
} else {
|
|
2262
|
+
actions.selectNode(nodeId);
|
|
2263
|
+
}
|
|
2264
|
+
},
|
|
2265
|
+
[actions, visibleNodes]
|
|
2266
|
+
);
|
|
1897
2267
|
const handleToggleCollapse = (0, import_react8.useCallback)((nodeId) => {
|
|
1898
2268
|
setCollapsedIds((prev) => {
|
|
1899
2269
|
const next = new Set(prev);
|
|
@@ -1941,15 +2311,23 @@ function FormView({
|
|
|
1941
2311
|
}
|
|
1942
2312
|
return;
|
|
1943
2313
|
}
|
|
1944
|
-
|
|
1945
|
-
(n) => n.id ===
|
|
2314
|
+
let currentIndex = visibleNodes.findIndex(
|
|
2315
|
+
(n) => n.id === state.focusedNodeId
|
|
1946
2316
|
);
|
|
2317
|
+
if (currentIndex === -1 && visibleNodes.length > 0) {
|
|
2318
|
+
currentIndex = 0;
|
|
2319
|
+
}
|
|
1947
2320
|
switch (e.key) {
|
|
1948
2321
|
case "ArrowDown": {
|
|
1949
2322
|
e.preventDefault();
|
|
1950
2323
|
const next = visibleNodes[currentIndex + 1];
|
|
1951
2324
|
if (next) {
|
|
1952
|
-
|
|
2325
|
+
if (e.shiftKey) {
|
|
2326
|
+
actions.setVisibleNodesOverride(visibleNodes);
|
|
2327
|
+
actions.selectNodeRange(next.id);
|
|
2328
|
+
} else {
|
|
2329
|
+
actions.selectNode(next.id);
|
|
2330
|
+
}
|
|
1953
2331
|
scrollToNode(next.id);
|
|
1954
2332
|
}
|
|
1955
2333
|
break;
|
|
@@ -1958,7 +2336,12 @@ function FormView({
|
|
|
1958
2336
|
e.preventDefault();
|
|
1959
2337
|
const prev = visibleNodes[currentIndex - 1];
|
|
1960
2338
|
if (prev) {
|
|
1961
|
-
|
|
2339
|
+
if (e.shiftKey) {
|
|
2340
|
+
actions.setVisibleNodesOverride(visibleNodes);
|
|
2341
|
+
actions.selectNodeRange(prev.id);
|
|
2342
|
+
} else {
|
|
2343
|
+
actions.selectNode(prev.id);
|
|
2344
|
+
}
|
|
1962
2345
|
scrollToNode(prev.id);
|
|
1963
2346
|
}
|
|
1964
2347
|
break;
|
|
@@ -1974,7 +2357,7 @@ function FormView({
|
|
|
1974
2357
|
return next;
|
|
1975
2358
|
});
|
|
1976
2359
|
} else if (node.children.length > 0) {
|
|
1977
|
-
|
|
2360
|
+
actions.selectNode(node.children[0].id);
|
|
1978
2361
|
scrollToNode(node.children[0].id);
|
|
1979
2362
|
}
|
|
1980
2363
|
}
|
|
@@ -1996,7 +2379,7 @@ function FormView({
|
|
|
1996
2379
|
(n) => n.id === current.parentId
|
|
1997
2380
|
);
|
|
1998
2381
|
if (parentInVisible) {
|
|
1999
|
-
|
|
2382
|
+
actions.selectNode(parentInVisible.id);
|
|
2000
2383
|
scrollToNode(parentInVisible.id);
|
|
2001
2384
|
}
|
|
2002
2385
|
}
|
|
@@ -2004,30 +2387,54 @@ function FormView({
|
|
|
2004
2387
|
}
|
|
2005
2388
|
case "Enter": {
|
|
2006
2389
|
e.preventDefault();
|
|
2007
|
-
if (
|
|
2390
|
+
if (state.focusedNodeId) {
|
|
2008
2391
|
preEditTreeRef.current = state.tree;
|
|
2009
|
-
|
|
2392
|
+
actions.selectNode(state.focusedNodeId);
|
|
2393
|
+
setEditingNodeId(state.focusedNodeId);
|
|
2394
|
+
}
|
|
2395
|
+
break;
|
|
2396
|
+
}
|
|
2397
|
+
case "a": {
|
|
2398
|
+
if (e.metaKey || e.ctrlKey) {
|
|
2399
|
+
e.preventDefault();
|
|
2400
|
+
const ids = computeSelectAllIds(
|
|
2401
|
+
state.tree,
|
|
2402
|
+
state.focusedNodeId,
|
|
2403
|
+
state.selectedNodeIds
|
|
2404
|
+
);
|
|
2405
|
+
if (ids) {
|
|
2406
|
+
actions.setSelection(
|
|
2407
|
+
state.focusedNodeId,
|
|
2408
|
+
ids,
|
|
2409
|
+
state.focusedNodeId
|
|
2410
|
+
);
|
|
2411
|
+
}
|
|
2010
2412
|
}
|
|
2011
2413
|
break;
|
|
2012
2414
|
}
|
|
2013
2415
|
case "Escape": {
|
|
2014
2416
|
e.preventDefault();
|
|
2015
|
-
|
|
2417
|
+
if (state.selectedNodeIds.size > 1 && state.focusedNodeId) {
|
|
2418
|
+
actions.selectNode(state.focusedNodeId);
|
|
2419
|
+
} else {
|
|
2420
|
+
actions.setSelection(null, /* @__PURE__ */ new Set(), null);
|
|
2421
|
+
}
|
|
2016
2422
|
break;
|
|
2017
2423
|
}
|
|
2018
2424
|
case "Delete":
|
|
2019
2425
|
case "Backspace": {
|
|
2020
2426
|
e.preventDefault();
|
|
2021
|
-
const
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2427
|
+
const { newTree, nextFocusId } = deleteSelectedNodes(
|
|
2428
|
+
state.tree,
|
|
2429
|
+
state.selectedNodeIds,
|
|
2430
|
+
visibleNodes
|
|
2431
|
+
);
|
|
2432
|
+
if (newTree === state.tree) break;
|
|
2433
|
+
actions.setTree(newTree);
|
|
2434
|
+
if (nextFocusId) {
|
|
2435
|
+
actions.selectNode(nextFocusId);
|
|
2436
|
+
} else {
|
|
2437
|
+
actions.setSelection(null, /* @__PURE__ */ new Set(), null);
|
|
2031
2438
|
}
|
|
2032
2439
|
break;
|
|
2033
2440
|
}
|
|
@@ -2035,7 +2442,8 @@ function FormView({
|
|
|
2035
2442
|
},
|
|
2036
2443
|
[
|
|
2037
2444
|
visibleNodes,
|
|
2038
|
-
|
|
2445
|
+
state.focusedNodeId,
|
|
2446
|
+
state.selectedNodeIds,
|
|
2039
2447
|
editingNodeId,
|
|
2040
2448
|
collapsedIds,
|
|
2041
2449
|
scrollToNode,
|
|
@@ -2060,9 +2468,11 @@ function FormView({
|
|
|
2060
2468
|
"div",
|
|
2061
2469
|
{
|
|
2062
2470
|
style: {
|
|
2471
|
+
display: "flex",
|
|
2472
|
+
alignItems: "center",
|
|
2063
2473
|
padding: "4px 8px",
|
|
2064
2474
|
borderBottom: "1px solid var(--vj-border, #333333)",
|
|
2065
|
-
backgroundColor: "var(--vj-bg
|
|
2475
|
+
backgroundColor: "var(--vj-bg, #1e1e1e)",
|
|
2066
2476
|
flexShrink: 0
|
|
2067
2477
|
},
|
|
2068
2478
|
children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Breadcrumbs, {})
|
|
@@ -2095,7 +2505,6 @@ function FormView({
|
|
|
2095
2505
|
depth: 0,
|
|
2096
2506
|
showDescriptions,
|
|
2097
2507
|
showCounts,
|
|
2098
|
-
formSelectedNodeId,
|
|
2099
2508
|
editingNodeId,
|
|
2100
2509
|
collapsedIds,
|
|
2101
2510
|
maxKeyLength,
|
|
@@ -2165,7 +2574,7 @@ function SearchBar({ className }) {
|
|
|
2165
2574
|
alignItems: "center",
|
|
2166
2575
|
gap: 6,
|
|
2167
2576
|
padding: "4px 8px",
|
|
2168
|
-
backgroundColor: "var(--vj-bg
|
|
2577
|
+
backgroundColor: "var(--vj-bg, #1e1e1e)",
|
|
2169
2578
|
borderBottom: "1px solid var(--vj-border, #333333)"
|
|
2170
2579
|
},
|
|
2171
2580
|
children: [
|
|
@@ -2372,31 +2781,6 @@ function SearchBar({ className }) {
|
|
|
2372
2781
|
|
|
2373
2782
|
// src/json-editor.tsx
|
|
2374
2783
|
var import_jsx_runtime8 = require("react/jsx-runtime");
|
|
2375
|
-
var DEFAULT_CSS_VARS = {
|
|
2376
|
-
"--vj-bg": "#1e1e1e",
|
|
2377
|
-
"--vj-bg-panel": "#252526",
|
|
2378
|
-
"--vj-bg-hover": "#2a2d2e",
|
|
2379
|
-
"--vj-bg-selected": "#2a5a1e",
|
|
2380
|
-
"--vj-bg-selected-muted": "#2a2d2e",
|
|
2381
|
-
"--vj-bg-match": "#3a3520",
|
|
2382
|
-
"--vj-bg-match-active": "#51502b",
|
|
2383
|
-
"--vj-border": "#333333",
|
|
2384
|
-
"--vj-border-subtle": "#2a2a2a",
|
|
2385
|
-
"--vj-text": "#cccccc",
|
|
2386
|
-
"--vj-text-muted": "#888888",
|
|
2387
|
-
"--vj-text-dim": "#666666",
|
|
2388
|
-
"--vj-text-dimmer": "#555555",
|
|
2389
|
-
"--vj-string": "#ce9178",
|
|
2390
|
-
"--vj-number": "#b5cea8",
|
|
2391
|
-
"--vj-boolean": "#569cd6",
|
|
2392
|
-
"--vj-accent": "#007acc",
|
|
2393
|
-
"--vj-accent-muted": "#094771",
|
|
2394
|
-
"--vj-input-bg": "#3c3c3c",
|
|
2395
|
-
"--vj-input-border": "#555555",
|
|
2396
|
-
"--vj-error": "#f48771",
|
|
2397
|
-
"--vj-font": "monospace",
|
|
2398
|
-
"--vj-input-font-size": "13px"
|
|
2399
|
-
};
|
|
2400
2784
|
function JsonEditor({
|
|
2401
2785
|
value,
|
|
2402
2786
|
defaultValue,
|
|
@@ -2723,430 +3107,10 @@ function EditorLayout({
|
|
|
2723
3107
|
] });
|
|
2724
3108
|
}
|
|
2725
3109
|
|
|
2726
|
-
// src/property-editor.tsx
|
|
2727
|
-
var import_react11 = require("react");
|
|
2728
|
-
var import_core5 = require("@visual-json/core");
|
|
2729
|
-
var import_jsx_runtime9 = require("react/jsx-runtime");
|
|
2730
|
-
var ALL_TYPES = [
|
|
2731
|
-
"string",
|
|
2732
|
-
"number",
|
|
2733
|
-
"boolean",
|
|
2734
|
-
"null",
|
|
2735
|
-
"object",
|
|
2736
|
-
"array"
|
|
2737
|
-
];
|
|
2738
|
-
function PropertyRow({ node, schemaProperty }) {
|
|
2739
|
-
const { state, actions } = useStudio();
|
|
2740
|
-
const isContainer = node.type === "object" || node.type === "array";
|
|
2741
|
-
const [hoveredRow, setHoveredRow] = (0, import_react11.useState)(false);
|
|
2742
|
-
const enumRef = (0, import_react11.useRef)(null);
|
|
2743
|
-
function handleValueChange(newValue) {
|
|
2744
|
-
let parsed;
|
|
2745
|
-
if (newValue === "null") parsed = null;
|
|
2746
|
-
else if (newValue === "true") parsed = true;
|
|
2747
|
-
else if (newValue === "false") parsed = false;
|
|
2748
|
-
else if ((node.type === "number" || schemaProperty?.type === "number" || schemaProperty?.type === "integer") && !isNaN(Number(newValue)) && newValue.trim() !== "")
|
|
2749
|
-
parsed = Number(newValue);
|
|
2750
|
-
else parsed = newValue;
|
|
2751
|
-
const newTree = (0, import_core5.setValue)(state.tree, node.id, parsed);
|
|
2752
|
-
actions.setTree(newTree);
|
|
2753
|
-
}
|
|
2754
|
-
function handleKeyChange(newKey) {
|
|
2755
|
-
const newTree = (0, import_core5.setKey)(state.tree, node.id, newKey);
|
|
2756
|
-
actions.setTree(newTree);
|
|
2757
|
-
}
|
|
2758
|
-
function handleRemove() {
|
|
2759
|
-
const newTree = (0, import_core5.removeNode)(state.tree, node.id);
|
|
2760
|
-
actions.setTree(newTree);
|
|
2761
|
-
}
|
|
2762
|
-
function handleAddChild() {
|
|
2763
|
-
const key = node.type === "array" ? String(node.children.length) : `key${node.children.length}`;
|
|
2764
|
-
const newTree = (0, import_core5.addProperty)(state.tree, node.id, key, "");
|
|
2765
|
-
actions.setTree(newTree);
|
|
2766
|
-
}
|
|
2767
|
-
function displayValue() {
|
|
2768
|
-
if (isContainer) {
|
|
2769
|
-
return node.type === "array" ? `[${node.children.length} items]` : `{${node.children.length} keys}`;
|
|
2770
|
-
}
|
|
2771
|
-
if (node.value === null) return "";
|
|
2772
|
-
if (node.value === void 0) return "";
|
|
2773
|
-
if (typeof node.value === "string" && node.value === "") return "";
|
|
2774
|
-
return String(node.value);
|
|
2775
|
-
}
|
|
2776
|
-
const hasEnumValues = schemaProperty?.enum && schemaProperty.enum.length > 0;
|
|
2777
|
-
const description = schemaProperty?.description;
|
|
2778
|
-
return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
|
|
2779
|
-
"div",
|
|
2780
|
-
{
|
|
2781
|
-
onMouseEnter: () => setHoveredRow(true),
|
|
2782
|
-
onMouseLeave: () => setHoveredRow(false),
|
|
2783
|
-
children: [
|
|
2784
|
-
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
|
|
2785
|
-
"div",
|
|
2786
|
-
{
|
|
2787
|
-
style: {
|
|
2788
|
-
display: "flex",
|
|
2789
|
-
alignItems: "center",
|
|
2790
|
-
gap: 8,
|
|
2791
|
-
padding: "4px 12px",
|
|
2792
|
-
borderBottom: "1px solid var(--vj-border-subtle, #2a2a2a)",
|
|
2793
|
-
minHeight: 32,
|
|
2794
|
-
backgroundColor: hoveredRow ? "var(--vj-bg-hover, #2a2d2e)" : "transparent"
|
|
2795
|
-
},
|
|
2796
|
-
children: [
|
|
2797
|
-
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
2798
|
-
"input",
|
|
2799
|
-
{
|
|
2800
|
-
value: node.key,
|
|
2801
|
-
onChange: (e) => handleKeyChange(e.target.value),
|
|
2802
|
-
style: {
|
|
2803
|
-
background: "none",
|
|
2804
|
-
border: "1px solid transparent",
|
|
2805
|
-
borderRadius: 3,
|
|
2806
|
-
color: "var(--vj-text, #cccccc)",
|
|
2807
|
-
fontFamily: "var(--vj-font, monospace)",
|
|
2808
|
-
fontSize: "var(--vj-input-font-size, 13px)",
|
|
2809
|
-
padding: "2px 6px",
|
|
2810
|
-
width: 140,
|
|
2811
|
-
flexShrink: 0
|
|
2812
|
-
}
|
|
2813
|
-
}
|
|
2814
|
-
),
|
|
2815
|
-
!isContainer ? hasEnumValues ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
2816
|
-
EnumInput,
|
|
2817
|
-
{
|
|
2818
|
-
enumValues: schemaProperty.enum,
|
|
2819
|
-
value: displayValue(),
|
|
2820
|
-
onValueChange: handleValueChange,
|
|
2821
|
-
inputRef: enumRef,
|
|
2822
|
-
inputStyle: {
|
|
2823
|
-
background: "none",
|
|
2824
|
-
border: "1px solid transparent",
|
|
2825
|
-
borderRadius: 3,
|
|
2826
|
-
color: node.type === "string" ? "var(--vj-string, #ce9178)" : "var(--vj-number, #b5cea8)",
|
|
2827
|
-
fontFamily: "var(--vj-font, monospace)",
|
|
2828
|
-
fontSize: "var(--vj-input-font-size, 13px)",
|
|
2829
|
-
padding: "2px 6px",
|
|
2830
|
-
flex: 1,
|
|
2831
|
-
outline: "none"
|
|
2832
|
-
}
|
|
2833
|
-
}
|
|
2834
|
-
) : /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
2835
|
-
"input",
|
|
2836
|
-
{
|
|
2837
|
-
value: displayValue(),
|
|
2838
|
-
onChange: (e) => handleValueChange(e.target.value),
|
|
2839
|
-
placeholder: "<value>",
|
|
2840
|
-
style: {
|
|
2841
|
-
background: "none",
|
|
2842
|
-
border: "1px solid transparent",
|
|
2843
|
-
borderRadius: 3,
|
|
2844
|
-
color: node.type === "string" ? "var(--vj-string, #ce9178)" : "var(--vj-number, #b5cea8)",
|
|
2845
|
-
fontFamily: "var(--vj-font, monospace)",
|
|
2846
|
-
fontSize: "var(--vj-input-font-size, 13px)",
|
|
2847
|
-
padding: "2px 6px",
|
|
2848
|
-
flex: 1,
|
|
2849
|
-
textAlign: "right"
|
|
2850
|
-
}
|
|
2851
|
-
}
|
|
2852
|
-
) : /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
2853
|
-
"span",
|
|
2854
|
-
{
|
|
2855
|
-
style: {
|
|
2856
|
-
color: "var(--vj-text-dim, #666666)",
|
|
2857
|
-
fontFamily: "var(--vj-font, monospace)",
|
|
2858
|
-
fontSize: 13,
|
|
2859
|
-
flex: 1,
|
|
2860
|
-
textAlign: "right"
|
|
2861
|
-
},
|
|
2862
|
-
children: displayValue()
|
|
2863
|
-
}
|
|
2864
|
-
),
|
|
2865
|
-
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
|
|
2866
|
-
"div",
|
|
2867
|
-
{
|
|
2868
|
-
style: {
|
|
2869
|
-
display: "flex",
|
|
2870
|
-
gap: 2,
|
|
2871
|
-
opacity: hoveredRow ? 1 : 0,
|
|
2872
|
-
transition: "opacity 0.1s",
|
|
2873
|
-
flexShrink: 0
|
|
2874
|
-
},
|
|
2875
|
-
children: [
|
|
2876
|
-
isContainer && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
2877
|
-
"button",
|
|
2878
|
-
{
|
|
2879
|
-
onClick: handleAddChild,
|
|
2880
|
-
style: {
|
|
2881
|
-
background: "none",
|
|
2882
|
-
border: "none",
|
|
2883
|
-
color: "var(--vj-text-muted, #888888)",
|
|
2884
|
-
cursor: "pointer",
|
|
2885
|
-
padding: "2px 4px",
|
|
2886
|
-
fontSize: 15,
|
|
2887
|
-
fontFamily: "var(--vj-font, monospace)",
|
|
2888
|
-
borderRadius: 3,
|
|
2889
|
-
lineHeight: 1
|
|
2890
|
-
},
|
|
2891
|
-
title: "Add child",
|
|
2892
|
-
children: "+"
|
|
2893
|
-
}
|
|
2894
|
-
),
|
|
2895
|
-
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
2896
|
-
"button",
|
|
2897
|
-
{
|
|
2898
|
-
onClick: handleRemove,
|
|
2899
|
-
style: {
|
|
2900
|
-
background: "none",
|
|
2901
|
-
border: "none",
|
|
2902
|
-
color: "var(--vj-text-muted, #888888)",
|
|
2903
|
-
cursor: "pointer",
|
|
2904
|
-
padding: "2px 4px",
|
|
2905
|
-
fontSize: 15,
|
|
2906
|
-
fontFamily: "var(--vj-font, monospace)",
|
|
2907
|
-
borderRadius: 3,
|
|
2908
|
-
lineHeight: 1
|
|
2909
|
-
},
|
|
2910
|
-
title: "Remove",
|
|
2911
|
-
children: "\xD7"
|
|
2912
|
-
}
|
|
2913
|
-
)
|
|
2914
|
-
]
|
|
2915
|
-
}
|
|
2916
|
-
)
|
|
2917
|
-
]
|
|
2918
|
-
}
|
|
2919
|
-
),
|
|
2920
|
-
description && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
2921
|
-
"div",
|
|
2922
|
-
{
|
|
2923
|
-
style: {
|
|
2924
|
-
padding: "2px 12px 4px 44px",
|
|
2925
|
-
fontSize: 11,
|
|
2926
|
-
color: "var(--vj-text-dim, #666666)",
|
|
2927
|
-
fontFamily: "var(--vj-font, monospace)",
|
|
2928
|
-
borderBottom: "1px solid var(--vj-border-subtle, #2a2a2a)"
|
|
2929
|
-
},
|
|
2930
|
-
children: description
|
|
2931
|
-
}
|
|
2932
|
-
)
|
|
2933
|
-
]
|
|
2934
|
-
}
|
|
2935
|
-
);
|
|
2936
|
-
}
|
|
2937
|
-
function PropertyEditor({ className }) {
|
|
2938
|
-
const { state, actions } = useStudio();
|
|
2939
|
-
const selectedNode = state.selectedNodeId ? state.tree.nodesById.get(state.selectedNodeId) : null;
|
|
2940
|
-
const handleChangeType = (0, import_react11.useCallback)(
|
|
2941
|
-
(newType) => {
|
|
2942
|
-
if (!selectedNode) return;
|
|
2943
|
-
const newTree = (0, import_core5.changeType)(state.tree, selectedNode.id, newType);
|
|
2944
|
-
actions.setTree(newTree);
|
|
2945
|
-
},
|
|
2946
|
-
[state.tree, selectedNode, actions]
|
|
2947
|
-
);
|
|
2948
|
-
const handleDuplicate = (0, import_react11.useCallback)(() => {
|
|
2949
|
-
if (!selectedNode) return;
|
|
2950
|
-
const newTree = (0, import_core5.duplicateNode)(state.tree, selectedNode.id);
|
|
2951
|
-
actions.setTree(newTree);
|
|
2952
|
-
}, [state.tree, selectedNode, actions]);
|
|
2953
|
-
const handleCopyPath = (0, import_react11.useCallback)(() => {
|
|
2954
|
-
if (!selectedNode) return;
|
|
2955
|
-
navigator.clipboard.writeText(selectedNode.path).catch(() => {
|
|
2956
|
-
});
|
|
2957
|
-
}, [selectedNode]);
|
|
2958
|
-
const handleCopyValue = (0, import_react11.useCallback)(() => {
|
|
2959
|
-
if (!selectedNode) return;
|
|
2960
|
-
const value = (0, import_core5.toJson)(selectedNode);
|
|
2961
|
-
const text = typeof value === "string" ? value : JSON.stringify(value, null, 2);
|
|
2962
|
-
navigator.clipboard.writeText(text).catch(() => {
|
|
2963
|
-
});
|
|
2964
|
-
}, [selectedNode]);
|
|
2965
|
-
if (!selectedNode) {
|
|
2966
|
-
return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
2967
|
-
"div",
|
|
2968
|
-
{
|
|
2969
|
-
className,
|
|
2970
|
-
style: {
|
|
2971
|
-
display: "flex",
|
|
2972
|
-
alignItems: "center",
|
|
2973
|
-
justifyContent: "center",
|
|
2974
|
-
backgroundColor: "var(--vj-bg, #1e1e1e)",
|
|
2975
|
-
color: "var(--vj-text-dimmer, #555555)",
|
|
2976
|
-
fontFamily: "var(--vj-font, monospace)",
|
|
2977
|
-
fontSize: 13,
|
|
2978
|
-
height: "100%"
|
|
2979
|
-
},
|
|
2980
|
-
children: "Select a node to edit"
|
|
2981
|
-
}
|
|
2982
|
-
);
|
|
2983
|
-
}
|
|
2984
|
-
const isContainer = selectedNode.type === "object" || selectedNode.type === "array";
|
|
2985
|
-
const schema = state.schema ?? void 0;
|
|
2986
|
-
const schemaTitle = schema?.title;
|
|
2987
|
-
function getChildSchema(childKey) {
|
|
2988
|
-
if (!schema || !selectedNode) return void 0;
|
|
2989
|
-
const childPath = selectedNode.path === "/" ? `/${childKey}` : `${selectedNode.path}/${childKey}`;
|
|
2990
|
-
const raw = (0, import_core5.getPropertySchema)(schema, childPath);
|
|
2991
|
-
return raw ? (0, import_core5.resolveRef)(raw, schema) : void 0;
|
|
2992
|
-
}
|
|
2993
|
-
function handleAdd() {
|
|
2994
|
-
if (!selectedNode) return;
|
|
2995
|
-
const key = selectedNode.type === "array" ? String(selectedNode.children.length) : `key${selectedNode.children.length}`;
|
|
2996
|
-
const newTree = (0, import_core5.addProperty)(state.tree, selectedNode.id, key, "");
|
|
2997
|
-
actions.setTree(newTree);
|
|
2998
|
-
}
|
|
2999
|
-
return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
|
|
3000
|
-
"div",
|
|
3001
|
-
{
|
|
3002
|
-
className,
|
|
3003
|
-
style: {
|
|
3004
|
-
backgroundColor: "var(--vj-bg, #1e1e1e)",
|
|
3005
|
-
color: "var(--vj-text, #cccccc)",
|
|
3006
|
-
overflow: "auto",
|
|
3007
|
-
height: "100%",
|
|
3008
|
-
display: "flex",
|
|
3009
|
-
flexDirection: "column"
|
|
3010
|
-
},
|
|
3011
|
-
children: [
|
|
3012
|
-
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
|
|
3013
|
-
"div",
|
|
3014
|
-
{
|
|
3015
|
-
style: {
|
|
3016
|
-
display: "flex",
|
|
3017
|
-
alignItems: "center",
|
|
3018
|
-
justifyContent: "space-between",
|
|
3019
|
-
padding: "6px 12px",
|
|
3020
|
-
borderBottom: "1px solid var(--vj-border, #333333)",
|
|
3021
|
-
fontSize: 12,
|
|
3022
|
-
color: "var(--vj-text-muted, #999999)",
|
|
3023
|
-
fontFamily: "var(--vj-font, monospace)",
|
|
3024
|
-
flexShrink: 0,
|
|
3025
|
-
backgroundColor: "var(--vj-bg-panel, #252526)"
|
|
3026
|
-
},
|
|
3027
|
-
children: [
|
|
3028
|
-
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
|
|
3029
|
-
"div",
|
|
3030
|
-
{
|
|
3031
|
-
style: {
|
|
3032
|
-
display: "flex",
|
|
3033
|
-
flexDirection: "column",
|
|
3034
|
-
gap: 2,
|
|
3035
|
-
flex: 1,
|
|
3036
|
-
minWidth: 0
|
|
3037
|
-
},
|
|
3038
|
-
children: [
|
|
3039
|
-
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Breadcrumbs, {}),
|
|
3040
|
-
schemaTitle && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
3041
|
-
"span",
|
|
3042
|
-
{
|
|
3043
|
-
style: { fontSize: 10, color: "var(--vj-text-dim, #666666)" },
|
|
3044
|
-
children: schemaTitle
|
|
3045
|
-
}
|
|
3046
|
-
)
|
|
3047
|
-
]
|
|
3048
|
-
}
|
|
3049
|
-
),
|
|
3050
|
-
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
|
|
3051
|
-
"div",
|
|
3052
|
-
{
|
|
3053
|
-
style: {
|
|
3054
|
-
display: "flex",
|
|
3055
|
-
alignItems: "center",
|
|
3056
|
-
gap: 4,
|
|
3057
|
-
flexShrink: 0
|
|
3058
|
-
},
|
|
3059
|
-
children: [
|
|
3060
|
-
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
3061
|
-
"select",
|
|
3062
|
-
{
|
|
3063
|
-
value: selectedNode.type,
|
|
3064
|
-
onChange: (e) => handleChangeType(e.target.value),
|
|
3065
|
-
style: {
|
|
3066
|
-
background: "var(--vj-input-bg, #3c3c3c)",
|
|
3067
|
-
border: "1px solid var(--vj-input-border, #555555)",
|
|
3068
|
-
borderRadius: 3,
|
|
3069
|
-
color: "var(--vj-text, #cccccc)",
|
|
3070
|
-
fontSize: "var(--vj-input-font-size, 13px)",
|
|
3071
|
-
fontFamily: "var(--vj-font, monospace)",
|
|
3072
|
-
padding: "1px 4px",
|
|
3073
|
-
cursor: "pointer"
|
|
3074
|
-
},
|
|
3075
|
-
title: "Change type",
|
|
3076
|
-
children: ALL_TYPES.map((t) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("option", { value: t, children: t }, t))
|
|
3077
|
-
}
|
|
3078
|
-
),
|
|
3079
|
-
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
3080
|
-
"button",
|
|
3081
|
-
{
|
|
3082
|
-
onClick: handleCopyPath,
|
|
3083
|
-
style: actionButtonStyle,
|
|
3084
|
-
title: "Copy path",
|
|
3085
|
-
children: "path"
|
|
3086
|
-
}
|
|
3087
|
-
),
|
|
3088
|
-
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
3089
|
-
"button",
|
|
3090
|
-
{
|
|
3091
|
-
onClick: handleCopyValue,
|
|
3092
|
-
style: actionButtonStyle,
|
|
3093
|
-
title: "Copy value",
|
|
3094
|
-
children: "value"
|
|
3095
|
-
}
|
|
3096
|
-
),
|
|
3097
|
-
selectedNode.parentId && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
3098
|
-
"button",
|
|
3099
|
-
{
|
|
3100
|
-
onClick: handleDuplicate,
|
|
3101
|
-
style: actionButtonStyle,
|
|
3102
|
-
title: "Duplicate",
|
|
3103
|
-
children: "dup"
|
|
3104
|
-
}
|
|
3105
|
-
),
|
|
3106
|
-
isContainer && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
3107
|
-
"button",
|
|
3108
|
-
{
|
|
3109
|
-
onClick: handleAdd,
|
|
3110
|
-
style: {
|
|
3111
|
-
...actionButtonStyle,
|
|
3112
|
-
border: "1px solid var(--vj-input-border, #555555)"
|
|
3113
|
-
},
|
|
3114
|
-
children: "+ Add"
|
|
3115
|
-
}
|
|
3116
|
-
)
|
|
3117
|
-
]
|
|
3118
|
-
}
|
|
3119
|
-
)
|
|
3120
|
-
]
|
|
3121
|
-
}
|
|
3122
|
-
),
|
|
3123
|
-
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { style: { flex: 1, overflow: "auto" }, children: isContainer ? selectedNode.children.map((child) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
3124
|
-
PropertyRow,
|
|
3125
|
-
{
|
|
3126
|
-
node: child,
|
|
3127
|
-
schemaProperty: getChildSchema(child.key)
|
|
3128
|
-
},
|
|
3129
|
-
child.id
|
|
3130
|
-
)) : /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(PropertyRow, { node: selectedNode }) })
|
|
3131
|
-
]
|
|
3132
|
-
}
|
|
3133
|
-
);
|
|
3134
|
-
}
|
|
3135
|
-
var actionButtonStyle = {
|
|
3136
|
-
background: "none",
|
|
3137
|
-
border: "none",
|
|
3138
|
-
borderRadius: 3,
|
|
3139
|
-
color: "var(--vj-text-muted, #888888)",
|
|
3140
|
-
cursor: "pointer",
|
|
3141
|
-
padding: "1px 6px",
|
|
3142
|
-
fontSize: 11,
|
|
3143
|
-
fontFamily: "var(--vj-font, monospace)"
|
|
3144
|
-
};
|
|
3145
|
-
|
|
3146
3110
|
// src/diff-view.tsx
|
|
3147
|
-
var
|
|
3111
|
+
var import_react11 = require("react");
|
|
3148
3112
|
var import_core6 = require("@visual-json/core");
|
|
3149
|
-
var
|
|
3113
|
+
var import_jsx_runtime9 = require("react/jsx-runtime");
|
|
3150
3114
|
var DIFF_COLORS = {
|
|
3151
3115
|
added: { bg: "#1e3a1e", marker: "+", label: "#4ec94e" },
|
|
3152
3116
|
removed: { bg: "#3a1e1e", marker: "-", label: "#f48771" },
|
|
@@ -3167,7 +3131,7 @@ function formatValue(value) {
|
|
|
3167
3131
|
}
|
|
3168
3132
|
function DiffRow({ entry }) {
|
|
3169
3133
|
const colors = DIFF_COLORS[entry.type];
|
|
3170
|
-
return /* @__PURE__ */ (0,
|
|
3134
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
|
|
3171
3135
|
"div",
|
|
3172
3136
|
{
|
|
3173
3137
|
style: {
|
|
@@ -3181,7 +3145,7 @@ function DiffRow({ entry }) {
|
|
|
3181
3145
|
gap: 8
|
|
3182
3146
|
},
|
|
3183
3147
|
children: [
|
|
3184
|
-
/* @__PURE__ */ (0,
|
|
3148
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
3185
3149
|
"span",
|
|
3186
3150
|
{
|
|
3187
3151
|
style: {
|
|
@@ -3194,7 +3158,7 @@ function DiffRow({ entry }) {
|
|
|
3194
3158
|
children: colors.marker
|
|
3195
3159
|
}
|
|
3196
3160
|
),
|
|
3197
|
-
/* @__PURE__ */ (0,
|
|
3161
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
3198
3162
|
"span",
|
|
3199
3163
|
{
|
|
3200
3164
|
style: {
|
|
@@ -3205,14 +3169,14 @@ function DiffRow({ entry }) {
|
|
|
3205
3169
|
children: entry.path
|
|
3206
3170
|
}
|
|
3207
3171
|
),
|
|
3208
|
-
/* @__PURE__ */ (0,
|
|
3209
|
-
entry.type === "changed" && /* @__PURE__ */ (0,
|
|
3210
|
-
/* @__PURE__ */ (0,
|
|
3211
|
-
/* @__PURE__ */ (0,
|
|
3212
|
-
/* @__PURE__ */ (0,
|
|
3172
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("span", { style: { flex: 1, display: "flex", gap: 8, overflow: "hidden" }, children: [
|
|
3173
|
+
entry.type === "changed" && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_jsx_runtime9.Fragment, { children: [
|
|
3174
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { style: { color: "#f48771", textDecoration: "line-through" }, children: formatValue(entry.oldValue) }),
|
|
3175
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { style: { color: "var(--vj-text-dim, #666666)" }, children: "\u2192" }),
|
|
3176
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { style: { color: "#4ec94e" }, children: formatValue(entry.newValue) })
|
|
3213
3177
|
] }),
|
|
3214
|
-
entry.type === "added" && /* @__PURE__ */ (0,
|
|
3215
|
-
entry.type === "removed" && /* @__PURE__ */ (0,
|
|
3178
|
+
entry.type === "added" && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { style: { color: "#4ec94e" }, children: formatValue(entry.newValue) }),
|
|
3179
|
+
entry.type === "removed" && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { style: { color: "#f48771", textDecoration: "line-through" }, children: formatValue(entry.oldValue) })
|
|
3216
3180
|
] })
|
|
3217
3181
|
]
|
|
3218
3182
|
}
|
|
@@ -3223,14 +3187,14 @@ function DiffView({
|
|
|
3223
3187
|
currentJson,
|
|
3224
3188
|
className
|
|
3225
3189
|
}) {
|
|
3226
|
-
const entries = (0,
|
|
3190
|
+
const entries = (0, import_react11.useMemo)(
|
|
3227
3191
|
() => (0, import_core6.computeDiff)(originalJson, currentJson),
|
|
3228
3192
|
[originalJson, currentJson]
|
|
3229
3193
|
);
|
|
3230
3194
|
const added = entries.filter((e) => e.type === "added").length;
|
|
3231
3195
|
const removed = entries.filter((e) => e.type === "removed").length;
|
|
3232
3196
|
const changed = entries.filter((e) => e.type === "changed").length;
|
|
3233
|
-
return /* @__PURE__ */ (0,
|
|
3197
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
|
|
3234
3198
|
"div",
|
|
3235
3199
|
{
|
|
3236
3200
|
className,
|
|
@@ -3243,7 +3207,7 @@ function DiffView({
|
|
|
3243
3207
|
flexDirection: "column"
|
|
3244
3208
|
},
|
|
3245
3209
|
children: [
|
|
3246
|
-
/* @__PURE__ */ (0,
|
|
3210
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
|
|
3247
3211
|
"div",
|
|
3248
3212
|
{
|
|
3249
3213
|
style: {
|
|
@@ -3258,18 +3222,18 @@ function DiffView({
|
|
|
3258
3222
|
flexShrink: 0
|
|
3259
3223
|
},
|
|
3260
3224
|
children: [
|
|
3261
|
-
/* @__PURE__ */ (0,
|
|
3262
|
-
added > 0 && /* @__PURE__ */ (0,
|
|
3225
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { style: { color: "var(--vj-text-muted, #999999)" }, children: entries.length === 0 ? "No changes" : `${entries.length} changes` }),
|
|
3226
|
+
added > 0 && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("span", { style: { color: "#4ec94e" }, children: [
|
|
3263
3227
|
"+",
|
|
3264
3228
|
added,
|
|
3265
3229
|
" added"
|
|
3266
3230
|
] }),
|
|
3267
|
-
removed > 0 && /* @__PURE__ */ (0,
|
|
3231
|
+
removed > 0 && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("span", { style: { color: "#f48771" }, children: [
|
|
3268
3232
|
"-",
|
|
3269
3233
|
removed,
|
|
3270
3234
|
" removed"
|
|
3271
3235
|
] }),
|
|
3272
|
-
changed > 0 && /* @__PURE__ */ (0,
|
|
3236
|
+
changed > 0 && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("span", { style: { color: "#dcdcaa" }, children: [
|
|
3273
3237
|
"~",
|
|
3274
3238
|
changed,
|
|
3275
3239
|
" modified"
|
|
@@ -3277,7 +3241,7 @@ function DiffView({
|
|
|
3277
3241
|
]
|
|
3278
3242
|
}
|
|
3279
3243
|
),
|
|
3280
|
-
/* @__PURE__ */ (0,
|
|
3244
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { style: { flex: 1, overflow: "auto" }, children: entries.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
3281
3245
|
"div",
|
|
3282
3246
|
{
|
|
3283
3247
|
style: {
|
|
@@ -3291,7 +3255,7 @@ function DiffView({
|
|
|
3291
3255
|
},
|
|
3292
3256
|
children: "No differences detected"
|
|
3293
3257
|
}
|
|
3294
|
-
) : entries.map((entry, i) => /* @__PURE__ */ (0,
|
|
3258
|
+
) : entries.map((entry, i) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(DiffRow, { entry }, i)) })
|
|
3295
3259
|
]
|
|
3296
3260
|
}
|
|
3297
3261
|
);
|
|
@@ -3303,7 +3267,6 @@ function DiffView({
|
|
|
3303
3267
|
DiffView,
|
|
3304
3268
|
FormView,
|
|
3305
3269
|
JsonEditor,
|
|
3306
|
-
PropertyEditor,
|
|
3307
3270
|
SearchBar,
|
|
3308
3271
|
StudioContext,
|
|
3309
3272
|
TreeView,
|