@visual-json/react 0.1.1 → 0.3.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 +674 -711
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +674 -714
- package/dist/index.mjs.map +1 -1
- package/package.json +11 -10
- package/LICENSE +0 -201
package/dist/index.mjs
CHANGED
|
@@ -22,6 +22,182 @@ import {
|
|
|
22
22
|
getAncestorIds
|
|
23
23
|
} from "@visual-json/core";
|
|
24
24
|
|
|
25
|
+
// ../../@internal/ui/dist/index.mjs
|
|
26
|
+
import {
|
|
27
|
+
reorderChildrenMulti,
|
|
28
|
+
removeNode,
|
|
29
|
+
insertNode,
|
|
30
|
+
isDescendant
|
|
31
|
+
} from "@visual-json/core";
|
|
32
|
+
import {
|
|
33
|
+
getPropertySchema,
|
|
34
|
+
resolveRef
|
|
35
|
+
} from "@visual-json/core";
|
|
36
|
+
function getVisibleNodes(root, isExpanded) {
|
|
37
|
+
const result = [];
|
|
38
|
+
function walk(node) {
|
|
39
|
+
result.push(node);
|
|
40
|
+
if (isExpanded(node.id) && (node.type === "object" || node.type === "array")) {
|
|
41
|
+
for (const child of node.children) {
|
|
42
|
+
walk(child);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
walk(root);
|
|
47
|
+
return result;
|
|
48
|
+
}
|
|
49
|
+
var LABEL_FIELDS = ["name", "type", "title", "id", "label", "key"];
|
|
50
|
+
function getDisplayKey(node, state) {
|
|
51
|
+
if (node.parentId === null) return "/";
|
|
52
|
+
const parent = state.nodesById.get(node.parentId);
|
|
53
|
+
if (parent?.type !== "array" || node.type !== "object") return node.key;
|
|
54
|
+
for (const field of LABEL_FIELDS) {
|
|
55
|
+
const child = node.children.find((c) => c.key === field);
|
|
56
|
+
if (child?.value != null && child.value !== "") {
|
|
57
|
+
return String(child.value);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return node.key;
|
|
61
|
+
}
|
|
62
|
+
function collectAllIds(node) {
|
|
63
|
+
const ids = [node.id];
|
|
64
|
+
for (const child of node.children) {
|
|
65
|
+
ids.push(...collectAllIds(child));
|
|
66
|
+
}
|
|
67
|
+
return ids;
|
|
68
|
+
}
|
|
69
|
+
var DEFAULT_CSS_VARS = {
|
|
70
|
+
"--vj-bg": "#1e1e1e",
|
|
71
|
+
"--vj-bg-panel": "#252526",
|
|
72
|
+
"--vj-bg-hover": "#2a2d2e",
|
|
73
|
+
"--vj-bg-selected": "#2a5a1e",
|
|
74
|
+
"--vj-bg-selected-muted": "#2a2d2e",
|
|
75
|
+
"--vj-bg-match": "#3a3520",
|
|
76
|
+
"--vj-bg-match-active": "#51502b",
|
|
77
|
+
"--vj-border": "#333333",
|
|
78
|
+
"--vj-border-subtle": "#2a2a2a",
|
|
79
|
+
"--vj-text": "#cccccc",
|
|
80
|
+
"--vj-text-muted": "#888888",
|
|
81
|
+
"--vj-text-dim": "#666666",
|
|
82
|
+
"--vj-text-dimmer": "#555555",
|
|
83
|
+
"--vj-string": "#ce9178",
|
|
84
|
+
"--vj-number": "#b5cea8",
|
|
85
|
+
"--vj-boolean": "#569cd6",
|
|
86
|
+
"--vj-accent": "#007acc",
|
|
87
|
+
"--vj-accent-muted": "#094771",
|
|
88
|
+
"--vj-input-bg": "#3c3c3c",
|
|
89
|
+
"--vj-input-border": "#555555",
|
|
90
|
+
"--vj-error": "#f48771",
|
|
91
|
+
"--vj-font": "monospace",
|
|
92
|
+
"--vj-input-font-size": "13px"
|
|
93
|
+
};
|
|
94
|
+
var EMPTY_SET = Object.freeze(/* @__PURE__ */ new Set());
|
|
95
|
+
function sortByTreeOrder(root, ids) {
|
|
96
|
+
const result = [];
|
|
97
|
+
function walk(node) {
|
|
98
|
+
if (ids.has(node.id)) result.push(node.id);
|
|
99
|
+
for (const child of node.children) walk(child);
|
|
100
|
+
}
|
|
101
|
+
walk(root);
|
|
102
|
+
return result;
|
|
103
|
+
}
|
|
104
|
+
function computeDrop(tree, drag) {
|
|
105
|
+
const { draggedNodeIds, dropTargetNodeId, dropPosition } = drag;
|
|
106
|
+
if (draggedNodeIds.size === 0 || !dropTargetNodeId || !dropPosition)
|
|
107
|
+
return null;
|
|
108
|
+
const targetNode = tree.nodesById.get(dropTargetNodeId);
|
|
109
|
+
if (!targetNode || !targetNode.parentId) return null;
|
|
110
|
+
for (const id of draggedNodeIds) {
|
|
111
|
+
if (isDescendant(tree, dropTargetNodeId, id)) return null;
|
|
112
|
+
}
|
|
113
|
+
const targetParentId = targetNode.parentId;
|
|
114
|
+
const targetParent = tree.nodesById.get(targetParentId);
|
|
115
|
+
if (!targetParent) return null;
|
|
116
|
+
const parentChildren = targetParent.children;
|
|
117
|
+
const orderedDragIds = parentChildren.filter((c) => draggedNodeIds.has(c.id)).map((c) => c.id);
|
|
118
|
+
const allSameParent = orderedDragIds.length === draggedNodeIds.size && [...draggedNodeIds].every((id) => {
|
|
119
|
+
const n = tree.nodesById.get(id);
|
|
120
|
+
return n?.parentId === targetParentId;
|
|
121
|
+
});
|
|
122
|
+
if (allSameParent) {
|
|
123
|
+
return reorderChildrenMulti(
|
|
124
|
+
tree,
|
|
125
|
+
targetParentId,
|
|
126
|
+
orderedDragIds,
|
|
127
|
+
dropTargetNodeId,
|
|
128
|
+
dropPosition
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
const orderedIds = sortByTreeOrder(tree.root, draggedNodeIds);
|
|
132
|
+
const draggedNodes = orderedIds.map((id) => tree.nodesById.get(id)).filter((n) => !!n && n.parentId !== null).map((n) => structuredClone(n));
|
|
133
|
+
let newTree = tree;
|
|
134
|
+
for (const id of [...orderedIds].reverse()) {
|
|
135
|
+
if (newTree.nodesById.has(id)) {
|
|
136
|
+
newTree = removeNode(newTree, id);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
const updatedTarget = newTree.nodesById.get(dropTargetNodeId);
|
|
140
|
+
if (!updatedTarget || !updatedTarget.parentId) return null;
|
|
141
|
+
const updatedParent = newTree.nodesById.get(updatedTarget.parentId);
|
|
142
|
+
if (!updatedParent) return null;
|
|
143
|
+
let insertIdx = updatedParent.children.findIndex(
|
|
144
|
+
(c) => c.id === dropTargetNodeId
|
|
145
|
+
);
|
|
146
|
+
if (dropPosition === "after") insertIdx++;
|
|
147
|
+
for (let i = 0; i < draggedNodes.length; i++) {
|
|
148
|
+
newTree = insertNode(
|
|
149
|
+
newTree,
|
|
150
|
+
updatedParent.id,
|
|
151
|
+
draggedNodes[i],
|
|
152
|
+
insertIdx + i
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
return newTree;
|
|
156
|
+
}
|
|
157
|
+
function setMultiDragImage(dataTransfer, count, rootEl) {
|
|
158
|
+
const ghost = document.createElement("div");
|
|
159
|
+
ghost.textContent = `${count} selected`;
|
|
160
|
+
const root = rootEl ?? document.querySelector("[data-form-container], [role='tree']");
|
|
161
|
+
const cs = root ? getComputedStyle(root) : null;
|
|
162
|
+
const bg = cs?.getPropertyValue("--vj-bg-selected").trim() || DEFAULT_CSS_VARS["--vj-bg-selected"];
|
|
163
|
+
const fg = cs?.getPropertyValue("--vj-text-selected").trim() || cs?.getPropertyValue("--vj-text").trim() || DEFAULT_CSS_VARS["--vj-text"];
|
|
164
|
+
const font = cs?.getPropertyValue("--vj-font").trim() || DEFAULT_CSS_VARS["--vj-font"];
|
|
165
|
+
ghost.style.cssText = [
|
|
166
|
+
"position:fixed",
|
|
167
|
+
"top:-1000px",
|
|
168
|
+
"left:-1000px",
|
|
169
|
+
"padding:4px 12px",
|
|
170
|
+
`background:${bg}`,
|
|
171
|
+
`color:${fg}`,
|
|
172
|
+
`font-family:${font}`,
|
|
173
|
+
"font-size:13px",
|
|
174
|
+
"border-radius:4px",
|
|
175
|
+
"white-space:nowrap",
|
|
176
|
+
"pointer-events:none"
|
|
177
|
+
].join(";");
|
|
178
|
+
document.body.appendChild(ghost);
|
|
179
|
+
dataTransfer.setDragImage(ghost, 0, 14);
|
|
180
|
+
requestAnimationFrame(() => ghost.remove());
|
|
181
|
+
}
|
|
182
|
+
var DIFF_COLORS = {
|
|
183
|
+
added: { bg: "#1e3a1e", marker: "+", label: "#4ec94e" },
|
|
184
|
+
removed: { bg: "#3a1e1e", marker: "-", label: "#f48771" },
|
|
185
|
+
changed: { bg: "#3a3a1e", marker: "~", label: "#dcdcaa" }
|
|
186
|
+
};
|
|
187
|
+
function formatValue(value) {
|
|
188
|
+
if (value === void 0) return "";
|
|
189
|
+
if (value === null) return "null";
|
|
190
|
+
if (typeof value === "string") return JSON.stringify(value);
|
|
191
|
+
if (typeof value === "object") {
|
|
192
|
+
const json = JSON.stringify(value, null, 2);
|
|
193
|
+
if (json.length > 80) {
|
|
194
|
+
return JSON.stringify(value).slice(0, 77) + "...";
|
|
195
|
+
}
|
|
196
|
+
return json;
|
|
197
|
+
}
|
|
198
|
+
return String(value);
|
|
199
|
+
}
|
|
200
|
+
|
|
25
201
|
// src/context.ts
|
|
26
202
|
import { createContext, useContext } from "react";
|
|
27
203
|
var StudioContext = createContext(null);
|
|
@@ -33,15 +209,78 @@ function useStudio() {
|
|
|
33
209
|
return ctx;
|
|
34
210
|
}
|
|
35
211
|
|
|
36
|
-
// src/
|
|
37
|
-
import {
|
|
38
|
-
function
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
212
|
+
// src/selection-utils.ts
|
|
213
|
+
import { removeNode as removeNode2 } from "@visual-json/core";
|
|
214
|
+
function computeSelectAllIds(tree, focusedNodeId, currentlySelected) {
|
|
215
|
+
if (!focusedNodeId) return null;
|
|
216
|
+
let node = tree.nodesById.get(focusedNodeId);
|
|
217
|
+
if (!node) return null;
|
|
218
|
+
while (node) {
|
|
219
|
+
const parent = node.parentId ? tree.nodesById.get(node.parentId) : void 0;
|
|
220
|
+
const siblings = parent ? parent.children : [tree.root];
|
|
221
|
+
const siblingIds = new Set(siblings.map((s) => s.id));
|
|
222
|
+
const allSelected = siblings.every((s) => currentlySelected.has(s.id));
|
|
223
|
+
if (!allSelected) {
|
|
224
|
+
return siblingIds;
|
|
225
|
+
}
|
|
226
|
+
if (!parent || !parent.parentId) return siblingIds;
|
|
227
|
+
node = parent;
|
|
228
|
+
}
|
|
229
|
+
return null;
|
|
230
|
+
}
|
|
231
|
+
function computeRangeIds(visibleNodes, anchorId, targetId) {
|
|
232
|
+
const anchorIdx = visibleNodes.findIndex((n) => n.id === anchorId);
|
|
233
|
+
const targetIdx = visibleNodes.findIndex((n) => n.id === targetId);
|
|
234
|
+
if (anchorIdx === -1 || targetIdx === -1) return null;
|
|
235
|
+
const start = Math.min(anchorIdx, targetIdx);
|
|
236
|
+
const end = Math.max(anchorIdx, targetIdx);
|
|
237
|
+
const ids = /* @__PURE__ */ new Set();
|
|
238
|
+
for (let i = start; i <= end; i++) {
|
|
239
|
+
ids.add(visibleNodes[i].id);
|
|
42
240
|
}
|
|
43
241
|
return ids;
|
|
44
242
|
}
|
|
243
|
+
function deleteSelectedNodes(tree, selectedIds, visibleNodes) {
|
|
244
|
+
const idsToDelete = [...selectedIds].filter((id) => {
|
|
245
|
+
const node = tree.nodesById.get(id);
|
|
246
|
+
if (!node || node.parentId === null) return false;
|
|
247
|
+
let cur = tree.nodesById.get(node.parentId);
|
|
248
|
+
while (cur) {
|
|
249
|
+
if (selectedIds.has(cur.id)) return false;
|
|
250
|
+
cur = cur.parentId ? tree.nodesById.get(cur.parentId) : void 0;
|
|
251
|
+
}
|
|
252
|
+
return true;
|
|
253
|
+
});
|
|
254
|
+
if (idsToDelete.length === 0) return { newTree: tree, nextFocusId: null };
|
|
255
|
+
const firstDeletedIdx = visibleNodes.findIndex((n) => selectedIds.has(n.id));
|
|
256
|
+
let newTree = tree;
|
|
257
|
+
for (const id of idsToDelete) {
|
|
258
|
+
if (newTree.nodesById.has(id)) {
|
|
259
|
+
newTree = removeNode2(newTree, id);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
let nextFocusId = null;
|
|
263
|
+
for (let i = firstDeletedIdx; i < visibleNodes.length; i++) {
|
|
264
|
+
const id = visibleNodes[i].id;
|
|
265
|
+
if (!selectedIds.has(id) && newTree.nodesById.has(id)) {
|
|
266
|
+
nextFocusId = id;
|
|
267
|
+
break;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
if (!nextFocusId) {
|
|
271
|
+
for (let i = firstDeletedIdx - 1; i >= 0; i--) {
|
|
272
|
+
const id = visibleNodes[i].id;
|
|
273
|
+
if (!selectedIds.has(id) && newTree.nodesById.has(id)) {
|
|
274
|
+
nextFocusId = id;
|
|
275
|
+
break;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
return { newTree, nextFocusId };
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// src/visual-json.tsx
|
|
283
|
+
import { jsx } from "react/jsx-runtime";
|
|
45
284
|
function VisualJson({
|
|
46
285
|
value,
|
|
47
286
|
onChange,
|
|
@@ -49,10 +288,42 @@ function VisualJson({
|
|
|
49
288
|
children
|
|
50
289
|
}) {
|
|
51
290
|
const [tree, setTreeState] = useState(() => fromJson(value));
|
|
52
|
-
const [
|
|
291
|
+
const [focusedNodeId, setFocusedNodeId] = useState(null);
|
|
292
|
+
const [selectedNodeIds, setSelectedNodeIdsState] = useState(
|
|
293
|
+
() => /* @__PURE__ */ new Set()
|
|
294
|
+
);
|
|
295
|
+
const selectedNodeIdsRef = useRef(/* @__PURE__ */ new Set());
|
|
296
|
+
const setSelectedNodeIds = useCallback((ids) => {
|
|
297
|
+
selectedNodeIdsRef.current = ids;
|
|
298
|
+
setSelectedNodeIdsState(ids);
|
|
299
|
+
}, []);
|
|
300
|
+
const anchorNodeIdRef = useRef(null);
|
|
301
|
+
const [anchorNodeId, setAnchorNodeIdState] = useState(null);
|
|
302
|
+
const [drillDownNodeId, setDrillDownNodeId] = useState(null);
|
|
53
303
|
const [expandedNodeIds, setExpandedNodeIds] = useState(
|
|
54
304
|
() => /* @__PURE__ */ new Set([tree.root.id])
|
|
55
305
|
);
|
|
306
|
+
const setAnchorNodeId = useCallback((id) => {
|
|
307
|
+
anchorNodeIdRef.current = id;
|
|
308
|
+
setAnchorNodeIdState(id);
|
|
309
|
+
}, []);
|
|
310
|
+
const focusSelectAndDrillDown = useCallback(
|
|
311
|
+
(nodeId) => {
|
|
312
|
+
setFocusedNodeId(nodeId);
|
|
313
|
+
setSelectedNodeIds(nodeId ? /* @__PURE__ */ new Set([nodeId]) : /* @__PURE__ */ new Set());
|
|
314
|
+
setAnchorNodeId(nodeId);
|
|
315
|
+
setDrillDownNodeId(nodeId);
|
|
316
|
+
},
|
|
317
|
+
[setSelectedNodeIds, setAnchorNodeId]
|
|
318
|
+
);
|
|
319
|
+
const visibleNodes = useMemo(
|
|
320
|
+
() => getVisibleNodes(tree.root, (id) => expandedNodeIds.has(id)),
|
|
321
|
+
[tree.root, expandedNodeIds]
|
|
322
|
+
);
|
|
323
|
+
const visibleNodesOverrideRef = useRef(null);
|
|
324
|
+
const setVisibleNodesOverride = useCallback((nodes) => {
|
|
325
|
+
visibleNodesOverrideRef.current = nodes;
|
|
326
|
+
}, []);
|
|
56
327
|
const historyRef = useRef(new History());
|
|
57
328
|
const isInternalChange = useRef(false);
|
|
58
329
|
const hasMounted = useRef(false);
|
|
@@ -81,7 +352,7 @@ function VisualJson({
|
|
|
81
352
|
const newTree = fromJson(value);
|
|
82
353
|
setTreeState(newTree);
|
|
83
354
|
setExpandedNodeIds(/* @__PURE__ */ new Set([newTree.root.id]));
|
|
84
|
-
|
|
355
|
+
focusSelectAndDrillDown(null);
|
|
85
356
|
historyRef.current = new History();
|
|
86
357
|
historyRef.current.push(newTree);
|
|
87
358
|
setCanUndo(false);
|
|
@@ -139,8 +410,66 @@ function VisualJson({
|
|
|
139
410
|
document.addEventListener("keydown", handleKeyDown);
|
|
140
411
|
return () => document.removeEventListener("keydown", handleKeyDown);
|
|
141
412
|
}, [undo, redo]);
|
|
142
|
-
const selectNode = useCallback(
|
|
143
|
-
|
|
413
|
+
const selectNode = useCallback(
|
|
414
|
+
(nodeId) => {
|
|
415
|
+
setFocusedNodeId(nodeId);
|
|
416
|
+
setSelectedNodeIds(nodeId ? /* @__PURE__ */ new Set([nodeId]) : /* @__PURE__ */ new Set());
|
|
417
|
+
setAnchorNodeId(nodeId);
|
|
418
|
+
},
|
|
419
|
+
[setSelectedNodeIds, setAnchorNodeId]
|
|
420
|
+
);
|
|
421
|
+
const selectAndDrillDown = focusSelectAndDrillDown;
|
|
422
|
+
const toggleNodeSelection = useCallback(
|
|
423
|
+
(nodeId) => {
|
|
424
|
+
const next = new Set(selectedNodeIdsRef.current);
|
|
425
|
+
if (next.has(nodeId)) {
|
|
426
|
+
next.delete(nodeId);
|
|
427
|
+
} else {
|
|
428
|
+
next.add(nodeId);
|
|
429
|
+
}
|
|
430
|
+
setSelectedNodeIds(next);
|
|
431
|
+
if (next.size === 0) {
|
|
432
|
+
setFocusedNodeId(null);
|
|
433
|
+
setAnchorNodeId(null);
|
|
434
|
+
} else {
|
|
435
|
+
setFocusedNodeId(nodeId);
|
|
436
|
+
setAnchorNodeId(nodeId);
|
|
437
|
+
}
|
|
438
|
+
},
|
|
439
|
+
[setSelectedNodeIds, setAnchorNodeId]
|
|
440
|
+
);
|
|
441
|
+
const selectNodeRange = useCallback(
|
|
442
|
+
(toNodeId) => {
|
|
443
|
+
const nodes = visibleNodesOverrideRef.current ?? visibleNodes;
|
|
444
|
+
const anchor = anchorNodeIdRef.current;
|
|
445
|
+
if (!anchor) {
|
|
446
|
+
setFocusedNodeId(toNodeId);
|
|
447
|
+
setSelectedNodeIds(/* @__PURE__ */ new Set([toNodeId]));
|
|
448
|
+
setAnchorNodeId(toNodeId);
|
|
449
|
+
return;
|
|
450
|
+
}
|
|
451
|
+
const rangeIds = computeRangeIds(nodes, anchor, toNodeId);
|
|
452
|
+
if (!rangeIds) {
|
|
453
|
+
setFocusedNodeId(toNodeId);
|
|
454
|
+
setSelectedNodeIds(/* @__PURE__ */ new Set([toNodeId]));
|
|
455
|
+
setAnchorNodeId(toNodeId);
|
|
456
|
+
return;
|
|
457
|
+
}
|
|
458
|
+
setSelectedNodeIds(rangeIds);
|
|
459
|
+
setFocusedNodeId(toNodeId);
|
|
460
|
+
},
|
|
461
|
+
[visibleNodes, setSelectedNodeIds, setAnchorNodeId]
|
|
462
|
+
);
|
|
463
|
+
const setSelection = useCallback(
|
|
464
|
+
(focusedId, newSelectedIds, newAnchorId) => {
|
|
465
|
+
setFocusedNodeId(focusedId);
|
|
466
|
+
setSelectedNodeIds(newSelectedIds);
|
|
467
|
+
setAnchorNodeId(newAnchorId);
|
|
468
|
+
},
|
|
469
|
+
[setSelectedNodeIds, setAnchorNodeId]
|
|
470
|
+
);
|
|
471
|
+
const drillDown = useCallback((nodeId) => {
|
|
472
|
+
setDrillDownNodeId(nodeId);
|
|
144
473
|
}, []);
|
|
145
474
|
const toggleExpand = useCallback((nodeId) => {
|
|
146
475
|
setExpandedNodeIds((prev) => {
|
|
@@ -186,6 +515,7 @@ function VisualJson({
|
|
|
186
515
|
const matchIds = new Set(matches.map((m) => m.nodeId));
|
|
187
516
|
setSearchMatchNodeIds(matchIds);
|
|
188
517
|
if (matches.length > 0) {
|
|
518
|
+
const firstId = matches[0].nodeId;
|
|
189
519
|
const ancestors = getAncestorIds(
|
|
190
520
|
tree,
|
|
191
521
|
matches.map((m) => m.nodeId)
|
|
@@ -195,7 +525,7 @@ function VisualJson({
|
|
|
195
525
|
for (const id of ancestors) next.add(id);
|
|
196
526
|
return next;
|
|
197
527
|
});
|
|
198
|
-
|
|
528
|
+
focusSelectAndDrillDown(firstId);
|
|
199
529
|
}
|
|
200
530
|
},
|
|
201
531
|
[tree]
|
|
@@ -204,14 +534,14 @@ function VisualJson({
|
|
|
204
534
|
if (searchMatches.length === 0) return;
|
|
205
535
|
const nextIdx = (searchMatchIndex + 1) % searchMatches.length;
|
|
206
536
|
setSearchMatchIndex(nextIdx);
|
|
207
|
-
|
|
208
|
-
}, [searchMatches, searchMatchIndex]);
|
|
537
|
+
focusSelectAndDrillDown(searchMatches[nextIdx].nodeId);
|
|
538
|
+
}, [searchMatches, searchMatchIndex, focusSelectAndDrillDown]);
|
|
209
539
|
const prevSearchMatch = useCallback(() => {
|
|
210
540
|
if (searchMatches.length === 0) return;
|
|
211
541
|
const prevIdx = (searchMatchIndex - 1 + searchMatches.length) % searchMatches.length;
|
|
212
542
|
setSearchMatchIndex(prevIdx);
|
|
213
|
-
|
|
214
|
-
}, [searchMatches, searchMatchIndex]);
|
|
543
|
+
focusSelectAndDrillDown(searchMatches[prevIdx].nodeId);
|
|
544
|
+
}, [searchMatches, searchMatchIndex, focusSelectAndDrillDown]);
|
|
215
545
|
useEffect(() => {
|
|
216
546
|
if (!searchQuery.trim()) return;
|
|
217
547
|
const matches = searchNodes(tree, searchQuery);
|
|
@@ -224,7 +554,10 @@ function VisualJson({
|
|
|
224
554
|
const state = useMemo(
|
|
225
555
|
() => ({
|
|
226
556
|
tree,
|
|
227
|
-
|
|
557
|
+
focusedNodeId,
|
|
558
|
+
selectedNodeIds,
|
|
559
|
+
anchorNodeId,
|
|
560
|
+
drillDownNodeId,
|
|
228
561
|
expandedNodeIds,
|
|
229
562
|
schema: schema ?? null,
|
|
230
563
|
searchQuery,
|
|
@@ -234,7 +567,10 @@ function VisualJson({
|
|
|
234
567
|
}),
|
|
235
568
|
[
|
|
236
569
|
tree,
|
|
237
|
-
|
|
570
|
+
focusedNodeId,
|
|
571
|
+
selectedNodeIds,
|
|
572
|
+
anchorNodeId,
|
|
573
|
+
drillDownNodeId,
|
|
238
574
|
expandedNodeIds,
|
|
239
575
|
schema,
|
|
240
576
|
searchQuery,
|
|
@@ -247,6 +583,12 @@ function VisualJson({
|
|
|
247
583
|
() => ({
|
|
248
584
|
setTree,
|
|
249
585
|
selectNode,
|
|
586
|
+
selectAndDrillDown,
|
|
587
|
+
toggleNodeSelection,
|
|
588
|
+
selectNodeRange,
|
|
589
|
+
setSelection,
|
|
590
|
+
setVisibleNodesOverride,
|
|
591
|
+
drillDown,
|
|
250
592
|
toggleExpand,
|
|
251
593
|
expandNode,
|
|
252
594
|
collapseNode,
|
|
@@ -263,6 +605,12 @@ function VisualJson({
|
|
|
263
605
|
[
|
|
264
606
|
setTree,
|
|
265
607
|
selectNode,
|
|
608
|
+
selectAndDrillDown,
|
|
609
|
+
toggleNodeSelection,
|
|
610
|
+
selectNodeRange,
|
|
611
|
+
setSelection,
|
|
612
|
+
setVisibleNodesOverride,
|
|
613
|
+
drillDown,
|
|
266
614
|
toggleExpand,
|
|
267
615
|
expandNode,
|
|
268
616
|
collapseNode,
|
|
@@ -282,11 +630,9 @@ function VisualJson({
|
|
|
282
630
|
}
|
|
283
631
|
|
|
284
632
|
// src/tree-view.tsx
|
|
285
|
-
import { useState as useState4, useMemo as
|
|
633
|
+
import { useState as useState4, useMemo as useMemo3, useRef as useRef4, useCallback as useCallback3, useEffect as useEffect3 } from "react";
|
|
286
634
|
import {
|
|
287
|
-
removeNode,
|
|
288
|
-
getPropertySchema,
|
|
289
|
-
validateNode,
|
|
635
|
+
removeNode as removeNode3,
|
|
290
636
|
duplicateNode,
|
|
291
637
|
changeType,
|
|
292
638
|
toJson as toJson2
|
|
@@ -389,111 +735,81 @@ function ContextMenu({ x, y, items, onClose }) {
|
|
|
389
735
|
);
|
|
390
736
|
}
|
|
391
737
|
|
|
392
|
-
// src/display-key.ts
|
|
393
|
-
var LABEL_FIELDS = ["name", "type", "title", "id", "label", "key"];
|
|
394
|
-
function getDisplayKey(node, state) {
|
|
395
|
-
if (node.parentId === null) return "/";
|
|
396
|
-
const parent = state.nodesById.get(node.parentId);
|
|
397
|
-
if (parent?.type !== "array" || node.type !== "object") return node.key;
|
|
398
|
-
for (const field of LABEL_FIELDS) {
|
|
399
|
-
const child = node.children.find((c) => c.key === field);
|
|
400
|
-
if (child?.value != null && child.value !== "") {
|
|
401
|
-
return String(child.value);
|
|
402
|
-
}
|
|
403
|
-
}
|
|
404
|
-
return node.key;
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
// src/get-visible-nodes.ts
|
|
408
|
-
function getVisibleNodes(root, isExpanded) {
|
|
409
|
-
const result = [];
|
|
410
|
-
function walk(node) {
|
|
411
|
-
result.push(node);
|
|
412
|
-
if (isExpanded(node.id) && (node.type === "object" || node.type === "array")) {
|
|
413
|
-
for (const child of node.children) {
|
|
414
|
-
walk(child);
|
|
415
|
-
}
|
|
416
|
-
}
|
|
417
|
-
}
|
|
418
|
-
walk(root);
|
|
419
|
-
return result;
|
|
420
|
-
}
|
|
421
|
-
|
|
422
738
|
// src/use-drag-drop.ts
|
|
423
|
-
import { useState as useState3, useCallback as useCallback2, useRef as useRef3 } from "react";
|
|
424
|
-
import {
|
|
739
|
+
import { useState as useState3, useCallback as useCallback2, useRef as useRef3, useMemo as useMemo2 } from "react";
|
|
740
|
+
import { isDescendant as isDescendant2 } from "@visual-json/core";
|
|
741
|
+
var EMPTY_SET2 = Object.freeze(/* @__PURE__ */ new Set());
|
|
425
742
|
var INITIAL_DRAG_STATE = {
|
|
426
|
-
|
|
743
|
+
draggedNodeIds: EMPTY_SET2,
|
|
427
744
|
dropTargetNodeId: null,
|
|
428
745
|
dropPosition: null
|
|
429
746
|
};
|
|
430
|
-
function
|
|
747
|
+
function setMultiDragImage2(e, count) {
|
|
748
|
+
setMultiDragImage(e.dataTransfer, count);
|
|
749
|
+
}
|
|
750
|
+
function useDragDrop(visibleNodes, selectedNodeIds) {
|
|
431
751
|
const { state, actions } = useStudio();
|
|
432
752
|
const [dragState, setDragState] = useState3(INITIAL_DRAG_STATE);
|
|
433
753
|
const dragStateRef = useRef3(dragState);
|
|
434
754
|
dragStateRef.current = dragState;
|
|
435
|
-
const
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
755
|
+
const visibleNodeIndexMap = useMemo2(() => {
|
|
756
|
+
const map = /* @__PURE__ */ new Map();
|
|
757
|
+
visibleNodes.forEach((n, i) => map.set(n.id, i));
|
|
758
|
+
return map;
|
|
759
|
+
}, [visibleNodes]);
|
|
760
|
+
const handleDragStart = useCallback2(
|
|
761
|
+
(nodeId) => {
|
|
762
|
+
let ids;
|
|
763
|
+
if (selectedNodeIds.size > 0 && selectedNodeIds.has(nodeId)) {
|
|
764
|
+
ids = selectedNodeIds;
|
|
765
|
+
} else {
|
|
766
|
+
ids = /* @__PURE__ */ new Set([nodeId]);
|
|
767
|
+
}
|
|
768
|
+
setDragState({
|
|
769
|
+
draggedNodeIds: ids,
|
|
770
|
+
dropTargetNodeId: null,
|
|
771
|
+
dropPosition: null
|
|
772
|
+
});
|
|
773
|
+
},
|
|
774
|
+
[selectedNodeIds]
|
|
775
|
+
);
|
|
776
|
+
const rawDragOver = useCallback2(
|
|
443
777
|
(nodeId, position) => {
|
|
778
|
+
const draggedIds = dragStateRef.current.draggedNodeIds;
|
|
779
|
+
for (const draggedId of draggedIds) {
|
|
780
|
+
if (nodeId === draggedId || isDescendant2(state.tree, nodeId, draggedId)) {
|
|
781
|
+
return;
|
|
782
|
+
}
|
|
783
|
+
}
|
|
444
784
|
setDragState((prev) => ({
|
|
445
785
|
...prev,
|
|
446
786
|
dropTargetNodeId: nodeId,
|
|
447
787
|
dropPosition: position
|
|
448
788
|
}));
|
|
449
789
|
},
|
|
450
|
-
[]
|
|
790
|
+
[state.tree]
|
|
791
|
+
);
|
|
792
|
+
const handleDragOver = useCallback2(
|
|
793
|
+
(nodeId, position) => {
|
|
794
|
+
if (position === "before") {
|
|
795
|
+
const idx = visibleNodeIndexMap.get(nodeId);
|
|
796
|
+
if (idx !== void 0 && idx > 0) {
|
|
797
|
+
rawDragOver(visibleNodes[idx - 1].id, "after");
|
|
798
|
+
return;
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
rawDragOver(nodeId, position);
|
|
802
|
+
},
|
|
803
|
+
[visibleNodes, visibleNodeIndexMap, rawDragOver]
|
|
451
804
|
);
|
|
452
805
|
const handleDragEnd = useCallback2(() => {
|
|
453
806
|
setDragState(INITIAL_DRAG_STATE);
|
|
454
807
|
}, []);
|
|
455
808
|
const handleDrop = useCallback2(() => {
|
|
456
|
-
const
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
if (!draggedNode || !targetNode) return;
|
|
461
|
-
if (draggedNode.parentId && draggedNode.parentId === targetNode.parentId) {
|
|
462
|
-
const parent = state.tree.nodesById.get(draggedNode.parentId);
|
|
463
|
-
if (parent) {
|
|
464
|
-
const fromIndex = parent.children.findIndex(
|
|
465
|
-
(c) => c.id === draggedNodeId
|
|
466
|
-
);
|
|
467
|
-
let toIndex = parent.children.findIndex(
|
|
468
|
-
(c) => c.id === dropTargetNodeId
|
|
469
|
-
);
|
|
470
|
-
if (dropPosition === "after") toIndex++;
|
|
471
|
-
if (fromIndex < toIndex) toIndex--;
|
|
472
|
-
if (fromIndex !== toIndex && fromIndex >= 0 && toIndex >= 0) {
|
|
473
|
-
const newTree = reorderChildren(
|
|
474
|
-
state.tree,
|
|
475
|
-
parent.id,
|
|
476
|
-
fromIndex,
|
|
477
|
-
toIndex
|
|
478
|
-
);
|
|
479
|
-
actions.setTree(newTree);
|
|
480
|
-
}
|
|
481
|
-
}
|
|
482
|
-
} else if (targetNode.parentId) {
|
|
483
|
-
const newParent = state.tree.nodesById.get(targetNode.parentId);
|
|
484
|
-
if (newParent) {
|
|
485
|
-
let toIndex = newParent.children.findIndex(
|
|
486
|
-
(c) => c.id === dropTargetNodeId
|
|
487
|
-
);
|
|
488
|
-
if (dropPosition === "after") toIndex++;
|
|
489
|
-
const newTree = moveNode(
|
|
490
|
-
state.tree,
|
|
491
|
-
draggedNodeId,
|
|
492
|
-
newParent.id,
|
|
493
|
-
toIndex
|
|
494
|
-
);
|
|
495
|
-
actions.setTree(newTree);
|
|
496
|
-
}
|
|
809
|
+
const currentDragState = dragStateRef.current;
|
|
810
|
+
const newTree = computeDrop(state.tree, currentDragState);
|
|
811
|
+
if (newTree) {
|
|
812
|
+
actions.setTree(newTree);
|
|
497
813
|
}
|
|
498
814
|
setDragState(INITIAL_DRAG_STATE);
|
|
499
815
|
}, [state.tree, actions]);
|
|
@@ -515,6 +831,7 @@ function TreeNodeRow({
|
|
|
515
831
|
showValues,
|
|
516
832
|
showCounts,
|
|
517
833
|
isFocused,
|
|
834
|
+
onSelectRange,
|
|
518
835
|
onDragStart,
|
|
519
836
|
onDragOver,
|
|
520
837
|
onDragEnd,
|
|
@@ -522,19 +839,15 @@ function TreeNodeRow({
|
|
|
522
839
|
onContextMenu
|
|
523
840
|
}) {
|
|
524
841
|
const { state, actions } = useStudio();
|
|
525
|
-
const isSelected = state.
|
|
842
|
+
const isSelected = state.selectedNodeIds.has(node.id);
|
|
526
843
|
const isExpanded = state.expandedNodeIds.has(node.id);
|
|
527
844
|
const isContainer = node.type === "object" || node.type === "array";
|
|
528
845
|
const [hovered, setHovered] = useState4(false);
|
|
529
846
|
const isRoot = node.parentId === null;
|
|
530
847
|
const isSearchMatch = state.searchMatchNodeIds.has(node.id);
|
|
531
848
|
const isActiveMatch = state.searchMatches.length > 0 && state.searchMatches[state.searchMatchIndex]?.nodeId === node.id;
|
|
532
|
-
const schema = state.schema;
|
|
533
|
-
const nodeSchema = schema ? getPropertySchema(schema, node.path) : void 0;
|
|
534
|
-
const validation = nodeSchema ? validateNode(node, nodeSchema) : null;
|
|
535
|
-
const hasError = validation ? !validation.valid : false;
|
|
536
849
|
const isDragTarget = dragState.dropTargetNodeId === node.id;
|
|
537
|
-
const isDraggedNode = dragState.
|
|
850
|
+
const isDraggedNode = dragState.draggedNodeIds.has(node.id);
|
|
538
851
|
function displayValue() {
|
|
539
852
|
if (isContainer) {
|
|
540
853
|
return node.type === "array" ? `[${node.children.length}]` : `{${node.children.length}}`;
|
|
@@ -550,12 +863,12 @@ function TreeNodeRow({
|
|
|
550
863
|
const position = e.clientY < midY ? "before" : "after";
|
|
551
864
|
onDragOver(node.id, position);
|
|
552
865
|
}
|
|
553
|
-
let
|
|
554
|
-
let
|
|
866
|
+
let borderTopColor = "transparent";
|
|
867
|
+
let borderBottomColor = "transparent";
|
|
555
868
|
if (isDragTarget && dragState.dropPosition === "before") {
|
|
556
|
-
|
|
869
|
+
borderTopColor = "var(--vj-accent, #007acc)";
|
|
557
870
|
} else if (isDragTarget && dragState.dropPosition === "after") {
|
|
558
|
-
|
|
871
|
+
borderBottomColor = "var(--vj-accent, #007acc)";
|
|
559
872
|
}
|
|
560
873
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
561
874
|
/* @__PURE__ */ jsxs(
|
|
@@ -564,13 +877,24 @@ function TreeNodeRow({
|
|
|
564
877
|
role: "treeitem",
|
|
565
878
|
"aria-selected": isSelected,
|
|
566
879
|
"aria-expanded": isContainer ? isExpanded : void 0,
|
|
567
|
-
onClick: () =>
|
|
880
|
+
onClick: (e) => {
|
|
881
|
+
if (e.shiftKey) {
|
|
882
|
+
onSelectRange(node.id);
|
|
883
|
+
} else if (e.metaKey || e.ctrlKey) {
|
|
884
|
+
actions.toggleNodeSelection(node.id);
|
|
885
|
+
} else {
|
|
886
|
+
actions.selectAndDrillDown(node.id);
|
|
887
|
+
}
|
|
888
|
+
},
|
|
568
889
|
onMouseEnter: () => setHovered(true),
|
|
569
890
|
onMouseLeave: () => setHovered(false),
|
|
570
891
|
onContextMenu: (e) => onContextMenu(e, node),
|
|
571
892
|
draggable: !isRoot,
|
|
572
893
|
onDragStart: (e) => {
|
|
573
894
|
e.dataTransfer.effectAllowed = "move";
|
|
895
|
+
if (state.selectedNodeIds.size > 1 && state.selectedNodeIds.has(node.id)) {
|
|
896
|
+
setMultiDragImage2(e, state.selectedNodeIds.size);
|
|
897
|
+
}
|
|
574
898
|
onDragStart(node.id);
|
|
575
899
|
},
|
|
576
900
|
onDragOver: handleDragOverEvent,
|
|
@@ -584,15 +908,16 @@ function TreeNodeRow({
|
|
|
584
908
|
display: "flex",
|
|
585
909
|
alignItems: "center",
|
|
586
910
|
gap: 6,
|
|
587
|
-
padding: "
|
|
911
|
+
padding: "1px 8px",
|
|
588
912
|
paddingLeft: 8 + depth * 16,
|
|
589
913
|
cursor: "pointer",
|
|
590
914
|
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",
|
|
591
915
|
minHeight: 28,
|
|
592
916
|
userSelect: "none",
|
|
593
917
|
opacity: isDraggedNode ? 0.4 : 1,
|
|
594
|
-
borderTop
|
|
595
|
-
borderBottom
|
|
918
|
+
borderTop: `2px solid ${borderTopColor}`,
|
|
919
|
+
borderBottom: `2px solid ${borderBottomColor}`,
|
|
920
|
+
boxSizing: "border-box",
|
|
596
921
|
color: isSelected && isFocused ? "var(--vj-text-selected, var(--vj-text, #cccccc))" : "var(--vj-text, #cccccc)"
|
|
597
922
|
},
|
|
598
923
|
children: [
|
|
@@ -675,6 +1000,7 @@ function TreeNodeRow({
|
|
|
675
1000
|
showValues,
|
|
676
1001
|
showCounts,
|
|
677
1002
|
isFocused,
|
|
1003
|
+
onSelectRange,
|
|
678
1004
|
onDragStart,
|
|
679
1005
|
onDragOver,
|
|
680
1006
|
onDragEnd,
|
|
@@ -692,25 +1018,34 @@ function TreeView({
|
|
|
692
1018
|
}) {
|
|
693
1019
|
const { state, actions } = useStudio();
|
|
694
1020
|
const containerRef = useRef4(null);
|
|
1021
|
+
const visibleNodes = useMemo3(
|
|
1022
|
+
() => getVisibleNodes(state.tree.root, (id) => state.expandedNodeIds.has(id)),
|
|
1023
|
+
[state.tree.root, state.expandedNodeIds]
|
|
1024
|
+
);
|
|
695
1025
|
const {
|
|
696
1026
|
dragState,
|
|
697
1027
|
handleDragStart,
|
|
698
1028
|
handleDragOver,
|
|
699
1029
|
handleDragEnd,
|
|
700
1030
|
handleDrop
|
|
701
|
-
} = useDragDrop();
|
|
702
|
-
const
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
1031
|
+
} = useDragDrop(visibleNodes, state.selectedNodeIds);
|
|
1032
|
+
const handleSelectRange = useCallback3(
|
|
1033
|
+
(nodeId) => {
|
|
1034
|
+
actions.setVisibleNodesOverride(visibleNodes);
|
|
1035
|
+
actions.selectNodeRange(nodeId);
|
|
1036
|
+
},
|
|
1037
|
+
[visibleNodes, actions]
|
|
706
1038
|
);
|
|
1039
|
+
const [contextMenu, setContextMenu] = useState4(null);
|
|
707
1040
|
const handleContextMenu = useCallback3(
|
|
708
1041
|
(e, node) => {
|
|
709
1042
|
e.preventDefault();
|
|
710
|
-
|
|
1043
|
+
if (!state.selectedNodeIds.has(node.id)) {
|
|
1044
|
+
actions.selectAndDrillDown(node.id);
|
|
1045
|
+
}
|
|
711
1046
|
setContextMenu({ x: e.clientX, y: e.clientY, node });
|
|
712
1047
|
},
|
|
713
|
-
[actions]
|
|
1048
|
+
[actions, state.selectedNodeIds]
|
|
714
1049
|
);
|
|
715
1050
|
const buildContextMenuItems = useCallback3(
|
|
716
1051
|
(node) => {
|
|
@@ -789,7 +1124,7 @@ function TreeView({
|
|
|
789
1124
|
items.push({
|
|
790
1125
|
label: "Delete",
|
|
791
1126
|
action: () => {
|
|
792
|
-
const newTree =
|
|
1127
|
+
const newTree = removeNode3(state.tree, node.id);
|
|
793
1128
|
actions.setTree(newTree);
|
|
794
1129
|
}
|
|
795
1130
|
});
|
|
@@ -801,19 +1136,31 @@ function TreeView({
|
|
|
801
1136
|
const handleKeyDown = useCallback3(
|
|
802
1137
|
(e) => {
|
|
803
1138
|
const currentIndex = visibleNodes.findIndex(
|
|
804
|
-
(n) => n.id === state.
|
|
1139
|
+
(n) => n.id === state.focusedNodeId
|
|
805
1140
|
);
|
|
806
1141
|
switch (e.key) {
|
|
807
1142
|
case "ArrowDown": {
|
|
808
1143
|
e.preventDefault();
|
|
809
1144
|
const next = visibleNodes[currentIndex + 1];
|
|
810
|
-
if (next)
|
|
1145
|
+
if (next) {
|
|
1146
|
+
if (e.shiftKey) {
|
|
1147
|
+
handleSelectRange(next.id);
|
|
1148
|
+
} else {
|
|
1149
|
+
actions.selectNode(next.id);
|
|
1150
|
+
}
|
|
1151
|
+
}
|
|
811
1152
|
break;
|
|
812
1153
|
}
|
|
813
1154
|
case "ArrowUp": {
|
|
814
1155
|
e.preventDefault();
|
|
815
1156
|
const prev = visibleNodes[currentIndex - 1];
|
|
816
|
-
if (prev)
|
|
1157
|
+
if (prev) {
|
|
1158
|
+
if (e.shiftKey) {
|
|
1159
|
+
handleSelectRange(prev.id);
|
|
1160
|
+
} else {
|
|
1161
|
+
actions.selectNode(prev.id);
|
|
1162
|
+
}
|
|
1163
|
+
}
|
|
817
1164
|
break;
|
|
818
1165
|
}
|
|
819
1166
|
case "ArrowRight": {
|
|
@@ -840,17 +1187,47 @@ function TreeView({
|
|
|
840
1187
|
}
|
|
841
1188
|
break;
|
|
842
1189
|
}
|
|
1190
|
+
case "a": {
|
|
1191
|
+
if (e.metaKey || e.ctrlKey) {
|
|
1192
|
+
e.preventDefault();
|
|
1193
|
+
const ids = computeSelectAllIds(
|
|
1194
|
+
state.tree,
|
|
1195
|
+
state.focusedNodeId,
|
|
1196
|
+
state.selectedNodeIds
|
|
1197
|
+
);
|
|
1198
|
+
if (ids) {
|
|
1199
|
+
actions.setSelection(
|
|
1200
|
+
state.focusedNodeId,
|
|
1201
|
+
ids,
|
|
1202
|
+
state.focusedNodeId
|
|
1203
|
+
);
|
|
1204
|
+
}
|
|
1205
|
+
}
|
|
1206
|
+
break;
|
|
1207
|
+
}
|
|
1208
|
+
case "Escape": {
|
|
1209
|
+
e.preventDefault();
|
|
1210
|
+
if (state.selectedNodeIds.size > 1 && state.focusedNodeId) {
|
|
1211
|
+
actions.selectNode(state.focusedNodeId);
|
|
1212
|
+
} else {
|
|
1213
|
+
actions.setSelection(null, /* @__PURE__ */ new Set(), null);
|
|
1214
|
+
}
|
|
1215
|
+
break;
|
|
1216
|
+
}
|
|
843
1217
|
case "Delete":
|
|
844
1218
|
case "Backspace": {
|
|
845
1219
|
e.preventDefault();
|
|
846
|
-
const
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
1220
|
+
const { newTree, nextFocusId } = deleteSelectedNodes(
|
|
1221
|
+
state.tree,
|
|
1222
|
+
state.selectedNodeIds,
|
|
1223
|
+
visibleNodes
|
|
1224
|
+
);
|
|
1225
|
+
if (newTree === state.tree) break;
|
|
1226
|
+
actions.setTree(newTree);
|
|
1227
|
+
if (nextFocusId) {
|
|
1228
|
+
actions.selectNode(nextFocusId);
|
|
1229
|
+
} else {
|
|
1230
|
+
actions.setSelection(null, /* @__PURE__ */ new Set(), null);
|
|
854
1231
|
}
|
|
855
1232
|
break;
|
|
856
1233
|
}
|
|
@@ -858,23 +1235,25 @@ function TreeView({
|
|
|
858
1235
|
},
|
|
859
1236
|
[
|
|
860
1237
|
visibleNodes,
|
|
861
|
-
state.
|
|
1238
|
+
state.focusedNodeId,
|
|
1239
|
+
state.selectedNodeIds,
|
|
862
1240
|
state.expandedNodeIds,
|
|
863
1241
|
state.tree,
|
|
864
|
-
actions
|
|
1242
|
+
actions,
|
|
1243
|
+
handleSelectRange
|
|
865
1244
|
]
|
|
866
1245
|
);
|
|
867
1246
|
const [isFocused, setIsFocused] = useState4(false);
|
|
868
1247
|
useEffect3(() => {
|
|
869
|
-
if (state.
|
|
1248
|
+
if (state.focusedNodeId && containerRef.current) {
|
|
870
1249
|
const el = containerRef.current.querySelector(
|
|
871
|
-
`[data-node-id="${state.
|
|
1250
|
+
`[data-node-id="${state.focusedNodeId}"]`
|
|
872
1251
|
);
|
|
873
1252
|
if (el) {
|
|
874
1253
|
el.scrollIntoView({ block: "nearest" });
|
|
875
1254
|
}
|
|
876
1255
|
}
|
|
877
|
-
}, [state.
|
|
1256
|
+
}, [state.focusedNodeId]);
|
|
878
1257
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
879
1258
|
/* @__PURE__ */ jsx3(
|
|
880
1259
|
"div",
|
|
@@ -904,6 +1283,7 @@ function TreeView({
|
|
|
904
1283
|
showValues,
|
|
905
1284
|
showCounts,
|
|
906
1285
|
isFocused,
|
|
1286
|
+
onSelectRange: handleSelectRange,
|
|
907
1287
|
onDragStart: handleDragStart,
|
|
908
1288
|
onDragOver: handleDragOver,
|
|
909
1289
|
onDragEnd: handleDragEnd,
|
|
@@ -926,25 +1306,25 @@ function TreeView({
|
|
|
926
1306
|
}
|
|
927
1307
|
|
|
928
1308
|
// src/form-view.tsx
|
|
929
|
-
import { useState as useState7, useCallback as useCallback6, useRef as useRef7, useEffect as useEffect6, useMemo as
|
|
1309
|
+
import { useState as useState7, useCallback as useCallback6, useRef as useRef7, useEffect as useEffect6, useMemo as useMemo6 } from "react";
|
|
930
1310
|
import {
|
|
931
1311
|
setValue,
|
|
932
1312
|
setKey,
|
|
933
1313
|
addProperty,
|
|
934
|
-
removeNode as
|
|
1314
|
+
removeNode as removeNode4,
|
|
935
1315
|
getPropertySchema as getPropertySchema2,
|
|
936
|
-
resolveRef
|
|
1316
|
+
resolveRef as resolveRef2
|
|
937
1317
|
} from "@visual-json/core";
|
|
938
1318
|
|
|
939
1319
|
// src/breadcrumbs.tsx
|
|
940
|
-
import { useState as useState5, useRef as useRef5, useEffect as useEffect4, useCallback as useCallback4, useMemo as
|
|
1320
|
+
import { useState as useState5, useRef as useRef5, useEffect as useEffect4, useCallback as useCallback4, useMemo as useMemo4 } from "react";
|
|
941
1321
|
import { jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
942
1322
|
var MAX_SUGGESTIONS = 20;
|
|
943
1323
|
var DROPDOWN_MAX_HEIGHT = 200;
|
|
944
1324
|
function Breadcrumbs({ className }) {
|
|
945
1325
|
const { state, actions } = useStudio();
|
|
946
|
-
const
|
|
947
|
-
const currentPath =
|
|
1326
|
+
const drillDownNode = state.drillDownNodeId ? state.tree.nodesById.get(state.drillDownNodeId) : null;
|
|
1327
|
+
const currentPath = drillDownNode?.path ?? "/";
|
|
948
1328
|
const [inputValue, setInputValue] = useState5(currentPath);
|
|
949
1329
|
const [open, setOpen] = useState5(false);
|
|
950
1330
|
const [highlightIndex, setHighlightIndex] = useState5(0);
|
|
@@ -954,7 +1334,7 @@ function Breadcrumbs({ className }) {
|
|
|
954
1334
|
useEffect4(() => {
|
|
955
1335
|
setInputValue(currentPath);
|
|
956
1336
|
}, [currentPath]);
|
|
957
|
-
const suggestions =
|
|
1337
|
+
const suggestions = useMemo4(() => {
|
|
958
1338
|
if (!open) return [];
|
|
959
1339
|
const query = inputValue.toLowerCase();
|
|
960
1340
|
const matches = [];
|
|
@@ -974,7 +1354,7 @@ function Breadcrumbs({ className }) {
|
|
|
974
1354
|
(path) => {
|
|
975
1355
|
for (const [id, node] of state.tree.nodesById) {
|
|
976
1356
|
if (node.path === path) {
|
|
977
|
-
actions.
|
|
1357
|
+
actions.selectAndDrillDown(id);
|
|
978
1358
|
break;
|
|
979
1359
|
}
|
|
980
1360
|
}
|
|
@@ -1062,7 +1442,7 @@ function Breadcrumbs({ className }) {
|
|
|
1062
1442
|
style: {
|
|
1063
1443
|
width: "100%",
|
|
1064
1444
|
boxSizing: "border-box",
|
|
1065
|
-
padding: "
|
|
1445
|
+
padding: "3px 0",
|
|
1066
1446
|
fontSize: "var(--vj-input-font-size, 13px)",
|
|
1067
1447
|
fontFamily: "var(--vj-font, monospace)",
|
|
1068
1448
|
color: "var(--vj-text-muted, #999999)",
|
|
@@ -1124,7 +1504,7 @@ import {
|
|
|
1124
1504
|
useRef as useRef6,
|
|
1125
1505
|
useEffect as useEffect5,
|
|
1126
1506
|
useCallback as useCallback5,
|
|
1127
|
-
useMemo as
|
|
1507
|
+
useMemo as useMemo5
|
|
1128
1508
|
} from "react";
|
|
1129
1509
|
import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
1130
1510
|
var DROPDOWN_MAX_HEIGHT2 = 200;
|
|
@@ -1143,7 +1523,7 @@ function EnumInput({
|
|
|
1143
1523
|
useEffect5(() => {
|
|
1144
1524
|
setInputValue(value);
|
|
1145
1525
|
}, [value]);
|
|
1146
|
-
const suggestions =
|
|
1526
|
+
const suggestions = useMemo5(
|
|
1147
1527
|
() => enumValues.map((v) => String(v)),
|
|
1148
1528
|
[enumValues]
|
|
1149
1529
|
);
|
|
@@ -1297,7 +1677,7 @@ function getResolvedSchema(schema, rootSchema, path) {
|
|
|
1297
1677
|
if (!schema) return void 0;
|
|
1298
1678
|
const raw = getPropertySchema2(schema, path, rootSchema);
|
|
1299
1679
|
if (!raw) return void 0;
|
|
1300
|
-
return
|
|
1680
|
+
return resolveRef2(raw, rootSchema ?? schema);
|
|
1301
1681
|
}
|
|
1302
1682
|
function getValueColor(node) {
|
|
1303
1683
|
if (node.type === "boolean" || node.type === "null")
|
|
@@ -1318,7 +1698,6 @@ function FormField({
|
|
|
1318
1698
|
depth,
|
|
1319
1699
|
showDescriptions,
|
|
1320
1700
|
showCounts,
|
|
1321
|
-
formSelectedNodeId,
|
|
1322
1701
|
editingNodeId,
|
|
1323
1702
|
collapsedIds,
|
|
1324
1703
|
maxKeyLength,
|
|
@@ -1336,26 +1715,26 @@ function FormField({
|
|
|
1336
1715
|
const { state, actions } = useStudio();
|
|
1337
1716
|
const isContainer = node.type === "object" || node.type === "array";
|
|
1338
1717
|
const collapsed = collapsedIds.has(node.id);
|
|
1339
|
-
const isSelected =
|
|
1718
|
+
const isSelected = state.selectedNodeIds.has(node.id);
|
|
1340
1719
|
const isEditing = editingNodeId === node.id;
|
|
1341
1720
|
const propSchema = getResolvedSchema(schema, rootSchema, node.path);
|
|
1342
1721
|
const isRequired = checkRequired(node, schema, rootSchema);
|
|
1343
1722
|
const [hovered, setHovered] = useState7(false);
|
|
1344
1723
|
const isRoot = node.parentId === null;
|
|
1345
1724
|
const isDragTarget = dragState.dropTargetNodeId === node.id;
|
|
1346
|
-
const isDraggedNode = dragState.
|
|
1725
|
+
const isDraggedNode = dragState.draggedNodeIds.has(node.id);
|
|
1347
1726
|
function handleDragOverEvent(e) {
|
|
1348
1727
|
e.preventDefault();
|
|
1349
1728
|
const rect = e.currentTarget.getBoundingClientRect();
|
|
1350
1729
|
const midY = rect.top + rect.height / 2;
|
|
1351
1730
|
onDragOver(node.id, e.clientY < midY ? "before" : "after");
|
|
1352
1731
|
}
|
|
1353
|
-
let
|
|
1354
|
-
let
|
|
1732
|
+
let borderTopColor = "transparent";
|
|
1733
|
+
let borderBottomColor = "transparent";
|
|
1355
1734
|
if (isDragTarget && dragState.dropPosition === "before") {
|
|
1356
|
-
|
|
1735
|
+
borderTopColor = "var(--vj-accent, #007acc)";
|
|
1357
1736
|
} else if (isDragTarget && dragState.dropPosition === "after") {
|
|
1358
|
-
|
|
1737
|
+
borderBottomColor = "var(--vj-accent, #007acc)";
|
|
1359
1738
|
}
|
|
1360
1739
|
const valueRef = useRef7(null);
|
|
1361
1740
|
const keyRef = useRef7(null);
|
|
@@ -1398,7 +1777,7 @@ function FormField({
|
|
|
1398
1777
|
[state.tree, node.id, actions]
|
|
1399
1778
|
);
|
|
1400
1779
|
const handleRemove = useCallback6(() => {
|
|
1401
|
-
const newTree =
|
|
1780
|
+
const newTree = removeNode4(state.tree, node.id);
|
|
1402
1781
|
actions.setTree(newTree);
|
|
1403
1782
|
}, [state.tree, node.id, actions]);
|
|
1404
1783
|
const handleAddChild = useCallback6(() => {
|
|
@@ -1407,7 +1786,6 @@ function FormField({
|
|
|
1407
1786
|
actions.setTree(newTree);
|
|
1408
1787
|
}, [state.tree, node.id, node.type, node.children.length, actions]);
|
|
1409
1788
|
const description = propSchema?.description;
|
|
1410
|
-
const defaultVal = propSchema?.default;
|
|
1411
1789
|
const isDeprecated = propSchema?.deprecated;
|
|
1412
1790
|
const fieldTitle = propSchema?.title;
|
|
1413
1791
|
const parentIsObject = node.parentId && state.tree.nodesById.get(node.parentId)?.type === "object";
|
|
@@ -1422,6 +1800,9 @@ function FormField({
|
|
|
1422
1800
|
draggable: !isRoot,
|
|
1423
1801
|
onDragStart: (e) => {
|
|
1424
1802
|
e.dataTransfer.effectAllowed = "move";
|
|
1803
|
+
if (state.selectedNodeIds.size > 1 && state.selectedNodeIds.has(node.id)) {
|
|
1804
|
+
setMultiDragImage2(e, state.selectedNodeIds.size);
|
|
1805
|
+
}
|
|
1425
1806
|
onDragStart(node.id);
|
|
1426
1807
|
},
|
|
1427
1808
|
onDragOver: handleDragOverEvent,
|
|
@@ -1434,20 +1815,21 @@ function FormField({
|
|
|
1434
1815
|
display: "flex",
|
|
1435
1816
|
alignItems: "center",
|
|
1436
1817
|
gap: 6,
|
|
1437
|
-
padding: "
|
|
1818
|
+
padding: "1px 8px",
|
|
1438
1819
|
paddingLeft: 8 + depth * 16,
|
|
1439
1820
|
cursor: "pointer",
|
|
1440
1821
|
backgroundColor: rowBg,
|
|
1441
1822
|
color: rowColor,
|
|
1442
1823
|
height: 28,
|
|
1824
|
+
boxSizing: "border-box",
|
|
1443
1825
|
userSelect: "none",
|
|
1444
1826
|
opacity: isDeprecated ? 0.5 : isDraggedNode ? 0.4 : 1,
|
|
1445
|
-
borderTop
|
|
1446
|
-
borderBottom
|
|
1827
|
+
borderTop: `2px solid ${borderTopColor}`,
|
|
1828
|
+
borderBottom: `2px solid ${borderBottomColor}`
|
|
1447
1829
|
},
|
|
1448
1830
|
onClick: (e) => {
|
|
1449
1831
|
e.stopPropagation();
|
|
1450
|
-
onSelect(node.id);
|
|
1832
|
+
onSelect(node.id, e);
|
|
1451
1833
|
},
|
|
1452
1834
|
onDoubleClick: () => onToggleCollapse(node.id),
|
|
1453
1835
|
onMouseEnter: () => setHovered(true),
|
|
@@ -1503,7 +1885,7 @@ function FormField({
|
|
|
1503
1885
|
{
|
|
1504
1886
|
style: {
|
|
1505
1887
|
color: !isRoot && !parentIsObject && !isSelected ? "var(--vj-text-muted, #888888)" : "inherit",
|
|
1506
|
-
fontSize:
|
|
1888
|
+
fontSize: "var(--vj-input-font-size, 13px)",
|
|
1507
1889
|
fontFamily: "var(--vj-font, monospace)",
|
|
1508
1890
|
fontWeight: 500,
|
|
1509
1891
|
flexShrink: 0,
|
|
@@ -1604,7 +1986,6 @@ function FormField({
|
|
|
1604
1986
|
depth: depth + 1,
|
|
1605
1987
|
showDescriptions,
|
|
1606
1988
|
showCounts,
|
|
1607
|
-
formSelectedNodeId,
|
|
1608
1989
|
editingNodeId,
|
|
1609
1990
|
collapsedIds,
|
|
1610
1991
|
maxKeyLength,
|
|
@@ -1632,6 +2013,9 @@ function FormField({
|
|
|
1632
2013
|
draggable: !isRoot,
|
|
1633
2014
|
onDragStart: (e) => {
|
|
1634
2015
|
e.dataTransfer.effectAllowed = "move";
|
|
2016
|
+
if (state.selectedNodeIds.size > 1 && state.selectedNodeIds.has(node.id)) {
|
|
2017
|
+
setMultiDragImage2(e, state.selectedNodeIds.size);
|
|
2018
|
+
}
|
|
1635
2019
|
onDragStart(node.id);
|
|
1636
2020
|
},
|
|
1637
2021
|
onDragOver: handleDragOverEvent,
|
|
@@ -1644,20 +2028,21 @@ function FormField({
|
|
|
1644
2028
|
display: "flex",
|
|
1645
2029
|
alignItems: "center",
|
|
1646
2030
|
gap: 6,
|
|
1647
|
-
padding: "
|
|
2031
|
+
padding: "1px 8px",
|
|
1648
2032
|
paddingLeft: 8 + depth * 16,
|
|
1649
2033
|
cursor: "pointer",
|
|
1650
2034
|
backgroundColor: rowBg,
|
|
1651
2035
|
color: rowColor,
|
|
1652
2036
|
height: 28,
|
|
2037
|
+
boxSizing: "border-box",
|
|
1653
2038
|
userSelect: "none",
|
|
1654
2039
|
opacity: isDeprecated ? 0.5 : isDraggedNode ? 0.4 : 1,
|
|
1655
|
-
borderTop
|
|
1656
|
-
borderBottom
|
|
2040
|
+
borderTop: `2px solid ${borderTopColor}`,
|
|
2041
|
+
borderBottom: `2px solid ${borderBottomColor}`
|
|
1657
2042
|
},
|
|
1658
2043
|
onClick: (e) => {
|
|
1659
2044
|
e.stopPropagation();
|
|
1660
|
-
onSelect(node.id);
|
|
2045
|
+
onSelect(node.id, e);
|
|
1661
2046
|
},
|
|
1662
2047
|
onDoubleClick: () => onStartEditing(node.id),
|
|
1663
2048
|
onMouseEnter: () => setHovered(true),
|
|
@@ -1694,7 +2079,7 @@ function FormField({
|
|
|
1694
2079
|
{
|
|
1695
2080
|
style: {
|
|
1696
2081
|
color: !parentIsObject && !isSelected ? "var(--vj-text-muted, #888888)" : "inherit",
|
|
1697
|
-
fontSize:
|
|
2082
|
+
fontSize: "var(--vj-input-font-size, 13px)",
|
|
1698
2083
|
fontFamily: "var(--vj-font, monospace)",
|
|
1699
2084
|
flexShrink: 0,
|
|
1700
2085
|
display: "inline-block",
|
|
@@ -1738,7 +2123,7 @@ function FormField({
|
|
|
1738
2123
|
{
|
|
1739
2124
|
style: {
|
|
1740
2125
|
color: valueColor,
|
|
1741
|
-
fontSize:
|
|
2126
|
+
fontSize: "var(--vj-input-font-size, 13px)",
|
|
1742
2127
|
fontFamily: "var(--vj-font, monospace)",
|
|
1743
2128
|
overflow: "hidden",
|
|
1744
2129
|
textOverflow: "ellipsis",
|
|
@@ -1816,7 +2201,7 @@ function renderEditInput(node, propSchema, displayValue, handleValueChange, inpu
|
|
|
1816
2201
|
style: {
|
|
1817
2202
|
color: "var(--vj-boolean, #569cd6)",
|
|
1818
2203
|
fontFamily: "var(--vj-font, monospace)",
|
|
1819
|
-
fontSize:
|
|
2204
|
+
fontSize: "var(--vj-input-font-size, 13px)",
|
|
1820
2205
|
fontStyle: "italic",
|
|
1821
2206
|
flex: 1
|
|
1822
2207
|
},
|
|
@@ -1849,11 +2234,8 @@ function FormView({
|
|
|
1849
2234
|
}) {
|
|
1850
2235
|
const { state, actions } = useStudio();
|
|
1851
2236
|
const rootSchema = state.schema ?? void 0;
|
|
1852
|
-
const
|
|
1853
|
-
const displayNode =
|
|
1854
|
-
const [formSelectedNodeId, setFormSelectedNodeId] = useState7(
|
|
1855
|
-
null
|
|
1856
|
-
);
|
|
2237
|
+
const drillDownNode = state.drillDownNodeId ? state.tree.nodesById.get(state.drillDownNodeId) : null;
|
|
2238
|
+
const displayNode = drillDownNode ?? state.tree.root;
|
|
1857
2239
|
const [editingNodeId, setEditingNodeId] = useState7(null);
|
|
1858
2240
|
const preEditTreeRef = useRef7(null);
|
|
1859
2241
|
const [collapsedIds, setCollapsedIds] = useState7(
|
|
@@ -1861,23 +2243,26 @@ function FormView({
|
|
|
1861
2243
|
);
|
|
1862
2244
|
const containerRef = useRef7(null);
|
|
1863
2245
|
const [isFocused, setIsFocused] = useState7(false);
|
|
1864
|
-
const {
|
|
1865
|
-
dragState,
|
|
1866
|
-
handleDragStart,
|
|
1867
|
-
handleDragOver,
|
|
1868
|
-
handleDragEnd,
|
|
1869
|
-
handleDrop
|
|
1870
|
-
} = useDragDrop();
|
|
1871
2246
|
useEffect6(() => {
|
|
1872
|
-
setFormSelectedNodeId(null);
|
|
1873
2247
|
setEditingNodeId(null);
|
|
1874
2248
|
setCollapsedIds(/* @__PURE__ */ new Set());
|
|
1875
2249
|
}, [displayNode.id]);
|
|
1876
|
-
const visibleNodes =
|
|
2250
|
+
const visibleNodes = useMemo6(
|
|
1877
2251
|
() => getVisibleNodes(displayNode, (id) => !collapsedIds.has(id)),
|
|
1878
2252
|
[displayNode, collapsedIds]
|
|
1879
2253
|
);
|
|
1880
|
-
const {
|
|
2254
|
+
const {
|
|
2255
|
+
dragState,
|
|
2256
|
+
handleDragStart,
|
|
2257
|
+
handleDragOver,
|
|
2258
|
+
handleDragEnd,
|
|
2259
|
+
handleDrop
|
|
2260
|
+
} = useDragDrop(visibleNodes, state.selectedNodeIds);
|
|
2261
|
+
useEffect6(() => {
|
|
2262
|
+
actions.setVisibleNodesOverride(visibleNodes);
|
|
2263
|
+
return () => actions.setVisibleNodesOverride(null);
|
|
2264
|
+
}, [visibleNodes, actions]);
|
|
2265
|
+
const { maxKeyLength, maxDepth } = useMemo6(() => {
|
|
1881
2266
|
let maxKey = 1;
|
|
1882
2267
|
let maxD = 0;
|
|
1883
2268
|
const baseSegments = displayNode.path === "/" ? 0 : displayNode.path.split("/").filter(Boolean).length;
|
|
@@ -1890,10 +2275,20 @@ function FormView({
|
|
|
1890
2275
|
}
|
|
1891
2276
|
return { maxKeyLength: maxKey, maxDepth: maxD };
|
|
1892
2277
|
}, [visibleNodes, displayNode.path, state.tree]);
|
|
1893
|
-
const handleSelect = useCallback6(
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
2278
|
+
const handleSelect = useCallback6(
|
|
2279
|
+
(nodeId, e) => {
|
|
2280
|
+
setEditingNodeId(null);
|
|
2281
|
+
if (e.shiftKey) {
|
|
2282
|
+
actions.setVisibleNodesOverride(visibleNodes);
|
|
2283
|
+
actions.selectNodeRange(nodeId);
|
|
2284
|
+
} else if (e.metaKey || e.ctrlKey) {
|
|
2285
|
+
actions.toggleNodeSelection(nodeId);
|
|
2286
|
+
} else {
|
|
2287
|
+
actions.selectNode(nodeId);
|
|
2288
|
+
}
|
|
2289
|
+
},
|
|
2290
|
+
[actions, visibleNodes]
|
|
2291
|
+
);
|
|
1897
2292
|
const handleToggleCollapse = useCallback6((nodeId) => {
|
|
1898
2293
|
setCollapsedIds((prev) => {
|
|
1899
2294
|
const next = new Set(prev);
|
|
@@ -1941,15 +2336,23 @@ function FormView({
|
|
|
1941
2336
|
}
|
|
1942
2337
|
return;
|
|
1943
2338
|
}
|
|
1944
|
-
|
|
1945
|
-
(n) => n.id ===
|
|
2339
|
+
let currentIndex = visibleNodes.findIndex(
|
|
2340
|
+
(n) => n.id === state.focusedNodeId
|
|
1946
2341
|
);
|
|
2342
|
+
if (currentIndex === -1 && visibleNodes.length > 0) {
|
|
2343
|
+
currentIndex = 0;
|
|
2344
|
+
}
|
|
1947
2345
|
switch (e.key) {
|
|
1948
2346
|
case "ArrowDown": {
|
|
1949
2347
|
e.preventDefault();
|
|
1950
2348
|
const next = visibleNodes[currentIndex + 1];
|
|
1951
2349
|
if (next) {
|
|
1952
|
-
|
|
2350
|
+
if (e.shiftKey) {
|
|
2351
|
+
actions.setVisibleNodesOverride(visibleNodes);
|
|
2352
|
+
actions.selectNodeRange(next.id);
|
|
2353
|
+
} else {
|
|
2354
|
+
actions.selectNode(next.id);
|
|
2355
|
+
}
|
|
1953
2356
|
scrollToNode(next.id);
|
|
1954
2357
|
}
|
|
1955
2358
|
break;
|
|
@@ -1958,7 +2361,12 @@ function FormView({
|
|
|
1958
2361
|
e.preventDefault();
|
|
1959
2362
|
const prev = visibleNodes[currentIndex - 1];
|
|
1960
2363
|
if (prev) {
|
|
1961
|
-
|
|
2364
|
+
if (e.shiftKey) {
|
|
2365
|
+
actions.setVisibleNodesOverride(visibleNodes);
|
|
2366
|
+
actions.selectNodeRange(prev.id);
|
|
2367
|
+
} else {
|
|
2368
|
+
actions.selectNode(prev.id);
|
|
2369
|
+
}
|
|
1962
2370
|
scrollToNode(prev.id);
|
|
1963
2371
|
}
|
|
1964
2372
|
break;
|
|
@@ -1974,7 +2382,7 @@ function FormView({
|
|
|
1974
2382
|
return next;
|
|
1975
2383
|
});
|
|
1976
2384
|
} else if (node.children.length > 0) {
|
|
1977
|
-
|
|
2385
|
+
actions.selectNode(node.children[0].id);
|
|
1978
2386
|
scrollToNode(node.children[0].id);
|
|
1979
2387
|
}
|
|
1980
2388
|
}
|
|
@@ -1996,7 +2404,7 @@ function FormView({
|
|
|
1996
2404
|
(n) => n.id === current.parentId
|
|
1997
2405
|
);
|
|
1998
2406
|
if (parentInVisible) {
|
|
1999
|
-
|
|
2407
|
+
actions.selectNode(parentInVisible.id);
|
|
2000
2408
|
scrollToNode(parentInVisible.id);
|
|
2001
2409
|
}
|
|
2002
2410
|
}
|
|
@@ -2004,30 +2412,54 @@ function FormView({
|
|
|
2004
2412
|
}
|
|
2005
2413
|
case "Enter": {
|
|
2006
2414
|
e.preventDefault();
|
|
2007
|
-
if (
|
|
2415
|
+
if (state.focusedNodeId) {
|
|
2008
2416
|
preEditTreeRef.current = state.tree;
|
|
2009
|
-
|
|
2417
|
+
actions.selectNode(state.focusedNodeId);
|
|
2418
|
+
setEditingNodeId(state.focusedNodeId);
|
|
2419
|
+
}
|
|
2420
|
+
break;
|
|
2421
|
+
}
|
|
2422
|
+
case "a": {
|
|
2423
|
+
if (e.metaKey || e.ctrlKey) {
|
|
2424
|
+
e.preventDefault();
|
|
2425
|
+
const ids = computeSelectAllIds(
|
|
2426
|
+
state.tree,
|
|
2427
|
+
state.focusedNodeId,
|
|
2428
|
+
state.selectedNodeIds
|
|
2429
|
+
);
|
|
2430
|
+
if (ids) {
|
|
2431
|
+
actions.setSelection(
|
|
2432
|
+
state.focusedNodeId,
|
|
2433
|
+
ids,
|
|
2434
|
+
state.focusedNodeId
|
|
2435
|
+
);
|
|
2436
|
+
}
|
|
2010
2437
|
}
|
|
2011
2438
|
break;
|
|
2012
2439
|
}
|
|
2013
2440
|
case "Escape": {
|
|
2014
2441
|
e.preventDefault();
|
|
2015
|
-
|
|
2442
|
+
if (state.selectedNodeIds.size > 1 && state.focusedNodeId) {
|
|
2443
|
+
actions.selectNode(state.focusedNodeId);
|
|
2444
|
+
} else {
|
|
2445
|
+
actions.setSelection(null, /* @__PURE__ */ new Set(), null);
|
|
2446
|
+
}
|
|
2016
2447
|
break;
|
|
2017
2448
|
}
|
|
2018
2449
|
case "Delete":
|
|
2019
2450
|
case "Backspace": {
|
|
2020
2451
|
e.preventDefault();
|
|
2021
|
-
const
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2452
|
+
const { newTree, nextFocusId } = deleteSelectedNodes(
|
|
2453
|
+
state.tree,
|
|
2454
|
+
state.selectedNodeIds,
|
|
2455
|
+
visibleNodes
|
|
2456
|
+
);
|
|
2457
|
+
if (newTree === state.tree) break;
|
|
2458
|
+
actions.setTree(newTree);
|
|
2459
|
+
if (nextFocusId) {
|
|
2460
|
+
actions.selectNode(nextFocusId);
|
|
2461
|
+
} else {
|
|
2462
|
+
actions.setSelection(null, /* @__PURE__ */ new Set(), null);
|
|
2031
2463
|
}
|
|
2032
2464
|
break;
|
|
2033
2465
|
}
|
|
@@ -2035,7 +2467,8 @@ function FormView({
|
|
|
2035
2467
|
},
|
|
2036
2468
|
[
|
|
2037
2469
|
visibleNodes,
|
|
2038
|
-
|
|
2470
|
+
state.focusedNodeId,
|
|
2471
|
+
state.selectedNodeIds,
|
|
2039
2472
|
editingNodeId,
|
|
2040
2473
|
collapsedIds,
|
|
2041
2474
|
scrollToNode,
|
|
@@ -2060,9 +2493,11 @@ function FormView({
|
|
|
2060
2493
|
"div",
|
|
2061
2494
|
{
|
|
2062
2495
|
style: {
|
|
2496
|
+
display: "flex",
|
|
2497
|
+
alignItems: "center",
|
|
2063
2498
|
padding: "4px 8px",
|
|
2064
2499
|
borderBottom: "1px solid var(--vj-border, #333333)",
|
|
2065
|
-
backgroundColor: "var(--vj-bg
|
|
2500
|
+
backgroundColor: "var(--vj-bg, #1e1e1e)",
|
|
2066
2501
|
flexShrink: 0
|
|
2067
2502
|
},
|
|
2068
2503
|
children: /* @__PURE__ */ jsx6(Breadcrumbs, {})
|
|
@@ -2095,7 +2530,6 @@ function FormView({
|
|
|
2095
2530
|
depth: 0,
|
|
2096
2531
|
showDescriptions,
|
|
2097
2532
|
showCounts,
|
|
2098
|
-
formSelectedNodeId,
|
|
2099
2533
|
editingNodeId,
|
|
2100
2534
|
collapsedIds,
|
|
2101
2535
|
maxKeyLength,
|
|
@@ -2165,7 +2599,7 @@ function SearchBar({ className }) {
|
|
|
2165
2599
|
alignItems: "center",
|
|
2166
2600
|
gap: 6,
|
|
2167
2601
|
padding: "4px 8px",
|
|
2168
|
-
backgroundColor: "var(--vj-bg
|
|
2602
|
+
backgroundColor: "var(--vj-bg, #1e1e1e)",
|
|
2169
2603
|
borderBottom: "1px solid var(--vj-border, #333333)"
|
|
2170
2604
|
},
|
|
2171
2605
|
children: [
|
|
@@ -2372,31 +2806,6 @@ function SearchBar({ className }) {
|
|
|
2372
2806
|
|
|
2373
2807
|
// src/json-editor.tsx
|
|
2374
2808
|
import { jsx as jsx8, jsxs as jsxs6 } from "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
2809
|
function JsonEditor({
|
|
2401
2810
|
value,
|
|
2402
2811
|
defaultValue,
|
|
@@ -2723,461 +3132,13 @@ function EditorLayout({
|
|
|
2723
3132
|
] });
|
|
2724
3133
|
}
|
|
2725
3134
|
|
|
2726
|
-
// src/property-editor.tsx
|
|
2727
|
-
import { useState as useState9, useCallback as useCallback9, useRef as useRef10 } from "react";
|
|
2728
|
-
import {
|
|
2729
|
-
setValue as setValue2,
|
|
2730
|
-
setKey as setKey2,
|
|
2731
|
-
addProperty as addProperty2,
|
|
2732
|
-
removeNode as removeNode3,
|
|
2733
|
-
changeType as changeType2,
|
|
2734
|
-
duplicateNode as duplicateNode2,
|
|
2735
|
-
toJson as toJson3,
|
|
2736
|
-
getPropertySchema as getPropertySchema3,
|
|
2737
|
-
resolveRef as resolveRef2
|
|
2738
|
-
} from "@visual-json/core";
|
|
2739
|
-
import { jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
2740
|
-
var ALL_TYPES = [
|
|
2741
|
-
"string",
|
|
2742
|
-
"number",
|
|
2743
|
-
"boolean",
|
|
2744
|
-
"null",
|
|
2745
|
-
"object",
|
|
2746
|
-
"array"
|
|
2747
|
-
];
|
|
2748
|
-
function PropertyRow({ node, schemaProperty }) {
|
|
2749
|
-
const { state, actions } = useStudio();
|
|
2750
|
-
const isContainer = node.type === "object" || node.type === "array";
|
|
2751
|
-
const [hoveredRow, setHoveredRow] = useState9(false);
|
|
2752
|
-
const enumRef = useRef10(null);
|
|
2753
|
-
function handleValueChange(newValue) {
|
|
2754
|
-
let parsed;
|
|
2755
|
-
if (newValue === "null") parsed = null;
|
|
2756
|
-
else if (newValue === "true") parsed = true;
|
|
2757
|
-
else if (newValue === "false") parsed = false;
|
|
2758
|
-
else if ((node.type === "number" || schemaProperty?.type === "number" || schemaProperty?.type === "integer") && !isNaN(Number(newValue)) && newValue.trim() !== "")
|
|
2759
|
-
parsed = Number(newValue);
|
|
2760
|
-
else parsed = newValue;
|
|
2761
|
-
const newTree = setValue2(state.tree, node.id, parsed);
|
|
2762
|
-
actions.setTree(newTree);
|
|
2763
|
-
}
|
|
2764
|
-
function handleKeyChange(newKey) {
|
|
2765
|
-
const newTree = setKey2(state.tree, node.id, newKey);
|
|
2766
|
-
actions.setTree(newTree);
|
|
2767
|
-
}
|
|
2768
|
-
function handleRemove() {
|
|
2769
|
-
const newTree = removeNode3(state.tree, node.id);
|
|
2770
|
-
actions.setTree(newTree);
|
|
2771
|
-
}
|
|
2772
|
-
function handleAddChild() {
|
|
2773
|
-
const key = node.type === "array" ? String(node.children.length) : `key${node.children.length}`;
|
|
2774
|
-
const newTree = addProperty2(state.tree, node.id, key, "");
|
|
2775
|
-
actions.setTree(newTree);
|
|
2776
|
-
}
|
|
2777
|
-
function displayValue() {
|
|
2778
|
-
if (isContainer) {
|
|
2779
|
-
return node.type === "array" ? `[${node.children.length} items]` : `{${node.children.length} keys}`;
|
|
2780
|
-
}
|
|
2781
|
-
if (node.value === null) return "";
|
|
2782
|
-
if (node.value === void 0) return "";
|
|
2783
|
-
if (typeof node.value === "string" && node.value === "") return "";
|
|
2784
|
-
return String(node.value);
|
|
2785
|
-
}
|
|
2786
|
-
const hasEnumValues = schemaProperty?.enum && schemaProperty.enum.length > 0;
|
|
2787
|
-
const description = schemaProperty?.description;
|
|
2788
|
-
return /* @__PURE__ */ jsxs7(
|
|
2789
|
-
"div",
|
|
2790
|
-
{
|
|
2791
|
-
onMouseEnter: () => setHoveredRow(true),
|
|
2792
|
-
onMouseLeave: () => setHoveredRow(false),
|
|
2793
|
-
children: [
|
|
2794
|
-
/* @__PURE__ */ jsxs7(
|
|
2795
|
-
"div",
|
|
2796
|
-
{
|
|
2797
|
-
style: {
|
|
2798
|
-
display: "flex",
|
|
2799
|
-
alignItems: "center",
|
|
2800
|
-
gap: 8,
|
|
2801
|
-
padding: "4px 12px",
|
|
2802
|
-
borderBottom: "1px solid var(--vj-border-subtle, #2a2a2a)",
|
|
2803
|
-
minHeight: 32,
|
|
2804
|
-
backgroundColor: hoveredRow ? "var(--vj-bg-hover, #2a2d2e)" : "transparent"
|
|
2805
|
-
},
|
|
2806
|
-
children: [
|
|
2807
|
-
/* @__PURE__ */ jsx9(
|
|
2808
|
-
"input",
|
|
2809
|
-
{
|
|
2810
|
-
value: node.key,
|
|
2811
|
-
onChange: (e) => handleKeyChange(e.target.value),
|
|
2812
|
-
style: {
|
|
2813
|
-
background: "none",
|
|
2814
|
-
border: "1px solid transparent",
|
|
2815
|
-
borderRadius: 3,
|
|
2816
|
-
color: "var(--vj-text, #cccccc)",
|
|
2817
|
-
fontFamily: "var(--vj-font, monospace)",
|
|
2818
|
-
fontSize: "var(--vj-input-font-size, 13px)",
|
|
2819
|
-
padding: "2px 6px",
|
|
2820
|
-
width: 140,
|
|
2821
|
-
flexShrink: 0
|
|
2822
|
-
}
|
|
2823
|
-
}
|
|
2824
|
-
),
|
|
2825
|
-
!isContainer ? hasEnumValues ? /* @__PURE__ */ jsx9(
|
|
2826
|
-
EnumInput,
|
|
2827
|
-
{
|
|
2828
|
-
enumValues: schemaProperty.enum,
|
|
2829
|
-
value: displayValue(),
|
|
2830
|
-
onValueChange: handleValueChange,
|
|
2831
|
-
inputRef: enumRef,
|
|
2832
|
-
inputStyle: {
|
|
2833
|
-
background: "none",
|
|
2834
|
-
border: "1px solid transparent",
|
|
2835
|
-
borderRadius: 3,
|
|
2836
|
-
color: node.type === "string" ? "var(--vj-string, #ce9178)" : "var(--vj-number, #b5cea8)",
|
|
2837
|
-
fontFamily: "var(--vj-font, monospace)",
|
|
2838
|
-
fontSize: "var(--vj-input-font-size, 13px)",
|
|
2839
|
-
padding: "2px 6px",
|
|
2840
|
-
flex: 1,
|
|
2841
|
-
outline: "none"
|
|
2842
|
-
}
|
|
2843
|
-
}
|
|
2844
|
-
) : /* @__PURE__ */ jsx9(
|
|
2845
|
-
"input",
|
|
2846
|
-
{
|
|
2847
|
-
value: displayValue(),
|
|
2848
|
-
onChange: (e) => handleValueChange(e.target.value),
|
|
2849
|
-
placeholder: "<value>",
|
|
2850
|
-
style: {
|
|
2851
|
-
background: "none",
|
|
2852
|
-
border: "1px solid transparent",
|
|
2853
|
-
borderRadius: 3,
|
|
2854
|
-
color: node.type === "string" ? "var(--vj-string, #ce9178)" : "var(--vj-number, #b5cea8)",
|
|
2855
|
-
fontFamily: "var(--vj-font, monospace)",
|
|
2856
|
-
fontSize: "var(--vj-input-font-size, 13px)",
|
|
2857
|
-
padding: "2px 6px",
|
|
2858
|
-
flex: 1,
|
|
2859
|
-
textAlign: "right"
|
|
2860
|
-
}
|
|
2861
|
-
}
|
|
2862
|
-
) : /* @__PURE__ */ jsx9(
|
|
2863
|
-
"span",
|
|
2864
|
-
{
|
|
2865
|
-
style: {
|
|
2866
|
-
color: "var(--vj-text-dim, #666666)",
|
|
2867
|
-
fontFamily: "var(--vj-font, monospace)",
|
|
2868
|
-
fontSize: 13,
|
|
2869
|
-
flex: 1,
|
|
2870
|
-
textAlign: "right"
|
|
2871
|
-
},
|
|
2872
|
-
children: displayValue()
|
|
2873
|
-
}
|
|
2874
|
-
),
|
|
2875
|
-
/* @__PURE__ */ jsxs7(
|
|
2876
|
-
"div",
|
|
2877
|
-
{
|
|
2878
|
-
style: {
|
|
2879
|
-
display: "flex",
|
|
2880
|
-
gap: 2,
|
|
2881
|
-
opacity: hoveredRow ? 1 : 0,
|
|
2882
|
-
transition: "opacity 0.1s",
|
|
2883
|
-
flexShrink: 0
|
|
2884
|
-
},
|
|
2885
|
-
children: [
|
|
2886
|
-
isContainer && /* @__PURE__ */ jsx9(
|
|
2887
|
-
"button",
|
|
2888
|
-
{
|
|
2889
|
-
onClick: handleAddChild,
|
|
2890
|
-
style: {
|
|
2891
|
-
background: "none",
|
|
2892
|
-
border: "none",
|
|
2893
|
-
color: "var(--vj-text-muted, #888888)",
|
|
2894
|
-
cursor: "pointer",
|
|
2895
|
-
padding: "2px 4px",
|
|
2896
|
-
fontSize: 15,
|
|
2897
|
-
fontFamily: "var(--vj-font, monospace)",
|
|
2898
|
-
borderRadius: 3,
|
|
2899
|
-
lineHeight: 1
|
|
2900
|
-
},
|
|
2901
|
-
title: "Add child",
|
|
2902
|
-
children: "+"
|
|
2903
|
-
}
|
|
2904
|
-
),
|
|
2905
|
-
/* @__PURE__ */ jsx9(
|
|
2906
|
-
"button",
|
|
2907
|
-
{
|
|
2908
|
-
onClick: handleRemove,
|
|
2909
|
-
style: {
|
|
2910
|
-
background: "none",
|
|
2911
|
-
border: "none",
|
|
2912
|
-
color: "var(--vj-text-muted, #888888)",
|
|
2913
|
-
cursor: "pointer",
|
|
2914
|
-
padding: "2px 4px",
|
|
2915
|
-
fontSize: 15,
|
|
2916
|
-
fontFamily: "var(--vj-font, monospace)",
|
|
2917
|
-
borderRadius: 3,
|
|
2918
|
-
lineHeight: 1
|
|
2919
|
-
},
|
|
2920
|
-
title: "Remove",
|
|
2921
|
-
children: "\xD7"
|
|
2922
|
-
}
|
|
2923
|
-
)
|
|
2924
|
-
]
|
|
2925
|
-
}
|
|
2926
|
-
)
|
|
2927
|
-
]
|
|
2928
|
-
}
|
|
2929
|
-
),
|
|
2930
|
-
description && /* @__PURE__ */ jsx9(
|
|
2931
|
-
"div",
|
|
2932
|
-
{
|
|
2933
|
-
style: {
|
|
2934
|
-
padding: "2px 12px 4px 44px",
|
|
2935
|
-
fontSize: 11,
|
|
2936
|
-
color: "var(--vj-text-dim, #666666)",
|
|
2937
|
-
fontFamily: "var(--vj-font, monospace)",
|
|
2938
|
-
borderBottom: "1px solid var(--vj-border-subtle, #2a2a2a)"
|
|
2939
|
-
},
|
|
2940
|
-
children: description
|
|
2941
|
-
}
|
|
2942
|
-
)
|
|
2943
|
-
]
|
|
2944
|
-
}
|
|
2945
|
-
);
|
|
2946
|
-
}
|
|
2947
|
-
function PropertyEditor({ className }) {
|
|
2948
|
-
const { state, actions } = useStudio();
|
|
2949
|
-
const selectedNode = state.selectedNodeId ? state.tree.nodesById.get(state.selectedNodeId) : null;
|
|
2950
|
-
const handleChangeType = useCallback9(
|
|
2951
|
-
(newType) => {
|
|
2952
|
-
if (!selectedNode) return;
|
|
2953
|
-
const newTree = changeType2(state.tree, selectedNode.id, newType);
|
|
2954
|
-
actions.setTree(newTree);
|
|
2955
|
-
},
|
|
2956
|
-
[state.tree, selectedNode, actions]
|
|
2957
|
-
);
|
|
2958
|
-
const handleDuplicate = useCallback9(() => {
|
|
2959
|
-
if (!selectedNode) return;
|
|
2960
|
-
const newTree = duplicateNode2(state.tree, selectedNode.id);
|
|
2961
|
-
actions.setTree(newTree);
|
|
2962
|
-
}, [state.tree, selectedNode, actions]);
|
|
2963
|
-
const handleCopyPath = useCallback9(() => {
|
|
2964
|
-
if (!selectedNode) return;
|
|
2965
|
-
navigator.clipboard.writeText(selectedNode.path).catch(() => {
|
|
2966
|
-
});
|
|
2967
|
-
}, [selectedNode]);
|
|
2968
|
-
const handleCopyValue = useCallback9(() => {
|
|
2969
|
-
if (!selectedNode) return;
|
|
2970
|
-
const value = toJson3(selectedNode);
|
|
2971
|
-
const text = typeof value === "string" ? value : JSON.stringify(value, null, 2);
|
|
2972
|
-
navigator.clipboard.writeText(text).catch(() => {
|
|
2973
|
-
});
|
|
2974
|
-
}, [selectedNode]);
|
|
2975
|
-
if (!selectedNode) {
|
|
2976
|
-
return /* @__PURE__ */ jsx9(
|
|
2977
|
-
"div",
|
|
2978
|
-
{
|
|
2979
|
-
className,
|
|
2980
|
-
style: {
|
|
2981
|
-
display: "flex",
|
|
2982
|
-
alignItems: "center",
|
|
2983
|
-
justifyContent: "center",
|
|
2984
|
-
backgroundColor: "var(--vj-bg, #1e1e1e)",
|
|
2985
|
-
color: "var(--vj-text-dimmer, #555555)",
|
|
2986
|
-
fontFamily: "var(--vj-font, monospace)",
|
|
2987
|
-
fontSize: 13,
|
|
2988
|
-
height: "100%"
|
|
2989
|
-
},
|
|
2990
|
-
children: "Select a node to edit"
|
|
2991
|
-
}
|
|
2992
|
-
);
|
|
2993
|
-
}
|
|
2994
|
-
const isContainer = selectedNode.type === "object" || selectedNode.type === "array";
|
|
2995
|
-
const schema = state.schema ?? void 0;
|
|
2996
|
-
const schemaTitle = schema?.title;
|
|
2997
|
-
function getChildSchema(childKey) {
|
|
2998
|
-
if (!schema || !selectedNode) return void 0;
|
|
2999
|
-
const childPath = selectedNode.path === "/" ? `/${childKey}` : `${selectedNode.path}/${childKey}`;
|
|
3000
|
-
const raw = getPropertySchema3(schema, childPath);
|
|
3001
|
-
return raw ? resolveRef2(raw, schema) : void 0;
|
|
3002
|
-
}
|
|
3003
|
-
function handleAdd() {
|
|
3004
|
-
if (!selectedNode) return;
|
|
3005
|
-
const key = selectedNode.type === "array" ? String(selectedNode.children.length) : `key${selectedNode.children.length}`;
|
|
3006
|
-
const newTree = addProperty2(state.tree, selectedNode.id, key, "");
|
|
3007
|
-
actions.setTree(newTree);
|
|
3008
|
-
}
|
|
3009
|
-
return /* @__PURE__ */ jsxs7(
|
|
3010
|
-
"div",
|
|
3011
|
-
{
|
|
3012
|
-
className,
|
|
3013
|
-
style: {
|
|
3014
|
-
backgroundColor: "var(--vj-bg, #1e1e1e)",
|
|
3015
|
-
color: "var(--vj-text, #cccccc)",
|
|
3016
|
-
overflow: "auto",
|
|
3017
|
-
height: "100%",
|
|
3018
|
-
display: "flex",
|
|
3019
|
-
flexDirection: "column"
|
|
3020
|
-
},
|
|
3021
|
-
children: [
|
|
3022
|
-
/* @__PURE__ */ jsxs7(
|
|
3023
|
-
"div",
|
|
3024
|
-
{
|
|
3025
|
-
style: {
|
|
3026
|
-
display: "flex",
|
|
3027
|
-
alignItems: "center",
|
|
3028
|
-
justifyContent: "space-between",
|
|
3029
|
-
padding: "6px 12px",
|
|
3030
|
-
borderBottom: "1px solid var(--vj-border, #333333)",
|
|
3031
|
-
fontSize: 12,
|
|
3032
|
-
color: "var(--vj-text-muted, #999999)",
|
|
3033
|
-
fontFamily: "var(--vj-font, monospace)",
|
|
3034
|
-
flexShrink: 0,
|
|
3035
|
-
backgroundColor: "var(--vj-bg-panel, #252526)"
|
|
3036
|
-
},
|
|
3037
|
-
children: [
|
|
3038
|
-
/* @__PURE__ */ jsxs7(
|
|
3039
|
-
"div",
|
|
3040
|
-
{
|
|
3041
|
-
style: {
|
|
3042
|
-
display: "flex",
|
|
3043
|
-
flexDirection: "column",
|
|
3044
|
-
gap: 2,
|
|
3045
|
-
flex: 1,
|
|
3046
|
-
minWidth: 0
|
|
3047
|
-
},
|
|
3048
|
-
children: [
|
|
3049
|
-
/* @__PURE__ */ jsx9(Breadcrumbs, {}),
|
|
3050
|
-
schemaTitle && /* @__PURE__ */ jsx9(
|
|
3051
|
-
"span",
|
|
3052
|
-
{
|
|
3053
|
-
style: { fontSize: 10, color: "var(--vj-text-dim, #666666)" },
|
|
3054
|
-
children: schemaTitle
|
|
3055
|
-
}
|
|
3056
|
-
)
|
|
3057
|
-
]
|
|
3058
|
-
}
|
|
3059
|
-
),
|
|
3060
|
-
/* @__PURE__ */ jsxs7(
|
|
3061
|
-
"div",
|
|
3062
|
-
{
|
|
3063
|
-
style: {
|
|
3064
|
-
display: "flex",
|
|
3065
|
-
alignItems: "center",
|
|
3066
|
-
gap: 4,
|
|
3067
|
-
flexShrink: 0
|
|
3068
|
-
},
|
|
3069
|
-
children: [
|
|
3070
|
-
/* @__PURE__ */ jsx9(
|
|
3071
|
-
"select",
|
|
3072
|
-
{
|
|
3073
|
-
value: selectedNode.type,
|
|
3074
|
-
onChange: (e) => handleChangeType(e.target.value),
|
|
3075
|
-
style: {
|
|
3076
|
-
background: "var(--vj-input-bg, #3c3c3c)",
|
|
3077
|
-
border: "1px solid var(--vj-input-border, #555555)",
|
|
3078
|
-
borderRadius: 3,
|
|
3079
|
-
color: "var(--vj-text, #cccccc)",
|
|
3080
|
-
fontSize: "var(--vj-input-font-size, 13px)",
|
|
3081
|
-
fontFamily: "var(--vj-font, monospace)",
|
|
3082
|
-
padding: "1px 4px",
|
|
3083
|
-
cursor: "pointer"
|
|
3084
|
-
},
|
|
3085
|
-
title: "Change type",
|
|
3086
|
-
children: ALL_TYPES.map((t) => /* @__PURE__ */ jsx9("option", { value: t, children: t }, t))
|
|
3087
|
-
}
|
|
3088
|
-
),
|
|
3089
|
-
/* @__PURE__ */ jsx9(
|
|
3090
|
-
"button",
|
|
3091
|
-
{
|
|
3092
|
-
onClick: handleCopyPath,
|
|
3093
|
-
style: actionButtonStyle,
|
|
3094
|
-
title: "Copy path",
|
|
3095
|
-
children: "path"
|
|
3096
|
-
}
|
|
3097
|
-
),
|
|
3098
|
-
/* @__PURE__ */ jsx9(
|
|
3099
|
-
"button",
|
|
3100
|
-
{
|
|
3101
|
-
onClick: handleCopyValue,
|
|
3102
|
-
style: actionButtonStyle,
|
|
3103
|
-
title: "Copy value",
|
|
3104
|
-
children: "value"
|
|
3105
|
-
}
|
|
3106
|
-
),
|
|
3107
|
-
selectedNode.parentId && /* @__PURE__ */ jsx9(
|
|
3108
|
-
"button",
|
|
3109
|
-
{
|
|
3110
|
-
onClick: handleDuplicate,
|
|
3111
|
-
style: actionButtonStyle,
|
|
3112
|
-
title: "Duplicate",
|
|
3113
|
-
children: "dup"
|
|
3114
|
-
}
|
|
3115
|
-
),
|
|
3116
|
-
isContainer && /* @__PURE__ */ jsx9(
|
|
3117
|
-
"button",
|
|
3118
|
-
{
|
|
3119
|
-
onClick: handleAdd,
|
|
3120
|
-
style: {
|
|
3121
|
-
...actionButtonStyle,
|
|
3122
|
-
border: "1px solid var(--vj-input-border, #555555)"
|
|
3123
|
-
},
|
|
3124
|
-
children: "+ Add"
|
|
3125
|
-
}
|
|
3126
|
-
)
|
|
3127
|
-
]
|
|
3128
|
-
}
|
|
3129
|
-
)
|
|
3130
|
-
]
|
|
3131
|
-
}
|
|
3132
|
-
),
|
|
3133
|
-
/* @__PURE__ */ jsx9("div", { style: { flex: 1, overflow: "auto" }, children: isContainer ? selectedNode.children.map((child) => /* @__PURE__ */ jsx9(
|
|
3134
|
-
PropertyRow,
|
|
3135
|
-
{
|
|
3136
|
-
node: child,
|
|
3137
|
-
schemaProperty: getChildSchema(child.key)
|
|
3138
|
-
},
|
|
3139
|
-
child.id
|
|
3140
|
-
)) : /* @__PURE__ */ jsx9(PropertyRow, { node: selectedNode }) })
|
|
3141
|
-
]
|
|
3142
|
-
}
|
|
3143
|
-
);
|
|
3144
|
-
}
|
|
3145
|
-
var actionButtonStyle = {
|
|
3146
|
-
background: "none",
|
|
3147
|
-
border: "none",
|
|
3148
|
-
borderRadius: 3,
|
|
3149
|
-
color: "var(--vj-text-muted, #888888)",
|
|
3150
|
-
cursor: "pointer",
|
|
3151
|
-
padding: "1px 6px",
|
|
3152
|
-
fontSize: 11,
|
|
3153
|
-
fontFamily: "var(--vj-font, monospace)"
|
|
3154
|
-
};
|
|
3155
|
-
|
|
3156
3135
|
// src/diff-view.tsx
|
|
3157
|
-
import { useMemo as
|
|
3136
|
+
import { useMemo as useMemo7 } from "react";
|
|
3158
3137
|
import { computeDiff } from "@visual-json/core";
|
|
3159
|
-
import { Fragment as Fragment3, jsx as
|
|
3160
|
-
var DIFF_COLORS = {
|
|
3161
|
-
added: { bg: "#1e3a1e", marker: "+", label: "#4ec94e" },
|
|
3162
|
-
removed: { bg: "#3a1e1e", marker: "-", label: "#f48771" },
|
|
3163
|
-
changed: { bg: "#3a3a1e", marker: "~", label: "#dcdcaa" }
|
|
3164
|
-
};
|
|
3165
|
-
function formatValue(value) {
|
|
3166
|
-
if (value === void 0) return "";
|
|
3167
|
-
if (value === null) return "null";
|
|
3168
|
-
if (typeof value === "string") return JSON.stringify(value);
|
|
3169
|
-
if (typeof value === "object") {
|
|
3170
|
-
const json = JSON.stringify(value, null, 2);
|
|
3171
|
-
if (json.length > 80) {
|
|
3172
|
-
return JSON.stringify(value).slice(0, 77) + "...";
|
|
3173
|
-
}
|
|
3174
|
-
return json;
|
|
3175
|
-
}
|
|
3176
|
-
return String(value);
|
|
3177
|
-
}
|
|
3138
|
+
import { Fragment as Fragment3, jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
3178
3139
|
function DiffRow({ entry }) {
|
|
3179
3140
|
const colors = DIFF_COLORS[entry.type];
|
|
3180
|
-
return /* @__PURE__ */
|
|
3141
|
+
return /* @__PURE__ */ jsxs7(
|
|
3181
3142
|
"div",
|
|
3182
3143
|
{
|
|
3183
3144
|
style: {
|
|
@@ -3191,7 +3152,7 @@ function DiffRow({ entry }) {
|
|
|
3191
3152
|
gap: 8
|
|
3192
3153
|
},
|
|
3193
3154
|
children: [
|
|
3194
|
-
/* @__PURE__ */
|
|
3155
|
+
/* @__PURE__ */ jsx9(
|
|
3195
3156
|
"span",
|
|
3196
3157
|
{
|
|
3197
3158
|
style: {
|
|
@@ -3204,7 +3165,7 @@ function DiffRow({ entry }) {
|
|
|
3204
3165
|
children: colors.marker
|
|
3205
3166
|
}
|
|
3206
3167
|
),
|
|
3207
|
-
/* @__PURE__ */
|
|
3168
|
+
/* @__PURE__ */ jsx9(
|
|
3208
3169
|
"span",
|
|
3209
3170
|
{
|
|
3210
3171
|
style: {
|
|
@@ -3215,14 +3176,14 @@ function DiffRow({ entry }) {
|
|
|
3215
3176
|
children: entry.path
|
|
3216
3177
|
}
|
|
3217
3178
|
),
|
|
3218
|
-
/* @__PURE__ */
|
|
3219
|
-
entry.type === "changed" && /* @__PURE__ */
|
|
3220
|
-
/* @__PURE__ */
|
|
3221
|
-
/* @__PURE__ */
|
|
3222
|
-
/* @__PURE__ */
|
|
3179
|
+
/* @__PURE__ */ jsxs7("span", { style: { flex: 1, display: "flex", gap: 8, overflow: "hidden" }, children: [
|
|
3180
|
+
entry.type === "changed" && /* @__PURE__ */ jsxs7(Fragment3, { children: [
|
|
3181
|
+
/* @__PURE__ */ jsx9("span", { style: { color: "#f48771", textDecoration: "line-through" }, children: formatValue(entry.oldValue) }),
|
|
3182
|
+
/* @__PURE__ */ jsx9("span", { style: { color: "var(--vj-text-dim, #666666)" }, children: "\u2192" }),
|
|
3183
|
+
/* @__PURE__ */ jsx9("span", { style: { color: "#4ec94e" }, children: formatValue(entry.newValue) })
|
|
3223
3184
|
] }),
|
|
3224
|
-
entry.type === "added" && /* @__PURE__ */
|
|
3225
|
-
entry.type === "removed" && /* @__PURE__ */
|
|
3185
|
+
entry.type === "added" && /* @__PURE__ */ jsx9("span", { style: { color: "#4ec94e" }, children: formatValue(entry.newValue) }),
|
|
3186
|
+
entry.type === "removed" && /* @__PURE__ */ jsx9("span", { style: { color: "#f48771", textDecoration: "line-through" }, children: formatValue(entry.oldValue) })
|
|
3226
3187
|
] })
|
|
3227
3188
|
]
|
|
3228
3189
|
}
|
|
@@ -3233,14 +3194,14 @@ function DiffView({
|
|
|
3233
3194
|
currentJson,
|
|
3234
3195
|
className
|
|
3235
3196
|
}) {
|
|
3236
|
-
const entries =
|
|
3197
|
+
const entries = useMemo7(
|
|
3237
3198
|
() => computeDiff(originalJson, currentJson),
|
|
3238
3199
|
[originalJson, currentJson]
|
|
3239
3200
|
);
|
|
3240
3201
|
const added = entries.filter((e) => e.type === "added").length;
|
|
3241
3202
|
const removed = entries.filter((e) => e.type === "removed").length;
|
|
3242
3203
|
const changed = entries.filter((e) => e.type === "changed").length;
|
|
3243
|
-
return /* @__PURE__ */
|
|
3204
|
+
return /* @__PURE__ */ jsxs7(
|
|
3244
3205
|
"div",
|
|
3245
3206
|
{
|
|
3246
3207
|
className,
|
|
@@ -3253,7 +3214,7 @@ function DiffView({
|
|
|
3253
3214
|
flexDirection: "column"
|
|
3254
3215
|
},
|
|
3255
3216
|
children: [
|
|
3256
|
-
/* @__PURE__ */
|
|
3217
|
+
/* @__PURE__ */ jsxs7(
|
|
3257
3218
|
"div",
|
|
3258
3219
|
{
|
|
3259
3220
|
style: {
|
|
@@ -3268,18 +3229,18 @@ function DiffView({
|
|
|
3268
3229
|
flexShrink: 0
|
|
3269
3230
|
},
|
|
3270
3231
|
children: [
|
|
3271
|
-
/* @__PURE__ */
|
|
3272
|
-
added > 0 && /* @__PURE__ */
|
|
3232
|
+
/* @__PURE__ */ jsx9("span", { style: { color: "var(--vj-text-muted, #999999)" }, children: entries.length === 0 ? "No changes" : `${entries.length} changes` }),
|
|
3233
|
+
added > 0 && /* @__PURE__ */ jsxs7("span", { style: { color: "#4ec94e" }, children: [
|
|
3273
3234
|
"+",
|
|
3274
3235
|
added,
|
|
3275
3236
|
" added"
|
|
3276
3237
|
] }),
|
|
3277
|
-
removed > 0 && /* @__PURE__ */
|
|
3238
|
+
removed > 0 && /* @__PURE__ */ jsxs7("span", { style: { color: "#f48771" }, children: [
|
|
3278
3239
|
"-",
|
|
3279
3240
|
removed,
|
|
3280
3241
|
" removed"
|
|
3281
3242
|
] }),
|
|
3282
|
-
changed > 0 && /* @__PURE__ */
|
|
3243
|
+
changed > 0 && /* @__PURE__ */ jsxs7("span", { style: { color: "#dcdcaa" }, children: [
|
|
3283
3244
|
"~",
|
|
3284
3245
|
changed,
|
|
3285
3246
|
" modified"
|
|
@@ -3287,7 +3248,7 @@ function DiffView({
|
|
|
3287
3248
|
]
|
|
3288
3249
|
}
|
|
3289
3250
|
),
|
|
3290
|
-
/* @__PURE__ */
|
|
3251
|
+
/* @__PURE__ */ jsx9("div", { style: { flex: 1, overflow: "auto" }, children: entries.length === 0 ? /* @__PURE__ */ jsx9(
|
|
3291
3252
|
"div",
|
|
3292
3253
|
{
|
|
3293
3254
|
style: {
|
|
@@ -3301,7 +3262,7 @@ function DiffView({
|
|
|
3301
3262
|
},
|
|
3302
3263
|
children: "No differences detected"
|
|
3303
3264
|
}
|
|
3304
|
-
) : entries.map((entry, i) => /* @__PURE__ */
|
|
3265
|
+
) : entries.map((entry, i) => /* @__PURE__ */ jsx9(DiffRow, { entry }, i)) })
|
|
3305
3266
|
]
|
|
3306
3267
|
}
|
|
3307
3268
|
);
|
|
@@ -3312,7 +3273,6 @@ export {
|
|
|
3312
3273
|
DiffView,
|
|
3313
3274
|
FormView,
|
|
3314
3275
|
JsonEditor,
|
|
3315
|
-
PropertyEditor,
|
|
3316
3276
|
SearchBar,
|
|
3317
3277
|
StudioContext,
|
|
3318
3278
|
TreeView,
|