@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.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,175 @@ var import_react10 = require("react");
|
|
|
40
39
|
|
|
41
40
|
// src/visual-json.tsx
|
|
42
41
|
var import_react2 = require("react");
|
|
42
|
+
var import_core4 = require("@visual-json/core");
|
|
43
|
+
|
|
44
|
+
// ../../@internal/ui/dist/index.mjs
|
|
43
45
|
var import_core = require("@visual-json/core");
|
|
46
|
+
var import_core2 = require("@visual-json/core");
|
|
47
|
+
function getVisibleNodes(root, isExpanded) {
|
|
48
|
+
const result = [];
|
|
49
|
+
function walk(node) {
|
|
50
|
+
result.push(node);
|
|
51
|
+
if (isExpanded(node.id) && (node.type === "object" || node.type === "array")) {
|
|
52
|
+
for (const child of node.children) {
|
|
53
|
+
walk(child);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
walk(root);
|
|
58
|
+
return result;
|
|
59
|
+
}
|
|
60
|
+
var LABEL_FIELDS = ["name", "type", "title", "id", "label", "key"];
|
|
61
|
+
function getDisplayKey(node, state) {
|
|
62
|
+
if (node.parentId === null) return "/";
|
|
63
|
+
const parent = state.nodesById.get(node.parentId);
|
|
64
|
+
if (parent?.type !== "array" || node.type !== "object") return node.key;
|
|
65
|
+
for (const field of LABEL_FIELDS) {
|
|
66
|
+
const child = node.children.find((c) => c.key === field);
|
|
67
|
+
if (child?.value != null && child.value !== "") {
|
|
68
|
+
return String(child.value);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return node.key;
|
|
72
|
+
}
|
|
73
|
+
function collectAllIds(node) {
|
|
74
|
+
const ids = [node.id];
|
|
75
|
+
for (const child of node.children) {
|
|
76
|
+
ids.push(...collectAllIds(child));
|
|
77
|
+
}
|
|
78
|
+
return ids;
|
|
79
|
+
}
|
|
80
|
+
var DEFAULT_CSS_VARS = {
|
|
81
|
+
"--vj-bg": "#1e1e1e",
|
|
82
|
+
"--vj-bg-panel": "#252526",
|
|
83
|
+
"--vj-bg-hover": "#2a2d2e",
|
|
84
|
+
"--vj-bg-selected": "#2a5a1e",
|
|
85
|
+
"--vj-bg-selected-muted": "#2a2d2e",
|
|
86
|
+
"--vj-bg-match": "#3a3520",
|
|
87
|
+
"--vj-bg-match-active": "#51502b",
|
|
88
|
+
"--vj-border": "#333333",
|
|
89
|
+
"--vj-border-subtle": "#2a2a2a",
|
|
90
|
+
"--vj-text": "#cccccc",
|
|
91
|
+
"--vj-text-muted": "#888888",
|
|
92
|
+
"--vj-text-dim": "#666666",
|
|
93
|
+
"--vj-text-dimmer": "#555555",
|
|
94
|
+
"--vj-string": "#ce9178",
|
|
95
|
+
"--vj-number": "#b5cea8",
|
|
96
|
+
"--vj-boolean": "#569cd6",
|
|
97
|
+
"--vj-accent": "#007acc",
|
|
98
|
+
"--vj-accent-muted": "#094771",
|
|
99
|
+
"--vj-input-bg": "#3c3c3c",
|
|
100
|
+
"--vj-input-border": "#555555",
|
|
101
|
+
"--vj-error": "#f48771",
|
|
102
|
+
"--vj-font": "monospace",
|
|
103
|
+
"--vj-input-font-size": "13px"
|
|
104
|
+
};
|
|
105
|
+
var EMPTY_SET = Object.freeze(/* @__PURE__ */ new Set());
|
|
106
|
+
function sortByTreeOrder(root, ids) {
|
|
107
|
+
const result = [];
|
|
108
|
+
function walk(node) {
|
|
109
|
+
if (ids.has(node.id)) result.push(node.id);
|
|
110
|
+
for (const child of node.children) walk(child);
|
|
111
|
+
}
|
|
112
|
+
walk(root);
|
|
113
|
+
return result;
|
|
114
|
+
}
|
|
115
|
+
function computeDrop(tree, drag) {
|
|
116
|
+
const { draggedNodeIds, dropTargetNodeId, dropPosition } = drag;
|
|
117
|
+
if (draggedNodeIds.size === 0 || !dropTargetNodeId || !dropPosition)
|
|
118
|
+
return null;
|
|
119
|
+
const targetNode = tree.nodesById.get(dropTargetNodeId);
|
|
120
|
+
if (!targetNode || !targetNode.parentId) return null;
|
|
121
|
+
for (const id of draggedNodeIds) {
|
|
122
|
+
if ((0, import_core.isDescendant)(tree, dropTargetNodeId, id)) return null;
|
|
123
|
+
}
|
|
124
|
+
const targetParentId = targetNode.parentId;
|
|
125
|
+
const targetParent = tree.nodesById.get(targetParentId);
|
|
126
|
+
if (!targetParent) return null;
|
|
127
|
+
const parentChildren = targetParent.children;
|
|
128
|
+
const orderedDragIds = parentChildren.filter((c) => draggedNodeIds.has(c.id)).map((c) => c.id);
|
|
129
|
+
const allSameParent = orderedDragIds.length === draggedNodeIds.size && [...draggedNodeIds].every((id) => {
|
|
130
|
+
const n = tree.nodesById.get(id);
|
|
131
|
+
return n?.parentId === targetParentId;
|
|
132
|
+
});
|
|
133
|
+
if (allSameParent) {
|
|
134
|
+
return (0, import_core.reorderChildrenMulti)(
|
|
135
|
+
tree,
|
|
136
|
+
targetParentId,
|
|
137
|
+
orderedDragIds,
|
|
138
|
+
dropTargetNodeId,
|
|
139
|
+
dropPosition
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
const orderedIds = sortByTreeOrder(tree.root, draggedNodeIds);
|
|
143
|
+
const draggedNodes = orderedIds.map((id) => tree.nodesById.get(id)).filter((n) => !!n && n.parentId !== null).map((n) => structuredClone(n));
|
|
144
|
+
let newTree = tree;
|
|
145
|
+
for (const id of [...orderedIds].reverse()) {
|
|
146
|
+
if (newTree.nodesById.has(id)) {
|
|
147
|
+
newTree = (0, import_core.removeNode)(newTree, id);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
const updatedTarget = newTree.nodesById.get(dropTargetNodeId);
|
|
151
|
+
if (!updatedTarget || !updatedTarget.parentId) return null;
|
|
152
|
+
const updatedParent = newTree.nodesById.get(updatedTarget.parentId);
|
|
153
|
+
if (!updatedParent) return null;
|
|
154
|
+
let insertIdx = updatedParent.children.findIndex(
|
|
155
|
+
(c) => c.id === dropTargetNodeId
|
|
156
|
+
);
|
|
157
|
+
if (dropPosition === "after") insertIdx++;
|
|
158
|
+
for (let i = 0; i < draggedNodes.length; i++) {
|
|
159
|
+
newTree = (0, import_core.insertNode)(
|
|
160
|
+
newTree,
|
|
161
|
+
updatedParent.id,
|
|
162
|
+
draggedNodes[i],
|
|
163
|
+
insertIdx + i
|
|
164
|
+
);
|
|
165
|
+
}
|
|
166
|
+
return newTree;
|
|
167
|
+
}
|
|
168
|
+
function setMultiDragImage(dataTransfer, count, rootEl) {
|
|
169
|
+
const ghost = document.createElement("div");
|
|
170
|
+
ghost.textContent = `${count} selected`;
|
|
171
|
+
const root = rootEl ?? document.querySelector("[data-form-container], [role='tree']");
|
|
172
|
+
const cs = root ? getComputedStyle(root) : null;
|
|
173
|
+
const bg = cs?.getPropertyValue("--vj-bg-selected").trim() || DEFAULT_CSS_VARS["--vj-bg-selected"];
|
|
174
|
+
const fg = cs?.getPropertyValue("--vj-text-selected").trim() || cs?.getPropertyValue("--vj-text").trim() || DEFAULT_CSS_VARS["--vj-text"];
|
|
175
|
+
const font = cs?.getPropertyValue("--vj-font").trim() || DEFAULT_CSS_VARS["--vj-font"];
|
|
176
|
+
ghost.style.cssText = [
|
|
177
|
+
"position:fixed",
|
|
178
|
+
"top:-1000px",
|
|
179
|
+
"left:-1000px",
|
|
180
|
+
"padding:4px 12px",
|
|
181
|
+
`background:${bg}`,
|
|
182
|
+
`color:${fg}`,
|
|
183
|
+
`font-family:${font}`,
|
|
184
|
+
"font-size:13px",
|
|
185
|
+
"border-radius:4px",
|
|
186
|
+
"white-space:nowrap",
|
|
187
|
+
"pointer-events:none"
|
|
188
|
+
].join(";");
|
|
189
|
+
document.body.appendChild(ghost);
|
|
190
|
+
dataTransfer.setDragImage(ghost, 0, 14);
|
|
191
|
+
requestAnimationFrame(() => ghost.remove());
|
|
192
|
+
}
|
|
193
|
+
var DIFF_COLORS = {
|
|
194
|
+
added: { bg: "#1e3a1e", marker: "+", label: "#4ec94e" },
|
|
195
|
+
removed: { bg: "#3a1e1e", marker: "-", label: "#f48771" },
|
|
196
|
+
changed: { bg: "#3a3a1e", marker: "~", label: "#dcdcaa" }
|
|
197
|
+
};
|
|
198
|
+
function formatValue(value) {
|
|
199
|
+
if (value === void 0) return "";
|
|
200
|
+
if (value === null) return "null";
|
|
201
|
+
if (typeof value === "string") return JSON.stringify(value);
|
|
202
|
+
if (typeof value === "object") {
|
|
203
|
+
const json = JSON.stringify(value, null, 2);
|
|
204
|
+
if (json.length > 80) {
|
|
205
|
+
return JSON.stringify(value).slice(0, 77) + "...";
|
|
206
|
+
}
|
|
207
|
+
return json;
|
|
208
|
+
}
|
|
209
|
+
return String(value);
|
|
210
|
+
}
|
|
44
211
|
|
|
45
212
|
// src/context.ts
|
|
46
213
|
var import_react = require("react");
|
|
@@ -53,27 +220,122 @@ function useStudio() {
|
|
|
53
220
|
return ctx;
|
|
54
221
|
}
|
|
55
222
|
|
|
56
|
-
// src/
|
|
57
|
-
var
|
|
58
|
-
function
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
223
|
+
// src/selection-utils.ts
|
|
224
|
+
var import_core3 = require("@visual-json/core");
|
|
225
|
+
function computeSelectAllIds(tree, focusedNodeId, currentlySelected) {
|
|
226
|
+
if (!focusedNodeId) return null;
|
|
227
|
+
let node = tree.nodesById.get(focusedNodeId);
|
|
228
|
+
if (!node) return null;
|
|
229
|
+
while (node) {
|
|
230
|
+
const parent = node.parentId ? tree.nodesById.get(node.parentId) : void 0;
|
|
231
|
+
const siblings = parent ? parent.children : [tree.root];
|
|
232
|
+
const siblingIds = new Set(siblings.map((s) => s.id));
|
|
233
|
+
const allSelected = siblings.every((s) => currentlySelected.has(s.id));
|
|
234
|
+
if (!allSelected) {
|
|
235
|
+
return siblingIds;
|
|
236
|
+
}
|
|
237
|
+
if (!parent || !parent.parentId) return siblingIds;
|
|
238
|
+
node = parent;
|
|
239
|
+
}
|
|
240
|
+
return null;
|
|
241
|
+
}
|
|
242
|
+
function computeRangeIds(visibleNodes, anchorId, targetId) {
|
|
243
|
+
const anchorIdx = visibleNodes.findIndex((n) => n.id === anchorId);
|
|
244
|
+
const targetIdx = visibleNodes.findIndex((n) => n.id === targetId);
|
|
245
|
+
if (anchorIdx === -1 || targetIdx === -1) return null;
|
|
246
|
+
const start = Math.min(anchorIdx, targetIdx);
|
|
247
|
+
const end = Math.max(anchorIdx, targetIdx);
|
|
248
|
+
const ids = /* @__PURE__ */ new Set();
|
|
249
|
+
for (let i = start; i <= end; i++) {
|
|
250
|
+
ids.add(visibleNodes[i].id);
|
|
62
251
|
}
|
|
63
252
|
return ids;
|
|
64
253
|
}
|
|
254
|
+
function deleteSelectedNodes(tree, selectedIds, visibleNodes) {
|
|
255
|
+
const idsToDelete = [...selectedIds].filter((id) => {
|
|
256
|
+
const node = tree.nodesById.get(id);
|
|
257
|
+
if (!node || node.parentId === null) return false;
|
|
258
|
+
let cur = tree.nodesById.get(node.parentId);
|
|
259
|
+
while (cur) {
|
|
260
|
+
if (selectedIds.has(cur.id)) return false;
|
|
261
|
+
cur = cur.parentId ? tree.nodesById.get(cur.parentId) : void 0;
|
|
262
|
+
}
|
|
263
|
+
return true;
|
|
264
|
+
});
|
|
265
|
+
if (idsToDelete.length === 0) return { newTree: tree, nextFocusId: null };
|
|
266
|
+
const firstDeletedIdx = visibleNodes.findIndex((n) => selectedIds.has(n.id));
|
|
267
|
+
let newTree = tree;
|
|
268
|
+
for (const id of idsToDelete) {
|
|
269
|
+
if (newTree.nodesById.has(id)) {
|
|
270
|
+
newTree = (0, import_core3.removeNode)(newTree, id);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
let nextFocusId = null;
|
|
274
|
+
for (let i = firstDeletedIdx; i < visibleNodes.length; i++) {
|
|
275
|
+
const id = visibleNodes[i].id;
|
|
276
|
+
if (!selectedIds.has(id) && newTree.nodesById.has(id)) {
|
|
277
|
+
nextFocusId = id;
|
|
278
|
+
break;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
if (!nextFocusId) {
|
|
282
|
+
for (let i = firstDeletedIdx - 1; i >= 0; i--) {
|
|
283
|
+
const id = visibleNodes[i].id;
|
|
284
|
+
if (!selectedIds.has(id) && newTree.nodesById.has(id)) {
|
|
285
|
+
nextFocusId = id;
|
|
286
|
+
break;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
return { newTree, nextFocusId };
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// src/visual-json.tsx
|
|
294
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
65
295
|
function VisualJson({
|
|
66
296
|
value,
|
|
67
297
|
onChange,
|
|
68
298
|
schema,
|
|
69
299
|
children
|
|
70
300
|
}) {
|
|
71
|
-
const [tree, setTreeState] = (0, import_react2.useState)(() => (0,
|
|
72
|
-
const [
|
|
301
|
+
const [tree, setTreeState] = (0, import_react2.useState)(() => (0, import_core4.fromJson)(value));
|
|
302
|
+
const [focusedNodeId, setFocusedNodeId] = (0, import_react2.useState)(null);
|
|
303
|
+
const [selectedNodeIds, setSelectedNodeIdsState] = (0, import_react2.useState)(
|
|
304
|
+
() => /* @__PURE__ */ new Set()
|
|
305
|
+
);
|
|
306
|
+
const selectedNodeIdsRef = (0, import_react2.useRef)(/* @__PURE__ */ new Set());
|
|
307
|
+
const setSelectedNodeIds = (0, import_react2.useCallback)((ids) => {
|
|
308
|
+
selectedNodeIdsRef.current = ids;
|
|
309
|
+
setSelectedNodeIdsState(ids);
|
|
310
|
+
}, []);
|
|
311
|
+
const anchorNodeIdRef = (0, import_react2.useRef)(null);
|
|
312
|
+
const [anchorNodeId, setAnchorNodeIdState] = (0, import_react2.useState)(null);
|
|
313
|
+
const [drillDownNodeId, setDrillDownNodeId] = (0, import_react2.useState)(null);
|
|
73
314
|
const [expandedNodeIds, setExpandedNodeIds] = (0, import_react2.useState)(
|
|
74
315
|
() => /* @__PURE__ */ new Set([tree.root.id])
|
|
75
316
|
);
|
|
76
|
-
const
|
|
317
|
+
const setAnchorNodeId = (0, import_react2.useCallback)((id) => {
|
|
318
|
+
anchorNodeIdRef.current = id;
|
|
319
|
+
setAnchorNodeIdState(id);
|
|
320
|
+
}, []);
|
|
321
|
+
const focusSelectAndDrillDown = (0, import_react2.useCallback)(
|
|
322
|
+
(nodeId) => {
|
|
323
|
+
setFocusedNodeId(nodeId);
|
|
324
|
+
setSelectedNodeIds(nodeId ? /* @__PURE__ */ new Set([nodeId]) : /* @__PURE__ */ new Set());
|
|
325
|
+
setAnchorNodeId(nodeId);
|
|
326
|
+
setDrillDownNodeId(nodeId);
|
|
327
|
+
},
|
|
328
|
+
[setSelectedNodeIds, setAnchorNodeId]
|
|
329
|
+
);
|
|
330
|
+
const visibleNodes = (0, import_react2.useMemo)(
|
|
331
|
+
() => getVisibleNodes(tree.root, (id) => expandedNodeIds.has(id)),
|
|
332
|
+
[tree.root, expandedNodeIds]
|
|
333
|
+
);
|
|
334
|
+
const visibleNodesOverrideRef = (0, import_react2.useRef)(null);
|
|
335
|
+
const setVisibleNodesOverride = (0, import_react2.useCallback)((nodes) => {
|
|
336
|
+
visibleNodesOverrideRef.current = nodes;
|
|
337
|
+
}, []);
|
|
338
|
+
const historyRef = (0, import_react2.useRef)(new import_core4.History());
|
|
77
339
|
const isInternalChange = (0, import_react2.useRef)(false);
|
|
78
340
|
const hasMounted = (0, import_react2.useRef)(false);
|
|
79
341
|
const [canUndo, setCanUndo] = (0, import_react2.useState)(false);
|
|
@@ -98,11 +360,11 @@ function VisualJson({
|
|
|
98
360
|
isInternalChange.current = false;
|
|
99
361
|
return;
|
|
100
362
|
}
|
|
101
|
-
const newTree = (0,
|
|
363
|
+
const newTree = (0, import_core4.fromJson)(value);
|
|
102
364
|
setTreeState(newTree);
|
|
103
365
|
setExpandedNodeIds(/* @__PURE__ */ new Set([newTree.root.id]));
|
|
104
|
-
|
|
105
|
-
historyRef.current = new
|
|
366
|
+
focusSelectAndDrillDown(null);
|
|
367
|
+
historyRef.current = new import_core4.History();
|
|
106
368
|
historyRef.current.push(newTree);
|
|
107
369
|
setCanUndo(false);
|
|
108
370
|
setCanRedo(false);
|
|
@@ -118,7 +380,7 @@ function VisualJson({
|
|
|
118
380
|
setCanUndo(historyRef.current.canUndo);
|
|
119
381
|
setCanRedo(historyRef.current.canRedo);
|
|
120
382
|
isInternalChange.current = true;
|
|
121
|
-
onChange?.((0,
|
|
383
|
+
onChange?.((0, import_core4.toJson)(newTree.root));
|
|
122
384
|
},
|
|
123
385
|
[onChange]
|
|
124
386
|
);
|
|
@@ -129,7 +391,7 @@ function VisualJson({
|
|
|
129
391
|
setCanUndo(historyRef.current.canUndo);
|
|
130
392
|
setCanRedo(historyRef.current.canRedo);
|
|
131
393
|
isInternalChange.current = true;
|
|
132
|
-
onChange?.((0,
|
|
394
|
+
onChange?.((0, import_core4.toJson)(prev.root));
|
|
133
395
|
}
|
|
134
396
|
}, [onChange]);
|
|
135
397
|
const redo = (0, import_react2.useCallback)(() => {
|
|
@@ -139,7 +401,7 @@ function VisualJson({
|
|
|
139
401
|
setCanUndo(historyRef.current.canUndo);
|
|
140
402
|
setCanRedo(historyRef.current.canRedo);
|
|
141
403
|
isInternalChange.current = true;
|
|
142
|
-
onChange?.((0,
|
|
404
|
+
onChange?.((0, import_core4.toJson)(next.root));
|
|
143
405
|
}
|
|
144
406
|
}, [onChange]);
|
|
145
407
|
(0, import_react2.useEffect)(() => {
|
|
@@ -159,8 +421,66 @@ function VisualJson({
|
|
|
159
421
|
document.addEventListener("keydown", handleKeyDown);
|
|
160
422
|
return () => document.removeEventListener("keydown", handleKeyDown);
|
|
161
423
|
}, [undo, redo]);
|
|
162
|
-
const selectNode = (0, import_react2.useCallback)(
|
|
163
|
-
|
|
424
|
+
const selectNode = (0, import_react2.useCallback)(
|
|
425
|
+
(nodeId) => {
|
|
426
|
+
setFocusedNodeId(nodeId);
|
|
427
|
+
setSelectedNodeIds(nodeId ? /* @__PURE__ */ new Set([nodeId]) : /* @__PURE__ */ new Set());
|
|
428
|
+
setAnchorNodeId(nodeId);
|
|
429
|
+
},
|
|
430
|
+
[setSelectedNodeIds, setAnchorNodeId]
|
|
431
|
+
);
|
|
432
|
+
const selectAndDrillDown = focusSelectAndDrillDown;
|
|
433
|
+
const toggleNodeSelection = (0, import_react2.useCallback)(
|
|
434
|
+
(nodeId) => {
|
|
435
|
+
const next = new Set(selectedNodeIdsRef.current);
|
|
436
|
+
if (next.has(nodeId)) {
|
|
437
|
+
next.delete(nodeId);
|
|
438
|
+
} else {
|
|
439
|
+
next.add(nodeId);
|
|
440
|
+
}
|
|
441
|
+
setSelectedNodeIds(next);
|
|
442
|
+
if (next.size === 0) {
|
|
443
|
+
setFocusedNodeId(null);
|
|
444
|
+
setAnchorNodeId(null);
|
|
445
|
+
} else {
|
|
446
|
+
setFocusedNodeId(nodeId);
|
|
447
|
+
setAnchorNodeId(nodeId);
|
|
448
|
+
}
|
|
449
|
+
},
|
|
450
|
+
[setSelectedNodeIds, setAnchorNodeId]
|
|
451
|
+
);
|
|
452
|
+
const selectNodeRange = (0, import_react2.useCallback)(
|
|
453
|
+
(toNodeId) => {
|
|
454
|
+
const nodes = visibleNodesOverrideRef.current ?? visibleNodes;
|
|
455
|
+
const anchor = anchorNodeIdRef.current;
|
|
456
|
+
if (!anchor) {
|
|
457
|
+
setFocusedNodeId(toNodeId);
|
|
458
|
+
setSelectedNodeIds(/* @__PURE__ */ new Set([toNodeId]));
|
|
459
|
+
setAnchorNodeId(toNodeId);
|
|
460
|
+
return;
|
|
461
|
+
}
|
|
462
|
+
const rangeIds = computeRangeIds(nodes, anchor, toNodeId);
|
|
463
|
+
if (!rangeIds) {
|
|
464
|
+
setFocusedNodeId(toNodeId);
|
|
465
|
+
setSelectedNodeIds(/* @__PURE__ */ new Set([toNodeId]));
|
|
466
|
+
setAnchorNodeId(toNodeId);
|
|
467
|
+
return;
|
|
468
|
+
}
|
|
469
|
+
setSelectedNodeIds(rangeIds);
|
|
470
|
+
setFocusedNodeId(toNodeId);
|
|
471
|
+
},
|
|
472
|
+
[visibleNodes, setSelectedNodeIds, setAnchorNodeId]
|
|
473
|
+
);
|
|
474
|
+
const setSelection = (0, import_react2.useCallback)(
|
|
475
|
+
(focusedId, newSelectedIds, newAnchorId) => {
|
|
476
|
+
setFocusedNodeId(focusedId);
|
|
477
|
+
setSelectedNodeIds(newSelectedIds);
|
|
478
|
+
setAnchorNodeId(newAnchorId);
|
|
479
|
+
},
|
|
480
|
+
[setSelectedNodeIds, setAnchorNodeId]
|
|
481
|
+
);
|
|
482
|
+
const drillDown = (0, import_react2.useCallback)((nodeId) => {
|
|
483
|
+
setDrillDownNodeId(nodeId);
|
|
164
484
|
}, []);
|
|
165
485
|
const toggleExpand = (0, import_react2.useCallback)((nodeId) => {
|
|
166
486
|
setExpandedNodeIds((prev) => {
|
|
@@ -200,13 +520,14 @@ function VisualJson({
|
|
|
200
520
|
setSearchMatchNodeIds(/* @__PURE__ */ new Set());
|
|
201
521
|
return;
|
|
202
522
|
}
|
|
203
|
-
const matches = (0,
|
|
523
|
+
const matches = (0, import_core4.searchNodes)(tree, query);
|
|
204
524
|
setSearchMatches(matches);
|
|
205
525
|
setSearchMatchIndex(0);
|
|
206
526
|
const matchIds = new Set(matches.map((m) => m.nodeId));
|
|
207
527
|
setSearchMatchNodeIds(matchIds);
|
|
208
528
|
if (matches.length > 0) {
|
|
209
|
-
const
|
|
529
|
+
const firstId = matches[0].nodeId;
|
|
530
|
+
const ancestors = (0, import_core4.getAncestorIds)(
|
|
210
531
|
tree,
|
|
211
532
|
matches.map((m) => m.nodeId)
|
|
212
533
|
);
|
|
@@ -215,7 +536,7 @@ function VisualJson({
|
|
|
215
536
|
for (const id of ancestors) next.add(id);
|
|
216
537
|
return next;
|
|
217
538
|
});
|
|
218
|
-
|
|
539
|
+
focusSelectAndDrillDown(firstId);
|
|
219
540
|
}
|
|
220
541
|
},
|
|
221
542
|
[tree]
|
|
@@ -224,17 +545,17 @@ function VisualJson({
|
|
|
224
545
|
if (searchMatches.length === 0) return;
|
|
225
546
|
const nextIdx = (searchMatchIndex + 1) % searchMatches.length;
|
|
226
547
|
setSearchMatchIndex(nextIdx);
|
|
227
|
-
|
|
228
|
-
}, [searchMatches, searchMatchIndex]);
|
|
548
|
+
focusSelectAndDrillDown(searchMatches[nextIdx].nodeId);
|
|
549
|
+
}, [searchMatches, searchMatchIndex, focusSelectAndDrillDown]);
|
|
229
550
|
const prevSearchMatch = (0, import_react2.useCallback)(() => {
|
|
230
551
|
if (searchMatches.length === 0) return;
|
|
231
552
|
const prevIdx = (searchMatchIndex - 1 + searchMatches.length) % searchMatches.length;
|
|
232
553
|
setSearchMatchIndex(prevIdx);
|
|
233
|
-
|
|
234
|
-
}, [searchMatches, searchMatchIndex]);
|
|
554
|
+
focusSelectAndDrillDown(searchMatches[prevIdx].nodeId);
|
|
555
|
+
}, [searchMatches, searchMatchIndex, focusSelectAndDrillDown]);
|
|
235
556
|
(0, import_react2.useEffect)(() => {
|
|
236
557
|
if (!searchQuery.trim()) return;
|
|
237
|
-
const matches = (0,
|
|
558
|
+
const matches = (0, import_core4.searchNodes)(tree, searchQuery);
|
|
238
559
|
setSearchMatches(matches);
|
|
239
560
|
setSearchMatchIndex(
|
|
240
561
|
(prev) => Math.min(prev, Math.max(matches.length - 1, 0))
|
|
@@ -244,7 +565,10 @@ function VisualJson({
|
|
|
244
565
|
const state = (0, import_react2.useMemo)(
|
|
245
566
|
() => ({
|
|
246
567
|
tree,
|
|
247
|
-
|
|
568
|
+
focusedNodeId,
|
|
569
|
+
selectedNodeIds,
|
|
570
|
+
anchorNodeId,
|
|
571
|
+
drillDownNodeId,
|
|
248
572
|
expandedNodeIds,
|
|
249
573
|
schema: schema ?? null,
|
|
250
574
|
searchQuery,
|
|
@@ -254,7 +578,10 @@ function VisualJson({
|
|
|
254
578
|
}),
|
|
255
579
|
[
|
|
256
580
|
tree,
|
|
257
|
-
|
|
581
|
+
focusedNodeId,
|
|
582
|
+
selectedNodeIds,
|
|
583
|
+
anchorNodeId,
|
|
584
|
+
drillDownNodeId,
|
|
258
585
|
expandedNodeIds,
|
|
259
586
|
schema,
|
|
260
587
|
searchQuery,
|
|
@@ -267,6 +594,12 @@ function VisualJson({
|
|
|
267
594
|
() => ({
|
|
268
595
|
setTree,
|
|
269
596
|
selectNode,
|
|
597
|
+
selectAndDrillDown,
|
|
598
|
+
toggleNodeSelection,
|
|
599
|
+
selectNodeRange,
|
|
600
|
+
setSelection,
|
|
601
|
+
setVisibleNodesOverride,
|
|
602
|
+
drillDown,
|
|
270
603
|
toggleExpand,
|
|
271
604
|
expandNode,
|
|
272
605
|
collapseNode,
|
|
@@ -283,6 +616,12 @@ function VisualJson({
|
|
|
283
616
|
[
|
|
284
617
|
setTree,
|
|
285
618
|
selectNode,
|
|
619
|
+
selectAndDrillDown,
|
|
620
|
+
toggleNodeSelection,
|
|
621
|
+
selectNodeRange,
|
|
622
|
+
setSelection,
|
|
623
|
+
setVisibleNodesOverride,
|
|
624
|
+
drillDown,
|
|
286
625
|
toggleExpand,
|
|
287
626
|
expandNode,
|
|
288
627
|
collapseNode,
|
|
@@ -303,7 +642,7 @@ function VisualJson({
|
|
|
303
642
|
|
|
304
643
|
// src/tree-view.tsx
|
|
305
644
|
var import_react5 = require("react");
|
|
306
|
-
var
|
|
645
|
+
var import_core6 = require("@visual-json/core");
|
|
307
646
|
|
|
308
647
|
// src/context-menu.tsx
|
|
309
648
|
var import_react3 = require("react");
|
|
@@ -402,111 +741,81 @@ function ContextMenu({ x, y, items, onClose }) {
|
|
|
402
741
|
);
|
|
403
742
|
}
|
|
404
743
|
|
|
405
|
-
// src/display-key.ts
|
|
406
|
-
var LABEL_FIELDS = ["name", "type", "title", "id", "label", "key"];
|
|
407
|
-
function getDisplayKey(node, state) {
|
|
408
|
-
if (node.parentId === null) return "/";
|
|
409
|
-
const parent = state.nodesById.get(node.parentId);
|
|
410
|
-
if (parent?.type !== "array" || node.type !== "object") return node.key;
|
|
411
|
-
for (const field of LABEL_FIELDS) {
|
|
412
|
-
const child = node.children.find((c) => c.key === field);
|
|
413
|
-
if (child?.value != null && child.value !== "") {
|
|
414
|
-
return String(child.value);
|
|
415
|
-
}
|
|
416
|
-
}
|
|
417
|
-
return node.key;
|
|
418
|
-
}
|
|
419
|
-
|
|
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
744
|
// src/use-drag-drop.ts
|
|
436
745
|
var import_react4 = require("react");
|
|
437
|
-
var
|
|
746
|
+
var import_core5 = require("@visual-json/core");
|
|
747
|
+
var EMPTY_SET2 = Object.freeze(/* @__PURE__ */ new Set());
|
|
438
748
|
var INITIAL_DRAG_STATE = {
|
|
439
|
-
|
|
749
|
+
draggedNodeIds: EMPTY_SET2,
|
|
440
750
|
dropTargetNodeId: null,
|
|
441
751
|
dropPosition: null
|
|
442
752
|
};
|
|
443
|
-
function
|
|
753
|
+
function setMultiDragImage2(e, count) {
|
|
754
|
+
setMultiDragImage(e.dataTransfer, count);
|
|
755
|
+
}
|
|
756
|
+
function useDragDrop(visibleNodes, selectedNodeIds) {
|
|
444
757
|
const { state, actions } = useStudio();
|
|
445
758
|
const [dragState, setDragState] = (0, import_react4.useState)(INITIAL_DRAG_STATE);
|
|
446
759
|
const dragStateRef = (0, import_react4.useRef)(dragState);
|
|
447
760
|
dragStateRef.current = dragState;
|
|
448
|
-
const
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
761
|
+
const visibleNodeIndexMap = (0, import_react4.useMemo)(() => {
|
|
762
|
+
const map = /* @__PURE__ */ new Map();
|
|
763
|
+
visibleNodes.forEach((n, i) => map.set(n.id, i));
|
|
764
|
+
return map;
|
|
765
|
+
}, [visibleNodes]);
|
|
766
|
+
const handleDragStart = (0, import_react4.useCallback)(
|
|
767
|
+
(nodeId) => {
|
|
768
|
+
let ids;
|
|
769
|
+
if (selectedNodeIds.size > 0 && selectedNodeIds.has(nodeId)) {
|
|
770
|
+
ids = selectedNodeIds;
|
|
771
|
+
} else {
|
|
772
|
+
ids = /* @__PURE__ */ new Set([nodeId]);
|
|
773
|
+
}
|
|
774
|
+
setDragState({
|
|
775
|
+
draggedNodeIds: ids,
|
|
776
|
+
dropTargetNodeId: null,
|
|
777
|
+
dropPosition: null
|
|
778
|
+
});
|
|
779
|
+
},
|
|
780
|
+
[selectedNodeIds]
|
|
781
|
+
);
|
|
782
|
+
const rawDragOver = (0, import_react4.useCallback)(
|
|
456
783
|
(nodeId, position) => {
|
|
784
|
+
const draggedIds = dragStateRef.current.draggedNodeIds;
|
|
785
|
+
for (const draggedId of draggedIds) {
|
|
786
|
+
if (nodeId === draggedId || (0, import_core5.isDescendant)(state.tree, nodeId, draggedId)) {
|
|
787
|
+
return;
|
|
788
|
+
}
|
|
789
|
+
}
|
|
457
790
|
setDragState((prev) => ({
|
|
458
791
|
...prev,
|
|
459
792
|
dropTargetNodeId: nodeId,
|
|
460
793
|
dropPosition: position
|
|
461
794
|
}));
|
|
462
795
|
},
|
|
463
|
-
[]
|
|
796
|
+
[state.tree]
|
|
797
|
+
);
|
|
798
|
+
const handleDragOver = (0, import_react4.useCallback)(
|
|
799
|
+
(nodeId, position) => {
|
|
800
|
+
if (position === "before") {
|
|
801
|
+
const idx = visibleNodeIndexMap.get(nodeId);
|
|
802
|
+
if (idx !== void 0 && idx > 0) {
|
|
803
|
+
rawDragOver(visibleNodes[idx - 1].id, "after");
|
|
804
|
+
return;
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
rawDragOver(nodeId, position);
|
|
808
|
+
},
|
|
809
|
+
[visibleNodes, visibleNodeIndexMap, rawDragOver]
|
|
464
810
|
);
|
|
465
811
|
const handleDragEnd = (0, import_react4.useCallback)(() => {
|
|
466
812
|
setDragState(INITIAL_DRAG_STATE);
|
|
467
813
|
}, []);
|
|
468
814
|
const handleDrop = (0, import_react4.useCallback)(() => {
|
|
469
|
-
const
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
if (!draggedNode || !targetNode) return;
|
|
474
|
-
if (draggedNode.parentId && draggedNode.parentId === targetNode.parentId) {
|
|
475
|
-
const parent = state.tree.nodesById.get(draggedNode.parentId);
|
|
476
|
-
if (parent) {
|
|
477
|
-
const fromIndex = parent.children.findIndex(
|
|
478
|
-
(c) => c.id === draggedNodeId
|
|
479
|
-
);
|
|
480
|
-
let toIndex = parent.children.findIndex(
|
|
481
|
-
(c) => c.id === dropTargetNodeId
|
|
482
|
-
);
|
|
483
|
-
if (dropPosition === "after") toIndex++;
|
|
484
|
-
if (fromIndex < toIndex) toIndex--;
|
|
485
|
-
if (fromIndex !== toIndex && fromIndex >= 0 && toIndex >= 0) {
|
|
486
|
-
const newTree = (0, import_core2.reorderChildren)(
|
|
487
|
-
state.tree,
|
|
488
|
-
parent.id,
|
|
489
|
-
fromIndex,
|
|
490
|
-
toIndex
|
|
491
|
-
);
|
|
492
|
-
actions.setTree(newTree);
|
|
493
|
-
}
|
|
494
|
-
}
|
|
495
|
-
} else if (targetNode.parentId) {
|
|
496
|
-
const newParent = state.tree.nodesById.get(targetNode.parentId);
|
|
497
|
-
if (newParent) {
|
|
498
|
-
let toIndex = newParent.children.findIndex(
|
|
499
|
-
(c) => c.id === dropTargetNodeId
|
|
500
|
-
);
|
|
501
|
-
if (dropPosition === "after") toIndex++;
|
|
502
|
-
const newTree = (0, import_core2.moveNode)(
|
|
503
|
-
state.tree,
|
|
504
|
-
draggedNodeId,
|
|
505
|
-
newParent.id,
|
|
506
|
-
toIndex
|
|
507
|
-
);
|
|
508
|
-
actions.setTree(newTree);
|
|
509
|
-
}
|
|
815
|
+
const currentDragState = dragStateRef.current;
|
|
816
|
+
const newTree = computeDrop(state.tree, currentDragState);
|
|
817
|
+
if (newTree) {
|
|
818
|
+
actions.setTree(newTree);
|
|
510
819
|
}
|
|
511
820
|
setDragState(INITIAL_DRAG_STATE);
|
|
512
821
|
}, [state.tree, actions]);
|
|
@@ -528,6 +837,7 @@ function TreeNodeRow({
|
|
|
528
837
|
showValues,
|
|
529
838
|
showCounts,
|
|
530
839
|
isFocused,
|
|
840
|
+
onSelectRange,
|
|
531
841
|
onDragStart,
|
|
532
842
|
onDragOver,
|
|
533
843
|
onDragEnd,
|
|
@@ -535,19 +845,15 @@ function TreeNodeRow({
|
|
|
535
845
|
onContextMenu
|
|
536
846
|
}) {
|
|
537
847
|
const { state, actions } = useStudio();
|
|
538
|
-
const isSelected = state.
|
|
848
|
+
const isSelected = state.selectedNodeIds.has(node.id);
|
|
539
849
|
const isExpanded = state.expandedNodeIds.has(node.id);
|
|
540
850
|
const isContainer = node.type === "object" || node.type === "array";
|
|
541
851
|
const [hovered, setHovered] = (0, import_react5.useState)(false);
|
|
542
852
|
const isRoot = node.parentId === null;
|
|
543
853
|
const isSearchMatch = state.searchMatchNodeIds.has(node.id);
|
|
544
854
|
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
855
|
const isDragTarget = dragState.dropTargetNodeId === node.id;
|
|
550
|
-
const isDraggedNode = dragState.
|
|
856
|
+
const isDraggedNode = dragState.draggedNodeIds.has(node.id);
|
|
551
857
|
function displayValue() {
|
|
552
858
|
if (isContainer) {
|
|
553
859
|
return node.type === "array" ? `[${node.children.length}]` : `{${node.children.length}}`;
|
|
@@ -563,12 +869,12 @@ function TreeNodeRow({
|
|
|
563
869
|
const position = e.clientY < midY ? "before" : "after";
|
|
564
870
|
onDragOver(node.id, position);
|
|
565
871
|
}
|
|
566
|
-
let
|
|
567
|
-
let
|
|
872
|
+
let borderTopColor = "transparent";
|
|
873
|
+
let borderBottomColor = "transparent";
|
|
568
874
|
if (isDragTarget && dragState.dropPosition === "before") {
|
|
569
|
-
|
|
875
|
+
borderTopColor = "var(--vj-accent, #007acc)";
|
|
570
876
|
} else if (isDragTarget && dragState.dropPosition === "after") {
|
|
571
|
-
|
|
877
|
+
borderBottomColor = "var(--vj-accent, #007acc)";
|
|
572
878
|
}
|
|
573
879
|
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
|
|
574
880
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
@@ -577,13 +883,24 @@ function TreeNodeRow({
|
|
|
577
883
|
role: "treeitem",
|
|
578
884
|
"aria-selected": isSelected,
|
|
579
885
|
"aria-expanded": isContainer ? isExpanded : void 0,
|
|
580
|
-
onClick: () =>
|
|
886
|
+
onClick: (e) => {
|
|
887
|
+
if (e.shiftKey) {
|
|
888
|
+
onSelectRange(node.id);
|
|
889
|
+
} else if (e.metaKey || e.ctrlKey) {
|
|
890
|
+
actions.toggleNodeSelection(node.id);
|
|
891
|
+
} else {
|
|
892
|
+
actions.selectAndDrillDown(node.id);
|
|
893
|
+
}
|
|
894
|
+
},
|
|
581
895
|
onMouseEnter: () => setHovered(true),
|
|
582
896
|
onMouseLeave: () => setHovered(false),
|
|
583
897
|
onContextMenu: (e) => onContextMenu(e, node),
|
|
584
898
|
draggable: !isRoot,
|
|
585
899
|
onDragStart: (e) => {
|
|
586
900
|
e.dataTransfer.effectAllowed = "move";
|
|
901
|
+
if (state.selectedNodeIds.size > 1 && state.selectedNodeIds.has(node.id)) {
|
|
902
|
+
setMultiDragImage2(e, state.selectedNodeIds.size);
|
|
903
|
+
}
|
|
587
904
|
onDragStart(node.id);
|
|
588
905
|
},
|
|
589
906
|
onDragOver: handleDragOverEvent,
|
|
@@ -597,15 +914,16 @@ function TreeNodeRow({
|
|
|
597
914
|
display: "flex",
|
|
598
915
|
alignItems: "center",
|
|
599
916
|
gap: 6,
|
|
600
|
-
padding: "
|
|
917
|
+
padding: "1px 8px",
|
|
601
918
|
paddingLeft: 8 + depth * 16,
|
|
602
919
|
cursor: "pointer",
|
|
603
920
|
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
921
|
minHeight: 28,
|
|
605
922
|
userSelect: "none",
|
|
606
923
|
opacity: isDraggedNode ? 0.4 : 1,
|
|
607
|
-
borderTop
|
|
608
|
-
borderBottom
|
|
924
|
+
borderTop: `2px solid ${borderTopColor}`,
|
|
925
|
+
borderBottom: `2px solid ${borderBottomColor}`,
|
|
926
|
+
boxSizing: "border-box",
|
|
609
927
|
color: isSelected && isFocused ? "var(--vj-text-selected, var(--vj-text, #cccccc))" : "var(--vj-text, #cccccc)"
|
|
610
928
|
},
|
|
611
929
|
children: [
|
|
@@ -688,6 +1006,7 @@ function TreeNodeRow({
|
|
|
688
1006
|
showValues,
|
|
689
1007
|
showCounts,
|
|
690
1008
|
isFocused,
|
|
1009
|
+
onSelectRange,
|
|
691
1010
|
onDragStart,
|
|
692
1011
|
onDragOver,
|
|
693
1012
|
onDragEnd,
|
|
@@ -705,25 +1024,34 @@ function TreeView({
|
|
|
705
1024
|
}) {
|
|
706
1025
|
const { state, actions } = useStudio();
|
|
707
1026
|
const containerRef = (0, import_react5.useRef)(null);
|
|
1027
|
+
const visibleNodes = (0, import_react5.useMemo)(
|
|
1028
|
+
() => getVisibleNodes(state.tree.root, (id) => state.expandedNodeIds.has(id)),
|
|
1029
|
+
[state.tree.root, state.expandedNodeIds]
|
|
1030
|
+
);
|
|
708
1031
|
const {
|
|
709
1032
|
dragState,
|
|
710
1033
|
handleDragStart,
|
|
711
1034
|
handleDragOver,
|
|
712
1035
|
handleDragEnd,
|
|
713
1036
|
handleDrop
|
|
714
|
-
} = useDragDrop();
|
|
715
|
-
const
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
1037
|
+
} = useDragDrop(visibleNodes, state.selectedNodeIds);
|
|
1038
|
+
const handleSelectRange = (0, import_react5.useCallback)(
|
|
1039
|
+
(nodeId) => {
|
|
1040
|
+
actions.setVisibleNodesOverride(visibleNodes);
|
|
1041
|
+
actions.selectNodeRange(nodeId);
|
|
1042
|
+
},
|
|
1043
|
+
[visibleNodes, actions]
|
|
719
1044
|
);
|
|
1045
|
+
const [contextMenu, setContextMenu] = (0, import_react5.useState)(null);
|
|
720
1046
|
const handleContextMenu = (0, import_react5.useCallback)(
|
|
721
1047
|
(e, node) => {
|
|
722
1048
|
e.preventDefault();
|
|
723
|
-
|
|
1049
|
+
if (!state.selectedNodeIds.has(node.id)) {
|
|
1050
|
+
actions.selectAndDrillDown(node.id);
|
|
1051
|
+
}
|
|
724
1052
|
setContextMenu({ x: e.clientX, y: e.clientY, node });
|
|
725
1053
|
},
|
|
726
|
-
[actions]
|
|
1054
|
+
[actions, state.selectedNodeIds]
|
|
727
1055
|
);
|
|
728
1056
|
const buildContextMenuItems = (0, import_react5.useCallback)(
|
|
729
1057
|
(node) => {
|
|
@@ -767,7 +1095,7 @@ function TreeView({
|
|
|
767
1095
|
items.push({
|
|
768
1096
|
label: "Copy value as JSON",
|
|
769
1097
|
action: () => {
|
|
770
|
-
const val = (0,
|
|
1098
|
+
const val = (0, import_core6.toJson)(node);
|
|
771
1099
|
const text = typeof val === "string" ? val : JSON.stringify(val, null, 2);
|
|
772
1100
|
navigator.clipboard.writeText(text).catch(() => {
|
|
773
1101
|
});
|
|
@@ -778,7 +1106,7 @@ function TreeView({
|
|
|
778
1106
|
items.push({
|
|
779
1107
|
label: "Duplicate",
|
|
780
1108
|
action: () => {
|
|
781
|
-
const newTree = (0,
|
|
1109
|
+
const newTree = (0, import_core6.duplicateNode)(state.tree, node.id);
|
|
782
1110
|
actions.setTree(newTree);
|
|
783
1111
|
}
|
|
784
1112
|
});
|
|
@@ -792,7 +1120,7 @@ function TreeView({
|
|
|
792
1120
|
].filter((t) => t !== node.type).map((t) => ({
|
|
793
1121
|
label: `Change to ${t}`,
|
|
794
1122
|
action: () => {
|
|
795
|
-
const newTree = (0,
|
|
1123
|
+
const newTree = (0, import_core6.changeType)(state.tree, node.id, t);
|
|
796
1124
|
actions.setTree(newTree);
|
|
797
1125
|
}
|
|
798
1126
|
}));
|
|
@@ -802,7 +1130,7 @@ function TreeView({
|
|
|
802
1130
|
items.push({
|
|
803
1131
|
label: "Delete",
|
|
804
1132
|
action: () => {
|
|
805
|
-
const newTree = (0,
|
|
1133
|
+
const newTree = (0, import_core6.removeNode)(state.tree, node.id);
|
|
806
1134
|
actions.setTree(newTree);
|
|
807
1135
|
}
|
|
808
1136
|
});
|
|
@@ -814,19 +1142,31 @@ function TreeView({
|
|
|
814
1142
|
const handleKeyDown = (0, import_react5.useCallback)(
|
|
815
1143
|
(e) => {
|
|
816
1144
|
const currentIndex = visibleNodes.findIndex(
|
|
817
|
-
(n) => n.id === state.
|
|
1145
|
+
(n) => n.id === state.focusedNodeId
|
|
818
1146
|
);
|
|
819
1147
|
switch (e.key) {
|
|
820
1148
|
case "ArrowDown": {
|
|
821
1149
|
e.preventDefault();
|
|
822
1150
|
const next = visibleNodes[currentIndex + 1];
|
|
823
|
-
if (next)
|
|
1151
|
+
if (next) {
|
|
1152
|
+
if (e.shiftKey) {
|
|
1153
|
+
handleSelectRange(next.id);
|
|
1154
|
+
} else {
|
|
1155
|
+
actions.selectNode(next.id);
|
|
1156
|
+
}
|
|
1157
|
+
}
|
|
824
1158
|
break;
|
|
825
1159
|
}
|
|
826
1160
|
case "ArrowUp": {
|
|
827
1161
|
e.preventDefault();
|
|
828
1162
|
const prev = visibleNodes[currentIndex - 1];
|
|
829
|
-
if (prev)
|
|
1163
|
+
if (prev) {
|
|
1164
|
+
if (e.shiftKey) {
|
|
1165
|
+
handleSelectRange(prev.id);
|
|
1166
|
+
} else {
|
|
1167
|
+
actions.selectNode(prev.id);
|
|
1168
|
+
}
|
|
1169
|
+
}
|
|
830
1170
|
break;
|
|
831
1171
|
}
|
|
832
1172
|
case "ArrowRight": {
|
|
@@ -853,17 +1193,47 @@ function TreeView({
|
|
|
853
1193
|
}
|
|
854
1194
|
break;
|
|
855
1195
|
}
|
|
1196
|
+
case "a": {
|
|
1197
|
+
if (e.metaKey || e.ctrlKey) {
|
|
1198
|
+
e.preventDefault();
|
|
1199
|
+
const ids = computeSelectAllIds(
|
|
1200
|
+
state.tree,
|
|
1201
|
+
state.focusedNodeId,
|
|
1202
|
+
state.selectedNodeIds
|
|
1203
|
+
);
|
|
1204
|
+
if (ids) {
|
|
1205
|
+
actions.setSelection(
|
|
1206
|
+
state.focusedNodeId,
|
|
1207
|
+
ids,
|
|
1208
|
+
state.focusedNodeId
|
|
1209
|
+
);
|
|
1210
|
+
}
|
|
1211
|
+
}
|
|
1212
|
+
break;
|
|
1213
|
+
}
|
|
1214
|
+
case "Escape": {
|
|
1215
|
+
e.preventDefault();
|
|
1216
|
+
if (state.selectedNodeIds.size > 1 && state.focusedNodeId) {
|
|
1217
|
+
actions.selectNode(state.focusedNodeId);
|
|
1218
|
+
} else {
|
|
1219
|
+
actions.setSelection(null, /* @__PURE__ */ new Set(), null);
|
|
1220
|
+
}
|
|
1221
|
+
break;
|
|
1222
|
+
}
|
|
856
1223
|
case "Delete":
|
|
857
1224
|
case "Backspace": {
|
|
858
1225
|
e.preventDefault();
|
|
859
|
-
const
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
1226
|
+
const { newTree, nextFocusId } = deleteSelectedNodes(
|
|
1227
|
+
state.tree,
|
|
1228
|
+
state.selectedNodeIds,
|
|
1229
|
+
visibleNodes
|
|
1230
|
+
);
|
|
1231
|
+
if (newTree === state.tree) break;
|
|
1232
|
+
actions.setTree(newTree);
|
|
1233
|
+
if (nextFocusId) {
|
|
1234
|
+
actions.selectNode(nextFocusId);
|
|
1235
|
+
} else {
|
|
1236
|
+
actions.setSelection(null, /* @__PURE__ */ new Set(), null);
|
|
867
1237
|
}
|
|
868
1238
|
break;
|
|
869
1239
|
}
|
|
@@ -871,23 +1241,25 @@ function TreeView({
|
|
|
871
1241
|
},
|
|
872
1242
|
[
|
|
873
1243
|
visibleNodes,
|
|
874
|
-
state.
|
|
1244
|
+
state.focusedNodeId,
|
|
1245
|
+
state.selectedNodeIds,
|
|
875
1246
|
state.expandedNodeIds,
|
|
876
1247
|
state.tree,
|
|
877
|
-
actions
|
|
1248
|
+
actions,
|
|
1249
|
+
handleSelectRange
|
|
878
1250
|
]
|
|
879
1251
|
);
|
|
880
1252
|
const [isFocused, setIsFocused] = (0, import_react5.useState)(false);
|
|
881
1253
|
(0, import_react5.useEffect)(() => {
|
|
882
|
-
if (state.
|
|
1254
|
+
if (state.focusedNodeId && containerRef.current) {
|
|
883
1255
|
const el = containerRef.current.querySelector(
|
|
884
|
-
`[data-node-id="${state.
|
|
1256
|
+
`[data-node-id="${state.focusedNodeId}"]`
|
|
885
1257
|
);
|
|
886
1258
|
if (el) {
|
|
887
1259
|
el.scrollIntoView({ block: "nearest" });
|
|
888
1260
|
}
|
|
889
1261
|
}
|
|
890
|
-
}, [state.
|
|
1262
|
+
}, [state.focusedNodeId]);
|
|
891
1263
|
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
|
|
892
1264
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
893
1265
|
"div",
|
|
@@ -917,6 +1289,7 @@ function TreeView({
|
|
|
917
1289
|
showValues,
|
|
918
1290
|
showCounts,
|
|
919
1291
|
isFocused,
|
|
1292
|
+
onSelectRange: handleSelectRange,
|
|
920
1293
|
onDragStart: handleDragStart,
|
|
921
1294
|
onDragOver: handleDragOver,
|
|
922
1295
|
onDragEnd: handleDragEnd,
|
|
@@ -940,7 +1313,7 @@ function TreeView({
|
|
|
940
1313
|
|
|
941
1314
|
// src/form-view.tsx
|
|
942
1315
|
var import_react8 = require("react");
|
|
943
|
-
var
|
|
1316
|
+
var import_core7 = require("@visual-json/core");
|
|
944
1317
|
|
|
945
1318
|
// src/breadcrumbs.tsx
|
|
946
1319
|
var import_react6 = require("react");
|
|
@@ -949,8 +1322,8 @@ var MAX_SUGGESTIONS = 20;
|
|
|
949
1322
|
var DROPDOWN_MAX_HEIGHT = 200;
|
|
950
1323
|
function Breadcrumbs({ className }) {
|
|
951
1324
|
const { state, actions } = useStudio();
|
|
952
|
-
const
|
|
953
|
-
const currentPath =
|
|
1325
|
+
const drillDownNode = state.drillDownNodeId ? state.tree.nodesById.get(state.drillDownNodeId) : null;
|
|
1326
|
+
const currentPath = drillDownNode?.path ?? "/";
|
|
954
1327
|
const [inputValue, setInputValue] = (0, import_react6.useState)(currentPath);
|
|
955
1328
|
const [open, setOpen] = (0, import_react6.useState)(false);
|
|
956
1329
|
const [highlightIndex, setHighlightIndex] = (0, import_react6.useState)(0);
|
|
@@ -980,7 +1353,7 @@ function Breadcrumbs({ className }) {
|
|
|
980
1353
|
(path) => {
|
|
981
1354
|
for (const [id, node] of state.tree.nodesById) {
|
|
982
1355
|
if (node.path === path) {
|
|
983
|
-
actions.
|
|
1356
|
+
actions.selectAndDrillDown(id);
|
|
984
1357
|
break;
|
|
985
1358
|
}
|
|
986
1359
|
}
|
|
@@ -1068,7 +1441,7 @@ function Breadcrumbs({ className }) {
|
|
|
1068
1441
|
style: {
|
|
1069
1442
|
width: "100%",
|
|
1070
1443
|
boxSizing: "border-box",
|
|
1071
|
-
padding: "
|
|
1444
|
+
padding: "3px 0",
|
|
1072
1445
|
fontSize: "var(--vj-input-font-size, 13px)",
|
|
1073
1446
|
fontFamily: "var(--vj-font, monospace)",
|
|
1074
1447
|
color: "var(--vj-text-muted, #999999)",
|
|
@@ -1295,9 +1668,9 @@ function EnumInput({
|
|
|
1295
1668
|
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
1296
1669
|
function getResolvedSchema(schema, rootSchema, path) {
|
|
1297
1670
|
if (!schema) return void 0;
|
|
1298
|
-
const raw = (0,
|
|
1671
|
+
const raw = (0, import_core7.getPropertySchema)(schema, path, rootSchema);
|
|
1299
1672
|
if (!raw) return void 0;
|
|
1300
|
-
return (0,
|
|
1673
|
+
return (0, import_core7.resolveRef)(raw, rootSchema ?? schema);
|
|
1301
1674
|
}
|
|
1302
1675
|
function getValueColor(node) {
|
|
1303
1676
|
if (node.type === "boolean" || node.type === "null")
|
|
@@ -1318,7 +1691,6 @@ function FormField({
|
|
|
1318
1691
|
depth,
|
|
1319
1692
|
showDescriptions,
|
|
1320
1693
|
showCounts,
|
|
1321
|
-
formSelectedNodeId,
|
|
1322
1694
|
editingNodeId,
|
|
1323
1695
|
collapsedIds,
|
|
1324
1696
|
maxKeyLength,
|
|
@@ -1336,26 +1708,26 @@ function FormField({
|
|
|
1336
1708
|
const { state, actions } = useStudio();
|
|
1337
1709
|
const isContainer = node.type === "object" || node.type === "array";
|
|
1338
1710
|
const collapsed = collapsedIds.has(node.id);
|
|
1339
|
-
const isSelected =
|
|
1711
|
+
const isSelected = state.selectedNodeIds.has(node.id);
|
|
1340
1712
|
const isEditing = editingNodeId === node.id;
|
|
1341
1713
|
const propSchema = getResolvedSchema(schema, rootSchema, node.path);
|
|
1342
1714
|
const isRequired = checkRequired(node, schema, rootSchema);
|
|
1343
1715
|
const [hovered, setHovered] = (0, import_react8.useState)(false);
|
|
1344
1716
|
const isRoot = node.parentId === null;
|
|
1345
1717
|
const isDragTarget = dragState.dropTargetNodeId === node.id;
|
|
1346
|
-
const isDraggedNode = dragState.
|
|
1718
|
+
const isDraggedNode = dragState.draggedNodeIds.has(node.id);
|
|
1347
1719
|
function handleDragOverEvent(e) {
|
|
1348
1720
|
e.preventDefault();
|
|
1349
1721
|
const rect = e.currentTarget.getBoundingClientRect();
|
|
1350
1722
|
const midY = rect.top + rect.height / 2;
|
|
1351
1723
|
onDragOver(node.id, e.clientY < midY ? "before" : "after");
|
|
1352
1724
|
}
|
|
1353
|
-
let
|
|
1354
|
-
let
|
|
1725
|
+
let borderTopColor = "transparent";
|
|
1726
|
+
let borderBottomColor = "transparent";
|
|
1355
1727
|
if (isDragTarget && dragState.dropPosition === "before") {
|
|
1356
|
-
|
|
1728
|
+
borderTopColor = "var(--vj-accent, #007acc)";
|
|
1357
1729
|
} else if (isDragTarget && dragState.dropPosition === "after") {
|
|
1358
|
-
|
|
1730
|
+
borderBottomColor = "var(--vj-accent, #007acc)";
|
|
1359
1731
|
}
|
|
1360
1732
|
const valueRef = (0, import_react8.useRef)(null);
|
|
1361
1733
|
const keyRef = (0, import_react8.useRef)(null);
|
|
@@ -1385,29 +1757,28 @@ function FormField({
|
|
|
1385
1757
|
} else {
|
|
1386
1758
|
parsed = newValue;
|
|
1387
1759
|
}
|
|
1388
|
-
const newTree = (0,
|
|
1760
|
+
const newTree = (0, import_core7.setValue)(state.tree, node.id, parsed);
|
|
1389
1761
|
actions.setTree(newTree);
|
|
1390
1762
|
},
|
|
1391
1763
|
[state.tree, node.id, node.type, actions, propSchema]
|
|
1392
1764
|
);
|
|
1393
1765
|
const handleKeyChange = (0, import_react8.useCallback)(
|
|
1394
1766
|
(newKey) => {
|
|
1395
|
-
const newTree = (0,
|
|
1767
|
+
const newTree = (0, import_core7.setKey)(state.tree, node.id, newKey);
|
|
1396
1768
|
actions.setTree(newTree);
|
|
1397
1769
|
},
|
|
1398
1770
|
[state.tree, node.id, actions]
|
|
1399
1771
|
);
|
|
1400
1772
|
const handleRemove = (0, import_react8.useCallback)(() => {
|
|
1401
|
-
const newTree = (0,
|
|
1773
|
+
const newTree = (0, import_core7.removeNode)(state.tree, node.id);
|
|
1402
1774
|
actions.setTree(newTree);
|
|
1403
1775
|
}, [state.tree, node.id, actions]);
|
|
1404
1776
|
const handleAddChild = (0, import_react8.useCallback)(() => {
|
|
1405
1777
|
const key = node.type === "array" ? String(node.children.length) : `newKey${node.children.length}`;
|
|
1406
|
-
const newTree = (0,
|
|
1778
|
+
const newTree = (0, import_core7.addProperty)(state.tree, node.id, key, "");
|
|
1407
1779
|
actions.setTree(newTree);
|
|
1408
1780
|
}, [state.tree, node.id, node.type, node.children.length, actions]);
|
|
1409
1781
|
const description = propSchema?.description;
|
|
1410
|
-
const defaultVal = propSchema?.default;
|
|
1411
1782
|
const isDeprecated = propSchema?.deprecated;
|
|
1412
1783
|
const fieldTitle = propSchema?.title;
|
|
1413
1784
|
const parentIsObject = node.parentId && state.tree.nodesById.get(node.parentId)?.type === "object";
|
|
@@ -1422,6 +1793,9 @@ function FormField({
|
|
|
1422
1793
|
draggable: !isRoot,
|
|
1423
1794
|
onDragStart: (e) => {
|
|
1424
1795
|
e.dataTransfer.effectAllowed = "move";
|
|
1796
|
+
if (state.selectedNodeIds.size > 1 && state.selectedNodeIds.has(node.id)) {
|
|
1797
|
+
setMultiDragImage2(e, state.selectedNodeIds.size);
|
|
1798
|
+
}
|
|
1425
1799
|
onDragStart(node.id);
|
|
1426
1800
|
},
|
|
1427
1801
|
onDragOver: handleDragOverEvent,
|
|
@@ -1434,20 +1808,21 @@ function FormField({
|
|
|
1434
1808
|
display: "flex",
|
|
1435
1809
|
alignItems: "center",
|
|
1436
1810
|
gap: 6,
|
|
1437
|
-
padding: "
|
|
1811
|
+
padding: "1px 8px",
|
|
1438
1812
|
paddingLeft: 8 + depth * 16,
|
|
1439
1813
|
cursor: "pointer",
|
|
1440
1814
|
backgroundColor: rowBg,
|
|
1441
1815
|
color: rowColor,
|
|
1442
1816
|
height: 28,
|
|
1817
|
+
boxSizing: "border-box",
|
|
1443
1818
|
userSelect: "none",
|
|
1444
1819
|
opacity: isDeprecated ? 0.5 : isDraggedNode ? 0.4 : 1,
|
|
1445
|
-
borderTop
|
|
1446
|
-
borderBottom
|
|
1820
|
+
borderTop: `2px solid ${borderTopColor}`,
|
|
1821
|
+
borderBottom: `2px solid ${borderBottomColor}`
|
|
1447
1822
|
},
|
|
1448
1823
|
onClick: (e) => {
|
|
1449
1824
|
e.stopPropagation();
|
|
1450
|
-
onSelect(node.id);
|
|
1825
|
+
onSelect(node.id, e);
|
|
1451
1826
|
},
|
|
1452
1827
|
onDoubleClick: () => onToggleCollapse(node.id),
|
|
1453
1828
|
onMouseEnter: () => setHovered(true),
|
|
@@ -1503,7 +1878,7 @@ function FormField({
|
|
|
1503
1878
|
{
|
|
1504
1879
|
style: {
|
|
1505
1880
|
color: !isRoot && !parentIsObject && !isSelected ? "var(--vj-text-muted, #888888)" : "inherit",
|
|
1506
|
-
fontSize:
|
|
1881
|
+
fontSize: "var(--vj-input-font-size, 13px)",
|
|
1507
1882
|
fontFamily: "var(--vj-font, monospace)",
|
|
1508
1883
|
fontWeight: 500,
|
|
1509
1884
|
flexShrink: 0,
|
|
@@ -1604,7 +1979,6 @@ function FormField({
|
|
|
1604
1979
|
depth: depth + 1,
|
|
1605
1980
|
showDescriptions,
|
|
1606
1981
|
showCounts,
|
|
1607
|
-
formSelectedNodeId,
|
|
1608
1982
|
editingNodeId,
|
|
1609
1983
|
collapsedIds,
|
|
1610
1984
|
maxKeyLength,
|
|
@@ -1632,6 +2006,9 @@ function FormField({
|
|
|
1632
2006
|
draggable: !isRoot,
|
|
1633
2007
|
onDragStart: (e) => {
|
|
1634
2008
|
e.dataTransfer.effectAllowed = "move";
|
|
2009
|
+
if (state.selectedNodeIds.size > 1 && state.selectedNodeIds.has(node.id)) {
|
|
2010
|
+
setMultiDragImage2(e, state.selectedNodeIds.size);
|
|
2011
|
+
}
|
|
1635
2012
|
onDragStart(node.id);
|
|
1636
2013
|
},
|
|
1637
2014
|
onDragOver: handleDragOverEvent,
|
|
@@ -1644,20 +2021,21 @@ function FormField({
|
|
|
1644
2021
|
display: "flex",
|
|
1645
2022
|
alignItems: "center",
|
|
1646
2023
|
gap: 6,
|
|
1647
|
-
padding: "
|
|
2024
|
+
padding: "1px 8px",
|
|
1648
2025
|
paddingLeft: 8 + depth * 16,
|
|
1649
2026
|
cursor: "pointer",
|
|
1650
2027
|
backgroundColor: rowBg,
|
|
1651
2028
|
color: rowColor,
|
|
1652
2029
|
height: 28,
|
|
2030
|
+
boxSizing: "border-box",
|
|
1653
2031
|
userSelect: "none",
|
|
1654
2032
|
opacity: isDeprecated ? 0.5 : isDraggedNode ? 0.4 : 1,
|
|
1655
|
-
borderTop
|
|
1656
|
-
borderBottom
|
|
2033
|
+
borderTop: `2px solid ${borderTopColor}`,
|
|
2034
|
+
borderBottom: `2px solid ${borderBottomColor}`
|
|
1657
2035
|
},
|
|
1658
2036
|
onClick: (e) => {
|
|
1659
2037
|
e.stopPropagation();
|
|
1660
|
-
onSelect(node.id);
|
|
2038
|
+
onSelect(node.id, e);
|
|
1661
2039
|
},
|
|
1662
2040
|
onDoubleClick: () => onStartEditing(node.id),
|
|
1663
2041
|
onMouseEnter: () => setHovered(true),
|
|
@@ -1694,7 +2072,7 @@ function FormField({
|
|
|
1694
2072
|
{
|
|
1695
2073
|
style: {
|
|
1696
2074
|
color: !parentIsObject && !isSelected ? "var(--vj-text-muted, #888888)" : "inherit",
|
|
1697
|
-
fontSize:
|
|
2075
|
+
fontSize: "var(--vj-input-font-size, 13px)",
|
|
1698
2076
|
fontFamily: "var(--vj-font, monospace)",
|
|
1699
2077
|
flexShrink: 0,
|
|
1700
2078
|
display: "inline-block",
|
|
@@ -1738,7 +2116,7 @@ function FormField({
|
|
|
1738
2116
|
{
|
|
1739
2117
|
style: {
|
|
1740
2118
|
color: valueColor,
|
|
1741
|
-
fontSize:
|
|
2119
|
+
fontSize: "var(--vj-input-font-size, 13px)",
|
|
1742
2120
|
fontFamily: "var(--vj-font, monospace)",
|
|
1743
2121
|
overflow: "hidden",
|
|
1744
2122
|
textOverflow: "ellipsis",
|
|
@@ -1816,7 +2194,7 @@ function renderEditInput(node, propSchema, displayValue, handleValueChange, inpu
|
|
|
1816
2194
|
style: {
|
|
1817
2195
|
color: "var(--vj-boolean, #569cd6)",
|
|
1818
2196
|
fontFamily: "var(--vj-font, monospace)",
|
|
1819
|
-
fontSize:
|
|
2197
|
+
fontSize: "var(--vj-input-font-size, 13px)",
|
|
1820
2198
|
fontStyle: "italic",
|
|
1821
2199
|
flex: 1
|
|
1822
2200
|
},
|
|
@@ -1849,11 +2227,8 @@ function FormView({
|
|
|
1849
2227
|
}) {
|
|
1850
2228
|
const { state, actions } = useStudio();
|
|
1851
2229
|
const rootSchema = state.schema ?? void 0;
|
|
1852
|
-
const
|
|
1853
|
-
const displayNode =
|
|
1854
|
-
const [formSelectedNodeId, setFormSelectedNodeId] = (0, import_react8.useState)(
|
|
1855
|
-
null
|
|
1856
|
-
);
|
|
2230
|
+
const drillDownNode = state.drillDownNodeId ? state.tree.nodesById.get(state.drillDownNodeId) : null;
|
|
2231
|
+
const displayNode = drillDownNode ?? state.tree.root;
|
|
1857
2232
|
const [editingNodeId, setEditingNodeId] = (0, import_react8.useState)(null);
|
|
1858
2233
|
const preEditTreeRef = (0, import_react8.useRef)(null);
|
|
1859
2234
|
const [collapsedIds, setCollapsedIds] = (0, import_react8.useState)(
|
|
@@ -1861,15 +2236,7 @@ function FormView({
|
|
|
1861
2236
|
);
|
|
1862
2237
|
const containerRef = (0, import_react8.useRef)(null);
|
|
1863
2238
|
const [isFocused, setIsFocused] = (0, import_react8.useState)(false);
|
|
1864
|
-
const {
|
|
1865
|
-
dragState,
|
|
1866
|
-
handleDragStart,
|
|
1867
|
-
handleDragOver,
|
|
1868
|
-
handleDragEnd,
|
|
1869
|
-
handleDrop
|
|
1870
|
-
} = useDragDrop();
|
|
1871
2239
|
(0, import_react8.useEffect)(() => {
|
|
1872
|
-
setFormSelectedNodeId(null);
|
|
1873
2240
|
setEditingNodeId(null);
|
|
1874
2241
|
setCollapsedIds(/* @__PURE__ */ new Set());
|
|
1875
2242
|
}, [displayNode.id]);
|
|
@@ -1877,6 +2244,17 @@ function FormView({
|
|
|
1877
2244
|
() => getVisibleNodes(displayNode, (id) => !collapsedIds.has(id)),
|
|
1878
2245
|
[displayNode, collapsedIds]
|
|
1879
2246
|
);
|
|
2247
|
+
const {
|
|
2248
|
+
dragState,
|
|
2249
|
+
handleDragStart,
|
|
2250
|
+
handleDragOver,
|
|
2251
|
+
handleDragEnd,
|
|
2252
|
+
handleDrop
|
|
2253
|
+
} = useDragDrop(visibleNodes, state.selectedNodeIds);
|
|
2254
|
+
(0, import_react8.useEffect)(() => {
|
|
2255
|
+
actions.setVisibleNodesOverride(visibleNodes);
|
|
2256
|
+
return () => actions.setVisibleNodesOverride(null);
|
|
2257
|
+
}, [visibleNodes, actions]);
|
|
1880
2258
|
const { maxKeyLength, maxDepth } = (0, import_react8.useMemo)(() => {
|
|
1881
2259
|
let maxKey = 1;
|
|
1882
2260
|
let maxD = 0;
|
|
@@ -1890,10 +2268,20 @@ function FormView({
|
|
|
1890
2268
|
}
|
|
1891
2269
|
return { maxKeyLength: maxKey, maxDepth: maxD };
|
|
1892
2270
|
}, [visibleNodes, displayNode.path, state.tree]);
|
|
1893
|
-
const handleSelect = (0, import_react8.useCallback)(
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
2271
|
+
const handleSelect = (0, import_react8.useCallback)(
|
|
2272
|
+
(nodeId, e) => {
|
|
2273
|
+
setEditingNodeId(null);
|
|
2274
|
+
if (e.shiftKey) {
|
|
2275
|
+
actions.setVisibleNodesOverride(visibleNodes);
|
|
2276
|
+
actions.selectNodeRange(nodeId);
|
|
2277
|
+
} else if (e.metaKey || e.ctrlKey) {
|
|
2278
|
+
actions.toggleNodeSelection(nodeId);
|
|
2279
|
+
} else {
|
|
2280
|
+
actions.selectNode(nodeId);
|
|
2281
|
+
}
|
|
2282
|
+
},
|
|
2283
|
+
[actions, visibleNodes]
|
|
2284
|
+
);
|
|
1897
2285
|
const handleToggleCollapse = (0, import_react8.useCallback)((nodeId) => {
|
|
1898
2286
|
setCollapsedIds((prev) => {
|
|
1899
2287
|
const next = new Set(prev);
|
|
@@ -1941,15 +2329,23 @@ function FormView({
|
|
|
1941
2329
|
}
|
|
1942
2330
|
return;
|
|
1943
2331
|
}
|
|
1944
|
-
|
|
1945
|
-
(n) => n.id ===
|
|
2332
|
+
let currentIndex = visibleNodes.findIndex(
|
|
2333
|
+
(n) => n.id === state.focusedNodeId
|
|
1946
2334
|
);
|
|
2335
|
+
if (currentIndex === -1 && visibleNodes.length > 0) {
|
|
2336
|
+
currentIndex = 0;
|
|
2337
|
+
}
|
|
1947
2338
|
switch (e.key) {
|
|
1948
2339
|
case "ArrowDown": {
|
|
1949
2340
|
e.preventDefault();
|
|
1950
2341
|
const next = visibleNodes[currentIndex + 1];
|
|
1951
2342
|
if (next) {
|
|
1952
|
-
|
|
2343
|
+
if (e.shiftKey) {
|
|
2344
|
+
actions.setVisibleNodesOverride(visibleNodes);
|
|
2345
|
+
actions.selectNodeRange(next.id);
|
|
2346
|
+
} else {
|
|
2347
|
+
actions.selectNode(next.id);
|
|
2348
|
+
}
|
|
1953
2349
|
scrollToNode(next.id);
|
|
1954
2350
|
}
|
|
1955
2351
|
break;
|
|
@@ -1958,7 +2354,12 @@ function FormView({
|
|
|
1958
2354
|
e.preventDefault();
|
|
1959
2355
|
const prev = visibleNodes[currentIndex - 1];
|
|
1960
2356
|
if (prev) {
|
|
1961
|
-
|
|
2357
|
+
if (e.shiftKey) {
|
|
2358
|
+
actions.setVisibleNodesOverride(visibleNodes);
|
|
2359
|
+
actions.selectNodeRange(prev.id);
|
|
2360
|
+
} else {
|
|
2361
|
+
actions.selectNode(prev.id);
|
|
2362
|
+
}
|
|
1962
2363
|
scrollToNode(prev.id);
|
|
1963
2364
|
}
|
|
1964
2365
|
break;
|
|
@@ -1974,7 +2375,7 @@ function FormView({
|
|
|
1974
2375
|
return next;
|
|
1975
2376
|
});
|
|
1976
2377
|
} else if (node.children.length > 0) {
|
|
1977
|
-
|
|
2378
|
+
actions.selectNode(node.children[0].id);
|
|
1978
2379
|
scrollToNode(node.children[0].id);
|
|
1979
2380
|
}
|
|
1980
2381
|
}
|
|
@@ -1996,7 +2397,7 @@ function FormView({
|
|
|
1996
2397
|
(n) => n.id === current.parentId
|
|
1997
2398
|
);
|
|
1998
2399
|
if (parentInVisible) {
|
|
1999
|
-
|
|
2400
|
+
actions.selectNode(parentInVisible.id);
|
|
2000
2401
|
scrollToNode(parentInVisible.id);
|
|
2001
2402
|
}
|
|
2002
2403
|
}
|
|
@@ -2004,30 +2405,54 @@ function FormView({
|
|
|
2004
2405
|
}
|
|
2005
2406
|
case "Enter": {
|
|
2006
2407
|
e.preventDefault();
|
|
2007
|
-
if (
|
|
2408
|
+
if (state.focusedNodeId) {
|
|
2008
2409
|
preEditTreeRef.current = state.tree;
|
|
2009
|
-
|
|
2410
|
+
actions.selectNode(state.focusedNodeId);
|
|
2411
|
+
setEditingNodeId(state.focusedNodeId);
|
|
2412
|
+
}
|
|
2413
|
+
break;
|
|
2414
|
+
}
|
|
2415
|
+
case "a": {
|
|
2416
|
+
if (e.metaKey || e.ctrlKey) {
|
|
2417
|
+
e.preventDefault();
|
|
2418
|
+
const ids = computeSelectAllIds(
|
|
2419
|
+
state.tree,
|
|
2420
|
+
state.focusedNodeId,
|
|
2421
|
+
state.selectedNodeIds
|
|
2422
|
+
);
|
|
2423
|
+
if (ids) {
|
|
2424
|
+
actions.setSelection(
|
|
2425
|
+
state.focusedNodeId,
|
|
2426
|
+
ids,
|
|
2427
|
+
state.focusedNodeId
|
|
2428
|
+
);
|
|
2429
|
+
}
|
|
2010
2430
|
}
|
|
2011
2431
|
break;
|
|
2012
2432
|
}
|
|
2013
2433
|
case "Escape": {
|
|
2014
2434
|
e.preventDefault();
|
|
2015
|
-
|
|
2435
|
+
if (state.selectedNodeIds.size > 1 && state.focusedNodeId) {
|
|
2436
|
+
actions.selectNode(state.focusedNodeId);
|
|
2437
|
+
} else {
|
|
2438
|
+
actions.setSelection(null, /* @__PURE__ */ new Set(), null);
|
|
2439
|
+
}
|
|
2016
2440
|
break;
|
|
2017
2441
|
}
|
|
2018
2442
|
case "Delete":
|
|
2019
2443
|
case "Backspace": {
|
|
2020
2444
|
e.preventDefault();
|
|
2021
|
-
const
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2445
|
+
const { newTree, nextFocusId } = deleteSelectedNodes(
|
|
2446
|
+
state.tree,
|
|
2447
|
+
state.selectedNodeIds,
|
|
2448
|
+
visibleNodes
|
|
2449
|
+
);
|
|
2450
|
+
if (newTree === state.tree) break;
|
|
2451
|
+
actions.setTree(newTree);
|
|
2452
|
+
if (nextFocusId) {
|
|
2453
|
+
actions.selectNode(nextFocusId);
|
|
2454
|
+
} else {
|
|
2455
|
+
actions.setSelection(null, /* @__PURE__ */ new Set(), null);
|
|
2031
2456
|
}
|
|
2032
2457
|
break;
|
|
2033
2458
|
}
|
|
@@ -2035,7 +2460,8 @@ function FormView({
|
|
|
2035
2460
|
},
|
|
2036
2461
|
[
|
|
2037
2462
|
visibleNodes,
|
|
2038
|
-
|
|
2463
|
+
state.focusedNodeId,
|
|
2464
|
+
state.selectedNodeIds,
|
|
2039
2465
|
editingNodeId,
|
|
2040
2466
|
collapsedIds,
|
|
2041
2467
|
scrollToNode,
|
|
@@ -2060,9 +2486,11 @@ function FormView({
|
|
|
2060
2486
|
"div",
|
|
2061
2487
|
{
|
|
2062
2488
|
style: {
|
|
2489
|
+
display: "flex",
|
|
2490
|
+
alignItems: "center",
|
|
2063
2491
|
padding: "4px 8px",
|
|
2064
2492
|
borderBottom: "1px solid var(--vj-border, #333333)",
|
|
2065
|
-
backgroundColor: "var(--vj-bg
|
|
2493
|
+
backgroundColor: "var(--vj-bg, #1e1e1e)",
|
|
2066
2494
|
flexShrink: 0
|
|
2067
2495
|
},
|
|
2068
2496
|
children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Breadcrumbs, {})
|
|
@@ -2095,7 +2523,6 @@ function FormView({
|
|
|
2095
2523
|
depth: 0,
|
|
2096
2524
|
showDescriptions,
|
|
2097
2525
|
showCounts,
|
|
2098
|
-
formSelectedNodeId,
|
|
2099
2526
|
editingNodeId,
|
|
2100
2527
|
collapsedIds,
|
|
2101
2528
|
maxKeyLength,
|
|
@@ -2165,7 +2592,7 @@ function SearchBar({ className }) {
|
|
|
2165
2592
|
alignItems: "center",
|
|
2166
2593
|
gap: 6,
|
|
2167
2594
|
padding: "4px 8px",
|
|
2168
|
-
backgroundColor: "var(--vj-bg
|
|
2595
|
+
backgroundColor: "var(--vj-bg, #1e1e1e)",
|
|
2169
2596
|
borderBottom: "1px solid var(--vj-border, #333333)"
|
|
2170
2597
|
},
|
|
2171
2598
|
children: [
|
|
@@ -2372,31 +2799,6 @@ function SearchBar({ className }) {
|
|
|
2372
2799
|
|
|
2373
2800
|
// src/json-editor.tsx
|
|
2374
2801
|
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
2802
|
function JsonEditor({
|
|
2401
2803
|
value,
|
|
2402
2804
|
defaultValue,
|
|
@@ -2723,451 +3125,13 @@ function EditorLayout({
|
|
|
2723
3125
|
] });
|
|
2724
3126
|
}
|
|
2725
3127
|
|
|
2726
|
-
// src/
|
|
3128
|
+
// src/diff-view.tsx
|
|
2727
3129
|
var import_react11 = require("react");
|
|
2728
|
-
var
|
|
3130
|
+
var import_core8 = require("@visual-json/core");
|
|
2729
3131
|
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
|
-
// src/diff-view.tsx
|
|
3147
|
-
var import_react12 = require("react");
|
|
3148
|
-
var import_core6 = require("@visual-json/core");
|
|
3149
|
-
var import_jsx_runtime10 = require("react/jsx-runtime");
|
|
3150
|
-
var DIFF_COLORS = {
|
|
3151
|
-
added: { bg: "#1e3a1e", marker: "+", label: "#4ec94e" },
|
|
3152
|
-
removed: { bg: "#3a1e1e", marker: "-", label: "#f48771" },
|
|
3153
|
-
changed: { bg: "#3a3a1e", marker: "~", label: "#dcdcaa" }
|
|
3154
|
-
};
|
|
3155
|
-
function formatValue(value) {
|
|
3156
|
-
if (value === void 0) return "";
|
|
3157
|
-
if (value === null) return "null";
|
|
3158
|
-
if (typeof value === "string") return JSON.stringify(value);
|
|
3159
|
-
if (typeof value === "object") {
|
|
3160
|
-
const json = JSON.stringify(value, null, 2);
|
|
3161
|
-
if (json.length > 80) {
|
|
3162
|
-
return JSON.stringify(value).slice(0, 77) + "...";
|
|
3163
|
-
}
|
|
3164
|
-
return json;
|
|
3165
|
-
}
|
|
3166
|
-
return String(value);
|
|
3167
|
-
}
|
|
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,
|
|
3227
|
-
() => (0,
|
|
3190
|
+
const entries = (0, import_react11.useMemo)(
|
|
3191
|
+
() => (0, import_core8.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,
|