@visual-json/react 0.1.0 → 0.1.1

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/dist/index.mjs CHANGED
@@ -1,9 +1,9 @@
1
1
  // src/json-editor.tsx
2
2
  import {
3
- useState as useState7,
4
- useRef as useRef8,
5
- useCallback as useCallback7,
6
- useEffect as useEffect7
3
+ useState as useState8,
4
+ useRef as useRef9,
5
+ useCallback as useCallback8,
6
+ useEffect as useEffect8
7
7
  } from "react";
8
8
 
9
9
  // src/visual-json.tsx
@@ -926,7 +926,7 @@ function TreeView({
926
926
  }
927
927
 
928
928
  // src/form-view.tsx
929
- import { useState as useState6, useCallback as useCallback5, useRef as useRef6, useEffect as useEffect5, useMemo as useMemo4 } from "react";
929
+ import { useState as useState7, useCallback as useCallback6, useRef as useRef7, useEffect as useEffect6, useMemo as useMemo5 } from "react";
930
930
  import {
931
931
  setValue,
932
932
  setKey,
@@ -1063,7 +1063,7 @@ function Breadcrumbs({ className }) {
1063
1063
  width: "100%",
1064
1064
  boxSizing: "border-box",
1065
1065
  padding: "2px 0",
1066
- fontSize: 12,
1066
+ fontSize: "var(--vj-input-font-size, 13px)",
1067
1067
  fontFamily: "var(--vj-font, monospace)",
1068
1068
  color: "var(--vj-text-muted, #999999)",
1069
1069
  background: "transparent",
@@ -1118,8 +1118,181 @@ function Breadcrumbs({ className }) {
1118
1118
  );
1119
1119
  }
1120
1120
 
1121
- // src/form-view.tsx
1121
+ // src/enum-input.tsx
1122
+ import {
1123
+ useState as useState6,
1124
+ useRef as useRef6,
1125
+ useEffect as useEffect5,
1126
+ useCallback as useCallback5,
1127
+ useMemo as useMemo4
1128
+ } from "react";
1122
1129
  import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
1130
+ var DROPDOWN_MAX_HEIGHT2 = 200;
1131
+ function EnumInput({
1132
+ enumValues,
1133
+ value,
1134
+ onValueChange,
1135
+ inputRef,
1136
+ inputStyle
1137
+ }) {
1138
+ const [inputValue, setInputValue] = useState6(value);
1139
+ const [open, setOpen] = useState6(false);
1140
+ const [highlightIndex, setHighlightIndex] = useState6(0);
1141
+ const listRef = useRef6(null);
1142
+ const wrapperRef = useRef6(null);
1143
+ useEffect5(() => {
1144
+ setInputValue(value);
1145
+ }, [value]);
1146
+ const suggestions = useMemo4(
1147
+ () => enumValues.map((v) => String(v)),
1148
+ [enumValues]
1149
+ );
1150
+ useEffect5(() => {
1151
+ setHighlightIndex(0);
1152
+ }, [suggestions]);
1153
+ const selectValue = useCallback5(
1154
+ (val) => {
1155
+ onValueChange(val);
1156
+ setInputValue(val);
1157
+ setOpen(false);
1158
+ },
1159
+ [onValueChange]
1160
+ );
1161
+ const handleKeyDown = useCallback5(
1162
+ (e) => {
1163
+ if (!open) {
1164
+ if (e.key === "ArrowDown" || e.key === "ArrowUp") {
1165
+ e.preventDefault();
1166
+ e.stopPropagation();
1167
+ setOpen(true);
1168
+ }
1169
+ return;
1170
+ }
1171
+ switch (e.key) {
1172
+ case "ArrowDown":
1173
+ e.preventDefault();
1174
+ e.stopPropagation();
1175
+ setHighlightIndex((i) => Math.min(i + 1, suggestions.length - 1));
1176
+ break;
1177
+ case "ArrowUp":
1178
+ e.preventDefault();
1179
+ e.stopPropagation();
1180
+ setHighlightIndex((i) => Math.max(i - 1, 0));
1181
+ break;
1182
+ case "Enter":
1183
+ e.preventDefault();
1184
+ e.stopPropagation();
1185
+ if (suggestions.length > 0 && highlightIndex < suggestions.length) {
1186
+ selectValue(suggestions[highlightIndex]);
1187
+ }
1188
+ break;
1189
+ case "Escape":
1190
+ e.preventDefault();
1191
+ e.stopPropagation();
1192
+ setInputValue(value);
1193
+ setOpen(false);
1194
+ break;
1195
+ case "Tab":
1196
+ setInputValue(value);
1197
+ setOpen(false);
1198
+ break;
1199
+ }
1200
+ },
1201
+ [open, suggestions, highlightIndex, value, selectValue]
1202
+ );
1203
+ useEffect5(() => {
1204
+ const el = listRef.current;
1205
+ if (!el || !open) return;
1206
+ const item = el.children[highlightIndex];
1207
+ item?.scrollIntoView({ block: "nearest" });
1208
+ }, [highlightIndex, open]);
1209
+ useEffect5(() => {
1210
+ function handleClickOutside(e) {
1211
+ if (wrapperRef.current && !wrapperRef.current.contains(e.target)) {
1212
+ setInputValue(value);
1213
+ setOpen(false);
1214
+ }
1215
+ }
1216
+ document.addEventListener("mousedown", handleClickOutside);
1217
+ return () => document.removeEventListener("mousedown", handleClickOutside);
1218
+ }, [value]);
1219
+ return /* @__PURE__ */ jsxs3(
1220
+ "div",
1221
+ {
1222
+ ref: wrapperRef,
1223
+ style: { position: "relative", flex: 1, minWidth: 0 },
1224
+ children: [
1225
+ /* @__PURE__ */ jsx5(
1226
+ "input",
1227
+ {
1228
+ ref: inputRef,
1229
+ value: inputValue,
1230
+ onChange: (e) => {
1231
+ setInputValue(e.target.value);
1232
+ if (!open) setOpen(true);
1233
+ },
1234
+ onFocus: () => setOpen(true),
1235
+ onKeyDown: handleKeyDown,
1236
+ onClick: (e) => e.stopPropagation(),
1237
+ spellCheck: false,
1238
+ autoComplete: "off",
1239
+ style: inputStyle
1240
+ }
1241
+ ),
1242
+ open && suggestions.length > 0 && /* @__PURE__ */ jsx5(
1243
+ "div",
1244
+ {
1245
+ ref: listRef,
1246
+ style: {
1247
+ position: "absolute",
1248
+ top: "calc(100% + 4px)",
1249
+ left: -32,
1250
+ right: 0,
1251
+ zIndex: 50,
1252
+ maxHeight: DROPDOWN_MAX_HEIGHT2,
1253
+ overflowY: "auto",
1254
+ backgroundColor: "var(--vj-bg-panel, #252526)",
1255
+ border: "1px solid var(--vj-border, #333333)",
1256
+ boxShadow: "0 4px 12px rgba(0,0,0,0.3)"
1257
+ },
1258
+ children: suggestions.map((s, i) => /* @__PURE__ */ jsxs3(
1259
+ "div",
1260
+ {
1261
+ onMouseDown: (e) => {
1262
+ e.preventDefault();
1263
+ selectValue(s);
1264
+ },
1265
+ onMouseEnter: () => setHighlightIndex(i),
1266
+ style: {
1267
+ padding: "4px 12px",
1268
+ fontSize: 13,
1269
+ fontFamily: "var(--vj-font, monospace)",
1270
+ display: "flex",
1271
+ alignItems: "center",
1272
+ gap: 6,
1273
+ color: i === highlightIndex ? "var(--vj-text, #cccccc)" : "var(--vj-text-muted, #888888)",
1274
+ backgroundColor: i === highlightIndex ? "var(--vj-bg-hover, #2a2d2e)" : "transparent",
1275
+ cursor: "pointer",
1276
+ whiteSpace: "nowrap",
1277
+ overflow: "hidden",
1278
+ textOverflow: "ellipsis"
1279
+ },
1280
+ children: [
1281
+ /* @__PURE__ */ jsx5("span", { style: { width: 14, flexShrink: 0, fontSize: 12 }, children: s === value ? "\u2713" : "" }),
1282
+ s
1283
+ ]
1284
+ },
1285
+ s
1286
+ ))
1287
+ }
1288
+ )
1289
+ ]
1290
+ }
1291
+ );
1292
+ }
1293
+
1294
+ // src/form-view.tsx
1295
+ import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
1123
1296
  function getResolvedSchema(schema, rootSchema, path) {
1124
1297
  if (!schema) return void 0;
1125
1298
  const raw = getPropertySchema2(schema, path, rootSchema);
@@ -1167,7 +1340,7 @@ function FormField({
1167
1340
  const isEditing = editingNodeId === node.id;
1168
1341
  const propSchema = getResolvedSchema(schema, rootSchema, node.path);
1169
1342
  const isRequired = checkRequired(node, schema, rootSchema);
1170
- const [hovered, setHovered] = useState6(false);
1343
+ const [hovered, setHovered] = useState7(false);
1171
1344
  const isRoot = node.parentId === null;
1172
1345
  const isDragTarget = dragState.dropTargetNodeId === node.id;
1173
1346
  const isDraggedNode = dragState.draggedNodeId === node.id;
@@ -1184,9 +1357,9 @@ function FormField({
1184
1357
  } else if (isDragTarget && dragState.dropPosition === "after") {
1185
1358
  borderBottom = "2px solid var(--vj-accent, #007acc)";
1186
1359
  }
1187
- const valueRef = useRef6(null);
1188
- const keyRef = useRef6(null);
1189
- useEffect5(() => {
1360
+ const valueRef = useRef7(null);
1361
+ const keyRef = useRef7(null);
1362
+ useEffect6(() => {
1190
1363
  if (!isEditing) return;
1191
1364
  if (!isContainer) {
1192
1365
  const hasValue = node.value !== null && node.value !== void 0 && node.value !== "";
@@ -1199,7 +1372,7 @@ function FormField({
1199
1372
  keyRef.current.focus();
1200
1373
  }
1201
1374
  }, [isEditing, isContainer, node.value]);
1202
- const handleValueChange = useCallback5(
1375
+ const handleValueChange = useCallback6(
1203
1376
  (newValue) => {
1204
1377
  let parsed;
1205
1378
  if (propSchema?.type === "boolean" || newValue === "true" || newValue === "false") {
@@ -1217,18 +1390,18 @@ function FormField({
1217
1390
  },
1218
1391
  [state.tree, node.id, node.type, actions, propSchema]
1219
1392
  );
1220
- const handleKeyChange = useCallback5(
1393
+ const handleKeyChange = useCallback6(
1221
1394
  (newKey) => {
1222
1395
  const newTree = setKey(state.tree, node.id, newKey);
1223
1396
  actions.setTree(newTree);
1224
1397
  },
1225
1398
  [state.tree, node.id, actions]
1226
1399
  );
1227
- const handleRemove = useCallback5(() => {
1400
+ const handleRemove = useCallback6(() => {
1228
1401
  const newTree = removeNode2(state.tree, node.id);
1229
1402
  actions.setTree(newTree);
1230
1403
  }, [state.tree, node.id, actions]);
1231
- const handleAddChild = useCallback5(() => {
1404
+ const handleAddChild = useCallback6(() => {
1232
1405
  const key = node.type === "array" ? String(node.children.length) : `newKey${node.children.length}`;
1233
1406
  const newTree = addProperty(state.tree, node.id, key, "");
1234
1407
  actions.setTree(newTree);
@@ -1241,8 +1414,8 @@ function FormField({
1241
1414
  const rowBg = isSelected ? isFocused ? "var(--vj-bg-selected, #2a5a1e)" : "var(--vj-bg-selected-muted, var(--vj-bg-hover, #2a2d2e))" : hovered ? "var(--vj-bg-hover, #2a2d2e)" : "transparent";
1242
1415
  const rowColor = isSelected && isFocused ? "var(--vj-text-selected, var(--vj-text, #cccccc))" : "var(--vj-text, #cccccc)";
1243
1416
  if (isContainer) {
1244
- return /* @__PURE__ */ jsxs3("div", { children: [
1245
- /* @__PURE__ */ jsxs3(
1417
+ return /* @__PURE__ */ jsxs4("div", { children: [
1418
+ /* @__PURE__ */ jsxs4(
1246
1419
  "div",
1247
1420
  {
1248
1421
  "data-form-node-id": node.id,
@@ -1280,7 +1453,7 @@ function FormField({
1280
1453
  onMouseEnter: () => setHovered(true),
1281
1454
  onMouseLeave: () => setHovered(false),
1282
1455
  children: [
1283
- /* @__PURE__ */ jsx5(
1456
+ /* @__PURE__ */ jsx6(
1284
1457
  "button",
1285
1458
  {
1286
1459
  onClick: (e) => {
@@ -1305,7 +1478,7 @@ function FormField({
1305
1478
  children: "\u25B6"
1306
1479
  }
1307
1480
  ),
1308
- isEditing && !isRoot ? /* @__PURE__ */ jsx5(
1481
+ isEditing && !isRoot ? /* @__PURE__ */ jsx6(
1309
1482
  "input",
1310
1483
  {
1311
1484
  ref: keyRef,
@@ -1317,7 +1490,7 @@ function FormField({
1317
1490
  border: "none",
1318
1491
  color: "inherit",
1319
1492
  fontFamily: "var(--vj-font, monospace)",
1320
- fontSize: 13,
1493
+ fontSize: "var(--vj-input-font-size, 13px)",
1321
1494
  fontWeight: 500,
1322
1495
  padding: 0,
1323
1496
  outline: "none",
@@ -1325,7 +1498,7 @@ function FormField({
1325
1498
  width: `calc(${(maxDepth - depth) * 16}px + ${maxKeyLength}ch)`
1326
1499
  }
1327
1500
  }
1328
- ) : /* @__PURE__ */ jsx5(
1501
+ ) : /* @__PURE__ */ jsx6(
1329
1502
  "span",
1330
1503
  {
1331
1504
  style: {
@@ -1340,7 +1513,7 @@ function FormField({
1340
1513
  children: isRoot ? "/" : getDisplayKey(node, state.tree)
1341
1514
  }
1342
1515
  ),
1343
- showDescriptions && fieldTitle && !isSelected && /* @__PURE__ */ jsx5(
1516
+ showDescriptions && fieldTitle && !isSelected && /* @__PURE__ */ jsx6(
1344
1517
  "span",
1345
1518
  {
1346
1519
  style: {
@@ -1351,7 +1524,7 @@ function FormField({
1351
1524
  children: fieldTitle
1352
1525
  }
1353
1526
  ),
1354
- hovered && /* @__PURE__ */ jsxs3(
1527
+ hovered && /* @__PURE__ */ jsxs4(
1355
1528
  "button",
1356
1529
  {
1357
1530
  onClick: (e) => {
@@ -1373,7 +1546,7 @@ function FormField({
1373
1546
  ]
1374
1547
  }
1375
1548
  ),
1376
- showCounts && /* @__PURE__ */ jsx5(
1549
+ showCounts && /* @__PURE__ */ jsx6(
1377
1550
  "span",
1378
1551
  {
1379
1552
  style: {
@@ -1385,7 +1558,7 @@ function FormField({
1385
1558
  children: node.type === "array" ? `${node.children.length} items` : `${node.children.length} properties`
1386
1559
  }
1387
1560
  ),
1388
- !isRoot && isEditing && /* @__PURE__ */ jsx5(
1561
+ !isRoot && isEditing && /* @__PURE__ */ jsx6(
1389
1562
  "button",
1390
1563
  {
1391
1564
  onClick: (e) => {
@@ -1409,7 +1582,7 @@ function FormField({
1409
1582
  ]
1410
1583
  }
1411
1584
  ),
1412
- showDescriptions && description && /* @__PURE__ */ jsx5(
1585
+ showDescriptions && description && /* @__PURE__ */ jsx6(
1413
1586
  "div",
1414
1587
  {
1415
1588
  style: {
@@ -1422,7 +1595,7 @@ function FormField({
1422
1595
  children: description
1423
1596
  }
1424
1597
  ),
1425
- !collapsed && /* @__PURE__ */ jsx5("div", { children: node.children.map((child) => /* @__PURE__ */ jsx5(
1598
+ !collapsed && /* @__PURE__ */ jsx6("div", { children: node.children.map((child) => /* @__PURE__ */ jsx6(
1426
1599
  FormField,
1427
1600
  {
1428
1601
  node: child,
@@ -1452,7 +1625,7 @@ function FormField({
1452
1625
  }
1453
1626
  const displayValue = getDisplayValue(node);
1454
1627
  const valueColor = getValueColor(node);
1455
- return /* @__PURE__ */ jsxs3(
1628
+ return /* @__PURE__ */ jsxs4(
1456
1629
  "div",
1457
1630
  {
1458
1631
  "data-form-node-id": node.id,
@@ -1490,8 +1663,8 @@ function FormField({
1490
1663
  onMouseEnter: () => setHovered(true),
1491
1664
  onMouseLeave: () => setHovered(false),
1492
1665
  children: [
1493
- /* @__PURE__ */ jsx5("span", { style: { width: 16, flexShrink: 0 } }),
1494
- isEditing && parentIsObject ? /* @__PURE__ */ jsx5(
1666
+ /* @__PURE__ */ jsx6("span", { style: { width: 16, flexShrink: 0 } }),
1667
+ isEditing && parentIsObject ? /* @__PURE__ */ jsx6(
1495
1668
  "input",
1496
1669
  {
1497
1670
  ref: keyRef,
@@ -1509,14 +1682,14 @@ function FormField({
1509
1682
  border: "none",
1510
1683
  color: "inherit",
1511
1684
  fontFamily: "var(--vj-font, monospace)",
1512
- fontSize: 13,
1685
+ fontSize: "var(--vj-input-font-size, 13px)",
1513
1686
  padding: 0,
1514
1687
  flexShrink: 0,
1515
1688
  outline: "none",
1516
1689
  width: `calc(${(maxDepth - depth) * 16}px + ${maxKeyLength}ch)`
1517
1690
  }
1518
1691
  }
1519
- ) : /* @__PURE__ */ jsx5(
1692
+ ) : /* @__PURE__ */ jsx6(
1520
1693
  "span",
1521
1694
  {
1522
1695
  style: {
@@ -1530,7 +1703,7 @@ function FormField({
1530
1703
  children: getDisplayKey(node, state.tree)
1531
1704
  }
1532
1705
  ),
1533
- isRequired && !isSelected && /* @__PURE__ */ jsx5(
1706
+ isRequired && !isSelected && /* @__PURE__ */ jsx6(
1534
1707
  "span",
1535
1708
  {
1536
1709
  style: {
@@ -1541,7 +1714,7 @@ function FormField({
1541
1714
  children: "*"
1542
1715
  }
1543
1716
  ),
1544
- isEditing ? /* @__PURE__ */ jsx5(
1717
+ isEditing ? /* @__PURE__ */ jsx6(
1545
1718
  "div",
1546
1719
  {
1547
1720
  style: {
@@ -1560,7 +1733,7 @@ function FormField({
1560
1733
  valueColor
1561
1734
  )
1562
1735
  }
1563
- ) : /* @__PURE__ */ jsx5(
1736
+ ) : /* @__PURE__ */ jsx6(
1564
1737
  "span",
1565
1738
  {
1566
1739
  style: {
@@ -1575,7 +1748,7 @@ function FormField({
1575
1748
  children: displayValue
1576
1749
  }
1577
1750
  ),
1578
- isEditing && /* @__PURE__ */ jsx5(
1751
+ isEditing && /* @__PURE__ */ jsx6(
1579
1752
  "button",
1580
1753
  {
1581
1754
  onClick: (e) => {
@@ -1606,48 +1779,38 @@ function renderEditInput(node, propSchema, displayValue, handleValueChange, inpu
1606
1779
  background: "none",
1607
1780
  border: "none",
1608
1781
  fontFamily: "var(--vj-font, monospace)",
1609
- fontSize: 13,
1782
+ fontSize: "var(--vj-input-font-size, 13px)",
1610
1783
  padding: 0,
1611
1784
  flex: 1,
1612
1785
  outline: "none",
1613
1786
  color: valueColor
1614
1787
  };
1615
1788
  if (node.type === "boolean") {
1616
- return /* @__PURE__ */ jsxs3(
1617
- "select",
1789
+ return /* @__PURE__ */ jsx6(
1790
+ EnumInput,
1618
1791
  {
1619
- ref: inputRef,
1792
+ enumValues: ["true", "false"],
1620
1793
  value: String(node.value),
1621
- onChange: (e) => handleValueChange(e.target.value),
1622
- onClick: (e) => e.stopPropagation(),
1623
- style: { ...inputStyle, cursor: "pointer" },
1624
- children: [
1625
- /* @__PURE__ */ jsx5("option", { value: "true", children: "true" }),
1626
- /* @__PURE__ */ jsx5("option", { value: "false", children: "false" })
1627
- ]
1794
+ onValueChange: handleValueChange,
1795
+ inputRef,
1796
+ inputStyle
1628
1797
  }
1629
1798
  );
1630
1799
  }
1631
1800
  if (hasEnumValues && propSchema?.enum) {
1632
- return /* @__PURE__ */ jsxs3(
1633
- "select",
1801
+ return /* @__PURE__ */ jsx6(
1802
+ EnumInput,
1634
1803
  {
1635
- ref: inputRef,
1804
+ enumValues: propSchema.enum,
1636
1805
  value: displayValue,
1637
- onChange: (e) => handleValueChange(e.target.value),
1638
- onClick: (e) => e.stopPropagation(),
1639
- style: { ...inputStyle, cursor: "pointer" },
1640
- children: [
1641
- !propSchema.enum.some(
1642
- (v) => JSON.stringify(v) === JSON.stringify(node.value)
1643
- ) && /* @__PURE__ */ jsx5("option", { value: displayValue, children: displayValue || "(empty)" }),
1644
- propSchema.enum.map((v, i) => /* @__PURE__ */ jsx5("option", { value: String(v), children: String(v) }, i))
1645
- ]
1806
+ onValueChange: handleValueChange,
1807
+ inputRef,
1808
+ inputStyle
1646
1809
  }
1647
1810
  );
1648
1811
  }
1649
1812
  if (node.type === "null") {
1650
- return /* @__PURE__ */ jsx5(
1813
+ return /* @__PURE__ */ jsx6(
1651
1814
  "span",
1652
1815
  {
1653
1816
  style: {
@@ -1661,7 +1824,7 @@ function renderEditInput(node, propSchema, displayValue, handleValueChange, inpu
1661
1824
  }
1662
1825
  );
1663
1826
  }
1664
- return /* @__PURE__ */ jsx5(
1827
+ return /* @__PURE__ */ jsx6(
1665
1828
  "input",
1666
1829
  {
1667
1830
  ref: inputRef,
@@ -1688,16 +1851,16 @@ function FormView({
1688
1851
  const rootSchema = state.schema ?? void 0;
1689
1852
  const selectedNode = state.selectedNodeId ? state.tree.nodesById.get(state.selectedNodeId) : null;
1690
1853
  const displayNode = selectedNode ?? state.tree.root;
1691
- const [formSelectedNodeId, setFormSelectedNodeId] = useState6(
1854
+ const [formSelectedNodeId, setFormSelectedNodeId] = useState7(
1692
1855
  null
1693
1856
  );
1694
- const [editingNodeId, setEditingNodeId] = useState6(null);
1695
- const preEditTreeRef = useRef6(null);
1696
- const [collapsedIds, setCollapsedIds] = useState6(
1857
+ const [editingNodeId, setEditingNodeId] = useState7(null);
1858
+ const preEditTreeRef = useRef7(null);
1859
+ const [collapsedIds, setCollapsedIds] = useState7(
1697
1860
  () => /* @__PURE__ */ new Set()
1698
1861
  );
1699
- const containerRef = useRef6(null);
1700
- const [isFocused, setIsFocused] = useState6(false);
1862
+ const containerRef = useRef7(null);
1863
+ const [isFocused, setIsFocused] = useState7(false);
1701
1864
  const {
1702
1865
  dragState,
1703
1866
  handleDragStart,
@@ -1705,16 +1868,16 @@ function FormView({
1705
1868
  handleDragEnd,
1706
1869
  handleDrop
1707
1870
  } = useDragDrop();
1708
- useEffect5(() => {
1871
+ useEffect6(() => {
1709
1872
  setFormSelectedNodeId(null);
1710
1873
  setEditingNodeId(null);
1711
1874
  setCollapsedIds(/* @__PURE__ */ new Set());
1712
1875
  }, [displayNode.id]);
1713
- const visibleNodes = useMemo4(
1876
+ const visibleNodes = useMemo5(
1714
1877
  () => getVisibleNodes(displayNode, (id) => !collapsedIds.has(id)),
1715
1878
  [displayNode, collapsedIds]
1716
1879
  );
1717
- const { maxKeyLength, maxDepth } = useMemo4(() => {
1880
+ const { maxKeyLength, maxDepth } = useMemo5(() => {
1718
1881
  let maxKey = 1;
1719
1882
  let maxD = 0;
1720
1883
  const baseSegments = displayNode.path === "/" ? 0 : displayNode.path.split("/").filter(Boolean).length;
@@ -1727,11 +1890,11 @@ function FormView({
1727
1890
  }
1728
1891
  return { maxKeyLength: maxKey, maxDepth: maxD };
1729
1892
  }, [visibleNodes, displayNode.path, state.tree]);
1730
- const handleSelect = useCallback5((nodeId) => {
1893
+ const handleSelect = useCallback6((nodeId) => {
1731
1894
  setFormSelectedNodeId(nodeId);
1732
1895
  setEditingNodeId(null);
1733
1896
  }, []);
1734
- const handleToggleCollapse = useCallback5((nodeId) => {
1897
+ const handleToggleCollapse = useCallback6((nodeId) => {
1735
1898
  setCollapsedIds((prev) => {
1736
1899
  const next = new Set(prev);
1737
1900
  if (next.has(nodeId)) {
@@ -1742,14 +1905,14 @@ function FormView({
1742
1905
  return next;
1743
1906
  });
1744
1907
  }, []);
1745
- const handleStartEditing = useCallback5(
1908
+ const handleStartEditing = useCallback6(
1746
1909
  (nodeId) => {
1747
1910
  preEditTreeRef.current = state.tree;
1748
1911
  setEditingNodeId(nodeId);
1749
1912
  },
1750
1913
  [state.tree]
1751
1914
  );
1752
- const scrollToNode = useCallback5((nodeId) => {
1915
+ const scrollToNode = useCallback6((nodeId) => {
1753
1916
  requestAnimationFrame(() => {
1754
1917
  const el = containerRef.current?.querySelector(
1755
1918
  `[data-form-node-id="${nodeId}"]`
@@ -1757,7 +1920,7 @@ function FormView({
1757
1920
  el?.scrollIntoView({ block: "nearest" });
1758
1921
  });
1759
1922
  }, []);
1760
- const handleKeyDown = useCallback5(
1923
+ const handleKeyDown = useCallback6(
1761
1924
  (e) => {
1762
1925
  if (editingNodeId) {
1763
1926
  if (e.key === "Escape") {
@@ -1880,7 +2043,7 @@ function FormView({
1880
2043
  actions
1881
2044
  ]
1882
2045
  );
1883
- return /* @__PURE__ */ jsxs3(
2046
+ return /* @__PURE__ */ jsxs4(
1884
2047
  "div",
1885
2048
  {
1886
2049
  className,
@@ -1893,7 +2056,7 @@ function FormView({
1893
2056
  flexDirection: "column"
1894
2057
  },
1895
2058
  children: [
1896
- /* @__PURE__ */ jsx5(
2059
+ /* @__PURE__ */ jsx6(
1897
2060
  "div",
1898
2061
  {
1899
2062
  style: {
@@ -1902,10 +2065,10 @@ function FormView({
1902
2065
  backgroundColor: "var(--vj-bg-panel, #252526)",
1903
2066
  flexShrink: 0
1904
2067
  },
1905
- children: /* @__PURE__ */ jsx5(Breadcrumbs, {})
2068
+ children: /* @__PURE__ */ jsx6(Breadcrumbs, {})
1906
2069
  }
1907
2070
  ),
1908
- /* @__PURE__ */ jsx5(
2071
+ /* @__PURE__ */ jsx6(
1909
2072
  "div",
1910
2073
  {
1911
2074
  ref: containerRef,
@@ -1923,7 +2086,7 @@ function FormView({
1923
2086
  overflow: "auto",
1924
2087
  outline: "none"
1925
2088
  },
1926
- children: /* @__PURE__ */ jsx5(
2089
+ children: /* @__PURE__ */ jsx6(
1927
2090
  FormField,
1928
2091
  {
1929
2092
  node: displayNode,
@@ -1956,12 +2119,12 @@ function FormView({
1956
2119
  }
1957
2120
 
1958
2121
  // src/search-bar.tsx
1959
- import { useRef as useRef7, useEffect as useEffect6, useCallback as useCallback6 } from "react";
1960
- import { Fragment as Fragment2, jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
2122
+ import { useRef as useRef8, useEffect as useEffect7, useCallback as useCallback7 } from "react";
2123
+ import { Fragment as Fragment2, jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
1961
2124
  function SearchBar({ className }) {
1962
2125
  const { state, actions } = useStudio();
1963
- const inputRef = useRef7(null);
1964
- const handleKeyDown = useCallback6(
2126
+ const inputRef = useRef8(null);
2127
+ const handleKeyDown = useCallback7(
1965
2128
  (e) => {
1966
2129
  if (e.key === "Enter") {
1967
2130
  e.preventDefault();
@@ -1979,7 +2142,7 @@ function SearchBar({ className }) {
1979
2142
  },
1980
2143
  [actions]
1981
2144
  );
1982
- useEffect6(() => {
2145
+ useEffect7(() => {
1983
2146
  function handleGlobalKeyDown(e) {
1984
2147
  const mod = e.metaKey || e.ctrlKey;
1985
2148
  if (mod && e.key === "f") {
@@ -1993,7 +2156,7 @@ function SearchBar({ className }) {
1993
2156
  }, []);
1994
2157
  const matchCount = state.searchMatches.length;
1995
2158
  const currentMatch = matchCount > 0 ? state.searchMatchIndex + 1 : 0;
1996
- return /* @__PURE__ */ jsxs4(
2159
+ return /* @__PURE__ */ jsxs5(
1997
2160
  "div",
1998
2161
  {
1999
2162
  className,
@@ -2006,7 +2169,7 @@ function SearchBar({ className }) {
2006
2169
  borderBottom: "1px solid var(--vj-border, #333333)"
2007
2170
  },
2008
2171
  children: [
2009
- /* @__PURE__ */ jsx6(
2172
+ /* @__PURE__ */ jsx7(
2010
2173
  "input",
2011
2174
  {
2012
2175
  ref: inputRef,
@@ -2022,14 +2185,14 @@ function SearchBar({ className }) {
2022
2185
  borderRadius: 3,
2023
2186
  color: "var(--vj-text, #cccccc)",
2024
2187
  fontFamily: "var(--vj-font, monospace)",
2025
- fontSize: 12,
2188
+ fontSize: "var(--vj-input-font-size, 13px)",
2026
2189
  padding: "3px 8px",
2027
2190
  outline: "none",
2028
2191
  minWidth: 0
2029
2192
  }
2030
2193
  }
2031
2194
  ),
2032
- /* @__PURE__ */ jsx6(
2195
+ /* @__PURE__ */ jsx7(
2033
2196
  "div",
2034
2197
  {
2035
2198
  style: {
@@ -2039,8 +2202,8 @@ function SearchBar({ className }) {
2039
2202
  flexShrink: 0,
2040
2203
  height: 18
2041
2204
  },
2042
- children: state.searchQuery ? /* @__PURE__ */ jsxs4(Fragment2, { children: [
2043
- /* @__PURE__ */ jsx6(
2205
+ children: state.searchQuery ? /* @__PURE__ */ jsxs5(Fragment2, { children: [
2206
+ /* @__PURE__ */ jsx7(
2044
2207
  "span",
2045
2208
  {
2046
2209
  style: {
@@ -2053,7 +2216,7 @@ function SearchBar({ className }) {
2053
2216
  children: matchCount > 0 ? `${currentMatch}/${matchCount}` : "0/0"
2054
2217
  }
2055
2218
  ),
2056
- /* @__PURE__ */ jsx6(
2219
+ /* @__PURE__ */ jsx7(
2057
2220
  "button",
2058
2221
  {
2059
2222
  onClick: actions.prevSearchMatch,
@@ -2077,7 +2240,7 @@ function SearchBar({ className }) {
2077
2240
  children: "\u25B2"
2078
2241
  }
2079
2242
  ),
2080
- /* @__PURE__ */ jsx6(
2243
+ /* @__PURE__ */ jsx7(
2081
2244
  "button",
2082
2245
  {
2083
2246
  onClick: actions.nextSearchMatch,
@@ -2101,7 +2264,7 @@ function SearchBar({ className }) {
2101
2264
  children: "\u25BC"
2102
2265
  }
2103
2266
  ),
2104
- /* @__PURE__ */ jsx6(
2267
+ /* @__PURE__ */ jsx7(
2105
2268
  "button",
2106
2269
  {
2107
2270
  onClick: () => actions.setSearchQuery(""),
@@ -2124,8 +2287,8 @@ function SearchBar({ className }) {
2124
2287
  children: "\xD7"
2125
2288
  }
2126
2289
  )
2127
- ] }) : /* @__PURE__ */ jsxs4(Fragment2, { children: [
2128
- /* @__PURE__ */ jsx6(
2290
+ ] }) : /* @__PURE__ */ jsxs5(Fragment2, { children: [
2291
+ /* @__PURE__ */ jsx7(
2129
2292
  "button",
2130
2293
  {
2131
2294
  onClick: actions.expandAll,
@@ -2142,7 +2305,7 @@ function SearchBar({ className }) {
2142
2305
  alignItems: "center"
2143
2306
  },
2144
2307
  title: "Expand all",
2145
- children: /* @__PURE__ */ jsxs4(
2308
+ children: /* @__PURE__ */ jsxs5(
2146
2309
  "svg",
2147
2310
  {
2148
2311
  width: "14",
@@ -2154,15 +2317,15 @@ function SearchBar({ className }) {
2154
2317
  strokeLinecap: "round",
2155
2318
  strokeLinejoin: "round",
2156
2319
  children: [
2157
- /* @__PURE__ */ jsx6("path", { d: "M8 2v4M5 4l3-2 3 2" }),
2158
- /* @__PURE__ */ jsx6("path", { d: "M8 14v-4M5 12l3 2 3-2" }),
2159
- /* @__PURE__ */ jsx6("path", { d: "M2 8h12" })
2320
+ /* @__PURE__ */ jsx7("path", { d: "M8 2v4M5 4l3-2 3 2" }),
2321
+ /* @__PURE__ */ jsx7("path", { d: "M8 14v-4M5 12l3 2 3-2" }),
2322
+ /* @__PURE__ */ jsx7("path", { d: "M2 8h12" })
2160
2323
  ]
2161
2324
  }
2162
2325
  )
2163
2326
  }
2164
2327
  ),
2165
- /* @__PURE__ */ jsx6(
2328
+ /* @__PURE__ */ jsx7(
2166
2329
  "button",
2167
2330
  {
2168
2331
  onClick: actions.collapseAll,
@@ -2179,7 +2342,7 @@ function SearchBar({ className }) {
2179
2342
  alignItems: "center"
2180
2343
  },
2181
2344
  title: "Collapse all",
2182
- children: /* @__PURE__ */ jsxs4(
2345
+ children: /* @__PURE__ */ jsxs5(
2183
2346
  "svg",
2184
2347
  {
2185
2348
  width: "14",
@@ -2191,9 +2354,9 @@ function SearchBar({ className }) {
2191
2354
  strokeLinecap: "round",
2192
2355
  strokeLinejoin: "round",
2193
2356
  children: [
2194
- /* @__PURE__ */ jsx6("path", { d: "M8 5V1M5 3l3 2 3-2" }),
2195
- /* @__PURE__ */ jsx6("path", { d: "M8 11v4M5 13l3-2 3 2" }),
2196
- /* @__PURE__ */ jsx6("path", { d: "M2 8h12" })
2357
+ /* @__PURE__ */ jsx7("path", { d: "M8 5V1M5 3l3 2 3-2" }),
2358
+ /* @__PURE__ */ jsx7("path", { d: "M8 11v4M5 13l3-2 3 2" }),
2359
+ /* @__PURE__ */ jsx7("path", { d: "M2 8h12" })
2197
2360
  ]
2198
2361
  }
2199
2362
  )
@@ -2208,7 +2371,7 @@ function SearchBar({ className }) {
2208
2371
  }
2209
2372
 
2210
2373
  // src/json-editor.tsx
2211
- import { jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
2374
+ import { jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
2212
2375
  var DEFAULT_CSS_VARS = {
2213
2376
  "--vj-bg": "#1e1e1e",
2214
2377
  "--vj-bg-panel": "#252526",
@@ -2231,7 +2394,8 @@ var DEFAULT_CSS_VARS = {
2231
2394
  "--vj-input-bg": "#3c3c3c",
2232
2395
  "--vj-input-border": "#555555",
2233
2396
  "--vj-error": "#f48771",
2234
- "--vj-font": "monospace"
2397
+ "--vj-font": "monospace",
2398
+ "--vj-input-font-size": "13px"
2235
2399
  };
2236
2400
  function JsonEditor({
2237
2401
  value,
@@ -2251,15 +2415,15 @@ function JsonEditor({
2251
2415
  }) {
2252
2416
  const isControlled = value !== void 0;
2253
2417
  const initialValue = isControlled ? value : defaultValue ?? {};
2254
- const [editorKey, setEditorKey] = useState7(0);
2255
- const valueRef = useRef8(initialValue);
2256
- useEffect7(() => {
2418
+ const [editorKey, setEditorKey] = useState8(0);
2419
+ const valueRef = useRef9(initialValue);
2420
+ useEffect8(() => {
2257
2421
  if (isControlled && value !== valueRef.current) {
2258
2422
  valueRef.current = value;
2259
2423
  setEditorKey((k) => k + 1);
2260
2424
  }
2261
2425
  }, [value, isControlled]);
2262
- const handleChange = useCallback7(
2426
+ const handleChange = useCallback8(
2263
2427
  (newValue) => {
2264
2428
  valueRef.current = newValue;
2265
2429
  if (!readOnly) {
@@ -2277,25 +2441,35 @@ function JsonEditor({
2277
2441
  ...DEFAULT_CSS_VARS,
2278
2442
  ...style
2279
2443
  };
2280
- return /* @__PURE__ */ jsx7("div", { className, style: containerStyle, children: /* @__PURE__ */ jsx7(
2281
- VisualJson,
2282
- {
2283
- value: valueRef.current,
2284
- onChange: readOnly ? void 0 : handleChange,
2285
- schema,
2286
- children: /* @__PURE__ */ jsx7(
2287
- EditorLayout,
2288
- {
2289
- treeShowValues,
2290
- treeShowCounts,
2291
- editorShowDescriptions,
2292
- editorShowCounts,
2293
- sidebarOpen
2444
+ return /* @__PURE__ */ jsxs6("div", { className, "data-vj-root": "", style: containerStyle, children: [
2445
+ /* @__PURE__ */ jsx8(
2446
+ "style",
2447
+ {
2448
+ dangerouslySetInnerHTML: {
2449
+ __html: `@media(pointer:coarse){[data-vj-root]{--vj-input-font-size:16px}}`
2294
2450
  }
2295
- )
2296
- },
2297
- editorKey
2298
- ) });
2451
+ }
2452
+ ),
2453
+ /* @__PURE__ */ jsx8(
2454
+ VisualJson,
2455
+ {
2456
+ value: valueRef.current,
2457
+ onChange: readOnly ? void 0 : handleChange,
2458
+ schema,
2459
+ children: /* @__PURE__ */ jsx8(
2460
+ EditorLayout,
2461
+ {
2462
+ treeShowValues,
2463
+ treeShowCounts,
2464
+ editorShowDescriptions,
2465
+ editorShowCounts,
2466
+ sidebarOpen
2467
+ }
2468
+ )
2469
+ },
2470
+ editorKey
2471
+ )
2472
+ ] });
2299
2473
  }
2300
2474
  function EditorLayout({
2301
2475
  treeShowValues,
@@ -2304,14 +2478,14 @@ function EditorLayout({
2304
2478
  editorShowCounts,
2305
2479
  sidebarOpen
2306
2480
  }) {
2307
- const [sidebarWidth, setSidebarWidth] = useState7(280);
2308
- const [isNarrow, setIsNarrow] = useState7(false);
2309
- const [activePanel, setActivePanel] = useState7("tree");
2310
- const containerRef = useRef8(null);
2311
- const dragging = useRef8(false);
2312
- const startX = useRef8(0);
2313
- const startWidth = useRef8(0);
2314
- useEffect7(() => {
2481
+ const [sidebarWidth, setSidebarWidth] = useState8(280);
2482
+ const [isNarrow, setIsNarrow] = useState8(false);
2483
+ const [activePanel, setActivePanel] = useState8("tree");
2484
+ const containerRef = useRef9(null);
2485
+ const dragging = useRef9(false);
2486
+ const startX = useRef9(0);
2487
+ const startWidth = useRef9(0);
2488
+ useEffect8(() => {
2315
2489
  function checkWidth() {
2316
2490
  if (containerRef.current) {
2317
2491
  setIsNarrow(containerRef.current.offsetWidth < 500);
@@ -2322,7 +2496,7 @@ function EditorLayout({
2322
2496
  if (containerRef.current) observer.observe(containerRef.current);
2323
2497
  return () => observer.disconnect();
2324
2498
  }, []);
2325
- const handleMouseDown = useCallback7(
2499
+ const handleMouseDown = useCallback8(
2326
2500
  (e) => {
2327
2501
  dragging.current = true;
2328
2502
  startX.current = e.clientX;
@@ -2352,7 +2526,7 @@ function EditorLayout({
2352
2526
  );
2353
2527
  if (isNarrow) {
2354
2528
  if (!sidebarOpen) {
2355
- return /* @__PURE__ */ jsx7(
2529
+ return /* @__PURE__ */ jsx8(
2356
2530
  "div",
2357
2531
  {
2358
2532
  ref: containerRef,
@@ -2362,7 +2536,7 @@ function EditorLayout({
2362
2536
  flex: 1,
2363
2537
  minHeight: 0
2364
2538
  },
2365
- children: /* @__PURE__ */ jsx7("div", { style: { flex: 1, minHeight: 0 }, children: /* @__PURE__ */ jsx7(
2539
+ children: /* @__PURE__ */ jsx8("div", { style: { flex: 1, minHeight: 0 }, children: /* @__PURE__ */ jsx8(
2366
2540
  FormView,
2367
2541
  {
2368
2542
  showDescriptions: editorShowDescriptions,
@@ -2372,7 +2546,7 @@ function EditorLayout({
2372
2546
  }
2373
2547
  );
2374
2548
  }
2375
- return /* @__PURE__ */ jsxs5(
2549
+ return /* @__PURE__ */ jsxs6(
2376
2550
  "div",
2377
2551
  {
2378
2552
  ref: containerRef,
@@ -2383,7 +2557,7 @@ function EditorLayout({
2383
2557
  minHeight: 0
2384
2558
  },
2385
2559
  children: [
2386
- /* @__PURE__ */ jsxs5(
2560
+ /* @__PURE__ */ jsxs6(
2387
2561
  "div",
2388
2562
  {
2389
2563
  style: {
@@ -2393,7 +2567,7 @@ function EditorLayout({
2393
2567
  backgroundColor: "var(--vj-bg-panel, #252526)"
2394
2568
  },
2395
2569
  children: [
2396
- /* @__PURE__ */ jsx7(
2570
+ /* @__PURE__ */ jsx8(
2397
2571
  "button",
2398
2572
  {
2399
2573
  onClick: () => setActivePanel("tree"),
@@ -2410,7 +2584,7 @@ function EditorLayout({
2410
2584
  children: "Tree"
2411
2585
  }
2412
2586
  ),
2413
- /* @__PURE__ */ jsx7(
2587
+ /* @__PURE__ */ jsx8(
2414
2588
  "button",
2415
2589
  {
2416
2590
  onClick: () => setActivePanel("form"),
@@ -2430,7 +2604,7 @@ function EditorLayout({
2430
2604
  ]
2431
2605
  }
2432
2606
  ),
2433
- /* @__PURE__ */ jsx7("div", { style: { flex: 1, minHeight: 0, overflow: "hidden" }, children: activePanel === "tree" ? /* @__PURE__ */ jsxs5(
2607
+ /* @__PURE__ */ jsx8("div", { style: { flex: 1, minHeight: 0, overflow: "hidden" }, children: activePanel === "tree" ? /* @__PURE__ */ jsxs6(
2434
2608
  "div",
2435
2609
  {
2436
2610
  style: {
@@ -2439,8 +2613,8 @@ function EditorLayout({
2439
2613
  height: "100%"
2440
2614
  },
2441
2615
  children: [
2442
- /* @__PURE__ */ jsx7(SearchBar, {}),
2443
- /* @__PURE__ */ jsx7("div", { style: { flex: 1, minHeight: 0, overflow: "auto" }, children: /* @__PURE__ */ jsx7(
2616
+ /* @__PURE__ */ jsx8(SearchBar, {}),
2617
+ /* @__PURE__ */ jsx8("div", { style: { flex: 1, minHeight: 0, overflow: "auto" }, children: /* @__PURE__ */ jsx8(
2444
2618
  TreeView,
2445
2619
  {
2446
2620
  showValues: treeShowValues,
@@ -2449,7 +2623,7 @@ function EditorLayout({
2449
2623
  ) })
2450
2624
  ]
2451
2625
  }
2452
- ) : /* @__PURE__ */ jsx7(
2626
+ ) : /* @__PURE__ */ jsx8(
2453
2627
  "div",
2454
2628
  {
2455
2629
  style: {
@@ -2457,7 +2631,7 @@ function EditorLayout({
2457
2631
  flexDirection: "column",
2458
2632
  height: "100%"
2459
2633
  },
2460
- children: /* @__PURE__ */ jsx7("div", { style: { flex: 1, minHeight: 0 }, children: /* @__PURE__ */ jsx7(
2634
+ children: /* @__PURE__ */ jsx8("div", { style: { flex: 1, minHeight: 0 }, children: /* @__PURE__ */ jsx8(
2461
2635
  FormView,
2462
2636
  {
2463
2637
  showDescriptions: editorShowDescriptions,
@@ -2470,8 +2644,8 @@ function EditorLayout({
2470
2644
  }
2471
2645
  );
2472
2646
  }
2473
- return /* @__PURE__ */ jsxs5("div", { ref: containerRef, style: { display: "flex", flex: 1, minHeight: 0 }, children: [
2474
- /* @__PURE__ */ jsxs5(
2647
+ return /* @__PURE__ */ jsxs6("div", { ref: containerRef, style: { display: "flex", flex: 1, minHeight: 0 }, children: [
2648
+ /* @__PURE__ */ jsxs6(
2475
2649
  "div",
2476
2650
  {
2477
2651
  style: {
@@ -2483,12 +2657,12 @@ function EditorLayout({
2483
2657
  transition: "width 0.2s ease"
2484
2658
  },
2485
2659
  children: [
2486
- /* @__PURE__ */ jsx7(SearchBar, {}),
2487
- /* @__PURE__ */ jsx7("div", { style: { flex: 1, minHeight: 0, overflow: "auto" }, children: /* @__PURE__ */ jsx7(TreeView, { showValues: treeShowValues, showCounts: treeShowCounts }) })
2660
+ /* @__PURE__ */ jsx8(SearchBar, {}),
2661
+ /* @__PURE__ */ jsx8("div", { style: { flex: 1, minHeight: 0, overflow: "auto" }, children: /* @__PURE__ */ jsx8(TreeView, { showValues: treeShowValues, showCounts: treeShowCounts }) })
2488
2662
  ]
2489
2663
  }
2490
2664
  ),
2491
- sidebarOpen && /* @__PURE__ */ jsx7(
2665
+ sidebarOpen && /* @__PURE__ */ jsx8(
2492
2666
  "div",
2493
2667
  {
2494
2668
  style: {
@@ -2498,7 +2672,7 @@ function EditorLayout({
2498
2672
  position: "relative",
2499
2673
  transition: "background-color 0.15s"
2500
2674
  },
2501
- children: /* @__PURE__ */ jsx7(
2675
+ children: /* @__PURE__ */ jsx8(
2502
2676
  "div",
2503
2677
  {
2504
2678
  onMouseDown: handleMouseDown,
@@ -2527,7 +2701,7 @@ function EditorLayout({
2527
2701
  )
2528
2702
  }
2529
2703
  ),
2530
- /* @__PURE__ */ jsx7(
2704
+ /* @__PURE__ */ jsx8(
2531
2705
  "div",
2532
2706
  {
2533
2707
  style: {
@@ -2537,7 +2711,7 @@ function EditorLayout({
2537
2711
  minWidth: 0,
2538
2712
  overflow: "hidden"
2539
2713
  },
2540
- children: /* @__PURE__ */ jsx7("div", { style: { flex: 1, minHeight: 0 }, children: /* @__PURE__ */ jsx7(
2714
+ children: /* @__PURE__ */ jsx8("div", { style: { flex: 1, minHeight: 0 }, children: /* @__PURE__ */ jsx8(
2541
2715
  FormView,
2542
2716
  {
2543
2717
  showDescriptions: editorShowDescriptions,
@@ -2550,7 +2724,7 @@ function EditorLayout({
2550
2724
  }
2551
2725
 
2552
2726
  // src/property-editor.tsx
2553
- import { useState as useState8, useCallback as useCallback8 } from "react";
2727
+ import { useState as useState9, useCallback as useCallback9, useRef as useRef10 } from "react";
2554
2728
  import {
2555
2729
  setValue as setValue2,
2556
2730
  setKey as setKey2,
@@ -2562,7 +2736,7 @@ import {
2562
2736
  getPropertySchema as getPropertySchema3,
2563
2737
  resolveRef as resolveRef2
2564
2738
  } from "@visual-json/core";
2565
- import { jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
2739
+ import { jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
2566
2740
  var ALL_TYPES = [
2567
2741
  "string",
2568
2742
  "number",
@@ -2574,7 +2748,8 @@ var ALL_TYPES = [
2574
2748
  function PropertyRow({ node, schemaProperty }) {
2575
2749
  const { state, actions } = useStudio();
2576
2750
  const isContainer = node.type === "object" || node.type === "array";
2577
- const [hoveredRow, setHoveredRow] = useState8(false);
2751
+ const [hoveredRow, setHoveredRow] = useState9(false);
2752
+ const enumRef = useRef10(null);
2578
2753
  function handleValueChange(newValue) {
2579
2754
  let parsed;
2580
2755
  if (newValue === "null") parsed = null;
@@ -2610,13 +2785,13 @@ function PropertyRow({ node, schemaProperty }) {
2610
2785
  }
2611
2786
  const hasEnumValues = schemaProperty?.enum && schemaProperty.enum.length > 0;
2612
2787
  const description = schemaProperty?.description;
2613
- return /* @__PURE__ */ jsxs6(
2788
+ return /* @__PURE__ */ jsxs7(
2614
2789
  "div",
2615
2790
  {
2616
2791
  onMouseEnter: () => setHoveredRow(true),
2617
2792
  onMouseLeave: () => setHoveredRow(false),
2618
2793
  children: [
2619
- /* @__PURE__ */ jsxs6(
2794
+ /* @__PURE__ */ jsxs7(
2620
2795
  "div",
2621
2796
  {
2622
2797
  style: {
@@ -2629,7 +2804,7 @@ function PropertyRow({ node, schemaProperty }) {
2629
2804
  backgroundColor: hoveredRow ? "var(--vj-bg-hover, #2a2d2e)" : "transparent"
2630
2805
  },
2631
2806
  children: [
2632
- /* @__PURE__ */ jsx8(
2807
+ /* @__PURE__ */ jsx9(
2633
2808
  "input",
2634
2809
  {
2635
2810
  value: node.key,
@@ -2640,32 +2815,33 @@ function PropertyRow({ node, schemaProperty }) {
2640
2815
  borderRadius: 3,
2641
2816
  color: "var(--vj-text, #cccccc)",
2642
2817
  fontFamily: "var(--vj-font, monospace)",
2643
- fontSize: 13,
2818
+ fontSize: "var(--vj-input-font-size, 13px)",
2644
2819
  padding: "2px 6px",
2645
2820
  width: 140,
2646
2821
  flexShrink: 0
2647
2822
  }
2648
2823
  }
2649
2824
  ),
2650
- !isContainer ? hasEnumValues ? /* @__PURE__ */ jsx8(
2651
- "select",
2825
+ !isContainer ? hasEnumValues ? /* @__PURE__ */ jsx9(
2826
+ EnumInput,
2652
2827
  {
2828
+ enumValues: schemaProperty.enum,
2653
2829
  value: displayValue(),
2654
- onChange: (e) => handleValueChange(e.target.value),
2655
- style: {
2656
- background: "var(--vj-input-bg, #3c3c3c)",
2657
- border: "1px solid var(--vj-input-border, #555555)",
2830
+ onValueChange: handleValueChange,
2831
+ inputRef: enumRef,
2832
+ inputStyle: {
2833
+ background: "none",
2834
+ border: "1px solid transparent",
2658
2835
  borderRadius: 3,
2659
2836
  color: node.type === "string" ? "var(--vj-string, #ce9178)" : "var(--vj-number, #b5cea8)",
2660
2837
  fontFamily: "var(--vj-font, monospace)",
2661
- fontSize: 13,
2838
+ fontSize: "var(--vj-input-font-size, 13px)",
2662
2839
  padding: "2px 6px",
2663
2840
  flex: 1,
2664
- cursor: "pointer"
2665
- },
2666
- children: schemaProperty.enum.map((v, i) => /* @__PURE__ */ jsx8("option", { value: String(v), children: String(v) }, i))
2841
+ outline: "none"
2842
+ }
2667
2843
  }
2668
- ) : /* @__PURE__ */ jsx8(
2844
+ ) : /* @__PURE__ */ jsx9(
2669
2845
  "input",
2670
2846
  {
2671
2847
  value: displayValue(),
@@ -2677,13 +2853,13 @@ function PropertyRow({ node, schemaProperty }) {
2677
2853
  borderRadius: 3,
2678
2854
  color: node.type === "string" ? "var(--vj-string, #ce9178)" : "var(--vj-number, #b5cea8)",
2679
2855
  fontFamily: "var(--vj-font, monospace)",
2680
- fontSize: 13,
2856
+ fontSize: "var(--vj-input-font-size, 13px)",
2681
2857
  padding: "2px 6px",
2682
2858
  flex: 1,
2683
2859
  textAlign: "right"
2684
2860
  }
2685
2861
  }
2686
- ) : /* @__PURE__ */ jsx8(
2862
+ ) : /* @__PURE__ */ jsx9(
2687
2863
  "span",
2688
2864
  {
2689
2865
  style: {
@@ -2696,7 +2872,7 @@ function PropertyRow({ node, schemaProperty }) {
2696
2872
  children: displayValue()
2697
2873
  }
2698
2874
  ),
2699
- /* @__PURE__ */ jsxs6(
2875
+ /* @__PURE__ */ jsxs7(
2700
2876
  "div",
2701
2877
  {
2702
2878
  style: {
@@ -2707,7 +2883,7 @@ function PropertyRow({ node, schemaProperty }) {
2707
2883
  flexShrink: 0
2708
2884
  },
2709
2885
  children: [
2710
- isContainer && /* @__PURE__ */ jsx8(
2886
+ isContainer && /* @__PURE__ */ jsx9(
2711
2887
  "button",
2712
2888
  {
2713
2889
  onClick: handleAddChild,
@@ -2726,7 +2902,7 @@ function PropertyRow({ node, schemaProperty }) {
2726
2902
  children: "+"
2727
2903
  }
2728
2904
  ),
2729
- /* @__PURE__ */ jsx8(
2905
+ /* @__PURE__ */ jsx9(
2730
2906
  "button",
2731
2907
  {
2732
2908
  onClick: handleRemove,
@@ -2751,7 +2927,7 @@ function PropertyRow({ node, schemaProperty }) {
2751
2927
  ]
2752
2928
  }
2753
2929
  ),
2754
- description && /* @__PURE__ */ jsx8(
2930
+ description && /* @__PURE__ */ jsx9(
2755
2931
  "div",
2756
2932
  {
2757
2933
  style: {
@@ -2771,7 +2947,7 @@ function PropertyRow({ node, schemaProperty }) {
2771
2947
  function PropertyEditor({ className }) {
2772
2948
  const { state, actions } = useStudio();
2773
2949
  const selectedNode = state.selectedNodeId ? state.tree.nodesById.get(state.selectedNodeId) : null;
2774
- const handleChangeType = useCallback8(
2950
+ const handleChangeType = useCallback9(
2775
2951
  (newType) => {
2776
2952
  if (!selectedNode) return;
2777
2953
  const newTree = changeType2(state.tree, selectedNode.id, newType);
@@ -2779,17 +2955,17 @@ function PropertyEditor({ className }) {
2779
2955
  },
2780
2956
  [state.tree, selectedNode, actions]
2781
2957
  );
2782
- const handleDuplicate = useCallback8(() => {
2958
+ const handleDuplicate = useCallback9(() => {
2783
2959
  if (!selectedNode) return;
2784
2960
  const newTree = duplicateNode2(state.tree, selectedNode.id);
2785
2961
  actions.setTree(newTree);
2786
2962
  }, [state.tree, selectedNode, actions]);
2787
- const handleCopyPath = useCallback8(() => {
2963
+ const handleCopyPath = useCallback9(() => {
2788
2964
  if (!selectedNode) return;
2789
2965
  navigator.clipboard.writeText(selectedNode.path).catch(() => {
2790
2966
  });
2791
2967
  }, [selectedNode]);
2792
- const handleCopyValue = useCallback8(() => {
2968
+ const handleCopyValue = useCallback9(() => {
2793
2969
  if (!selectedNode) return;
2794
2970
  const value = toJson3(selectedNode);
2795
2971
  const text = typeof value === "string" ? value : JSON.stringify(value, null, 2);
@@ -2797,7 +2973,7 @@ function PropertyEditor({ className }) {
2797
2973
  });
2798
2974
  }, [selectedNode]);
2799
2975
  if (!selectedNode) {
2800
- return /* @__PURE__ */ jsx8(
2976
+ return /* @__PURE__ */ jsx9(
2801
2977
  "div",
2802
2978
  {
2803
2979
  className,
@@ -2830,7 +3006,7 @@ function PropertyEditor({ className }) {
2830
3006
  const newTree = addProperty2(state.tree, selectedNode.id, key, "");
2831
3007
  actions.setTree(newTree);
2832
3008
  }
2833
- return /* @__PURE__ */ jsxs6(
3009
+ return /* @__PURE__ */ jsxs7(
2834
3010
  "div",
2835
3011
  {
2836
3012
  className,
@@ -2843,7 +3019,7 @@ function PropertyEditor({ className }) {
2843
3019
  flexDirection: "column"
2844
3020
  },
2845
3021
  children: [
2846
- /* @__PURE__ */ jsxs6(
3022
+ /* @__PURE__ */ jsxs7(
2847
3023
  "div",
2848
3024
  {
2849
3025
  style: {
@@ -2859,7 +3035,7 @@ function PropertyEditor({ className }) {
2859
3035
  backgroundColor: "var(--vj-bg-panel, #252526)"
2860
3036
  },
2861
3037
  children: [
2862
- /* @__PURE__ */ jsxs6(
3038
+ /* @__PURE__ */ jsxs7(
2863
3039
  "div",
2864
3040
  {
2865
3041
  style: {
@@ -2870,8 +3046,8 @@ function PropertyEditor({ className }) {
2870
3046
  minWidth: 0
2871
3047
  },
2872
3048
  children: [
2873
- /* @__PURE__ */ jsx8(Breadcrumbs, {}),
2874
- schemaTitle && /* @__PURE__ */ jsx8(
3049
+ /* @__PURE__ */ jsx9(Breadcrumbs, {}),
3050
+ schemaTitle && /* @__PURE__ */ jsx9(
2875
3051
  "span",
2876
3052
  {
2877
3053
  style: { fontSize: 10, color: "var(--vj-text-dim, #666666)" },
@@ -2881,7 +3057,7 @@ function PropertyEditor({ className }) {
2881
3057
  ]
2882
3058
  }
2883
3059
  ),
2884
- /* @__PURE__ */ jsxs6(
3060
+ /* @__PURE__ */ jsxs7(
2885
3061
  "div",
2886
3062
  {
2887
3063
  style: {
@@ -2891,7 +3067,7 @@ function PropertyEditor({ className }) {
2891
3067
  flexShrink: 0
2892
3068
  },
2893
3069
  children: [
2894
- /* @__PURE__ */ jsx8(
3070
+ /* @__PURE__ */ jsx9(
2895
3071
  "select",
2896
3072
  {
2897
3073
  value: selectedNode.type,
@@ -2901,16 +3077,16 @@ function PropertyEditor({ className }) {
2901
3077
  border: "1px solid var(--vj-input-border, #555555)",
2902
3078
  borderRadius: 3,
2903
3079
  color: "var(--vj-text, #cccccc)",
2904
- fontSize: 11,
3080
+ fontSize: "var(--vj-input-font-size, 13px)",
2905
3081
  fontFamily: "var(--vj-font, monospace)",
2906
3082
  padding: "1px 4px",
2907
3083
  cursor: "pointer"
2908
3084
  },
2909
3085
  title: "Change type",
2910
- children: ALL_TYPES.map((t) => /* @__PURE__ */ jsx8("option", { value: t, children: t }, t))
3086
+ children: ALL_TYPES.map((t) => /* @__PURE__ */ jsx9("option", { value: t, children: t }, t))
2911
3087
  }
2912
3088
  ),
2913
- /* @__PURE__ */ jsx8(
3089
+ /* @__PURE__ */ jsx9(
2914
3090
  "button",
2915
3091
  {
2916
3092
  onClick: handleCopyPath,
@@ -2919,7 +3095,7 @@ function PropertyEditor({ className }) {
2919
3095
  children: "path"
2920
3096
  }
2921
3097
  ),
2922
- /* @__PURE__ */ jsx8(
3098
+ /* @__PURE__ */ jsx9(
2923
3099
  "button",
2924
3100
  {
2925
3101
  onClick: handleCopyValue,
@@ -2928,7 +3104,7 @@ function PropertyEditor({ className }) {
2928
3104
  children: "value"
2929
3105
  }
2930
3106
  ),
2931
- selectedNode.parentId && /* @__PURE__ */ jsx8(
3107
+ selectedNode.parentId && /* @__PURE__ */ jsx9(
2932
3108
  "button",
2933
3109
  {
2934
3110
  onClick: handleDuplicate,
@@ -2937,7 +3113,7 @@ function PropertyEditor({ className }) {
2937
3113
  children: "dup"
2938
3114
  }
2939
3115
  ),
2940
- isContainer && /* @__PURE__ */ jsx8(
3116
+ isContainer && /* @__PURE__ */ jsx9(
2941
3117
  "button",
2942
3118
  {
2943
3119
  onClick: handleAdd,
@@ -2954,14 +3130,14 @@ function PropertyEditor({ className }) {
2954
3130
  ]
2955
3131
  }
2956
3132
  ),
2957
- /* @__PURE__ */ jsx8("div", { style: { flex: 1, overflow: "auto" }, children: isContainer ? selectedNode.children.map((child) => /* @__PURE__ */ jsx8(
3133
+ /* @__PURE__ */ jsx9("div", { style: { flex: 1, overflow: "auto" }, children: isContainer ? selectedNode.children.map((child) => /* @__PURE__ */ jsx9(
2958
3134
  PropertyRow,
2959
3135
  {
2960
3136
  node: child,
2961
3137
  schemaProperty: getChildSchema(child.key)
2962
3138
  },
2963
3139
  child.id
2964
- )) : /* @__PURE__ */ jsx8(PropertyRow, { node: selectedNode }) })
3140
+ )) : /* @__PURE__ */ jsx9(PropertyRow, { node: selectedNode }) })
2965
3141
  ]
2966
3142
  }
2967
3143
  );
@@ -2978,9 +3154,9 @@ var actionButtonStyle = {
2978
3154
  };
2979
3155
 
2980
3156
  // src/diff-view.tsx
2981
- import { useMemo as useMemo5 } from "react";
3157
+ import { useMemo as useMemo6 } from "react";
2982
3158
  import { computeDiff } from "@visual-json/core";
2983
- import { Fragment as Fragment3, jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
3159
+ import { Fragment as Fragment3, jsx as jsx10, jsxs as jsxs8 } from "react/jsx-runtime";
2984
3160
  var DIFF_COLORS = {
2985
3161
  added: { bg: "#1e3a1e", marker: "+", label: "#4ec94e" },
2986
3162
  removed: { bg: "#3a1e1e", marker: "-", label: "#f48771" },
@@ -3001,7 +3177,7 @@ function formatValue(value) {
3001
3177
  }
3002
3178
  function DiffRow({ entry }) {
3003
3179
  const colors = DIFF_COLORS[entry.type];
3004
- return /* @__PURE__ */ jsxs7(
3180
+ return /* @__PURE__ */ jsxs8(
3005
3181
  "div",
3006
3182
  {
3007
3183
  style: {
@@ -3015,7 +3191,7 @@ function DiffRow({ entry }) {
3015
3191
  gap: 8
3016
3192
  },
3017
3193
  children: [
3018
- /* @__PURE__ */ jsx9(
3194
+ /* @__PURE__ */ jsx10(
3019
3195
  "span",
3020
3196
  {
3021
3197
  style: {
@@ -3028,7 +3204,7 @@ function DiffRow({ entry }) {
3028
3204
  children: colors.marker
3029
3205
  }
3030
3206
  ),
3031
- /* @__PURE__ */ jsx9(
3207
+ /* @__PURE__ */ jsx10(
3032
3208
  "span",
3033
3209
  {
3034
3210
  style: {
@@ -3039,14 +3215,14 @@ function DiffRow({ entry }) {
3039
3215
  children: entry.path
3040
3216
  }
3041
3217
  ),
3042
- /* @__PURE__ */ jsxs7("span", { style: { flex: 1, display: "flex", gap: 8, overflow: "hidden" }, children: [
3043
- entry.type === "changed" && /* @__PURE__ */ jsxs7(Fragment3, { children: [
3044
- /* @__PURE__ */ jsx9("span", { style: { color: "#f48771", textDecoration: "line-through" }, children: formatValue(entry.oldValue) }),
3045
- /* @__PURE__ */ jsx9("span", { style: { color: "var(--vj-text-dim, #666666)" }, children: "\u2192" }),
3046
- /* @__PURE__ */ jsx9("span", { style: { color: "#4ec94e" }, children: formatValue(entry.newValue) })
3218
+ /* @__PURE__ */ jsxs8("span", { style: { flex: 1, display: "flex", gap: 8, overflow: "hidden" }, children: [
3219
+ entry.type === "changed" && /* @__PURE__ */ jsxs8(Fragment3, { children: [
3220
+ /* @__PURE__ */ jsx10("span", { style: { color: "#f48771", textDecoration: "line-through" }, children: formatValue(entry.oldValue) }),
3221
+ /* @__PURE__ */ jsx10("span", { style: { color: "var(--vj-text-dim, #666666)" }, children: "\u2192" }),
3222
+ /* @__PURE__ */ jsx10("span", { style: { color: "#4ec94e" }, children: formatValue(entry.newValue) })
3047
3223
  ] }),
3048
- entry.type === "added" && /* @__PURE__ */ jsx9("span", { style: { color: "#4ec94e" }, children: formatValue(entry.newValue) }),
3049
- entry.type === "removed" && /* @__PURE__ */ jsx9("span", { style: { color: "#f48771", textDecoration: "line-through" }, children: formatValue(entry.oldValue) })
3224
+ entry.type === "added" && /* @__PURE__ */ jsx10("span", { style: { color: "#4ec94e" }, children: formatValue(entry.newValue) }),
3225
+ entry.type === "removed" && /* @__PURE__ */ jsx10("span", { style: { color: "#f48771", textDecoration: "line-through" }, children: formatValue(entry.oldValue) })
3050
3226
  ] })
3051
3227
  ]
3052
3228
  }
@@ -3057,14 +3233,14 @@ function DiffView({
3057
3233
  currentJson,
3058
3234
  className
3059
3235
  }) {
3060
- const entries = useMemo5(
3236
+ const entries = useMemo6(
3061
3237
  () => computeDiff(originalJson, currentJson),
3062
3238
  [originalJson, currentJson]
3063
3239
  );
3064
3240
  const added = entries.filter((e) => e.type === "added").length;
3065
3241
  const removed = entries.filter((e) => e.type === "removed").length;
3066
3242
  const changed = entries.filter((e) => e.type === "changed").length;
3067
- return /* @__PURE__ */ jsxs7(
3243
+ return /* @__PURE__ */ jsxs8(
3068
3244
  "div",
3069
3245
  {
3070
3246
  className,
@@ -3077,7 +3253,7 @@ function DiffView({
3077
3253
  flexDirection: "column"
3078
3254
  },
3079
3255
  children: [
3080
- /* @__PURE__ */ jsxs7(
3256
+ /* @__PURE__ */ jsxs8(
3081
3257
  "div",
3082
3258
  {
3083
3259
  style: {
@@ -3092,18 +3268,18 @@ function DiffView({
3092
3268
  flexShrink: 0
3093
3269
  },
3094
3270
  children: [
3095
- /* @__PURE__ */ jsx9("span", { style: { color: "var(--vj-text-muted, #999999)" }, children: entries.length === 0 ? "No changes" : `${entries.length} changes` }),
3096
- added > 0 && /* @__PURE__ */ jsxs7("span", { style: { color: "#4ec94e" }, children: [
3271
+ /* @__PURE__ */ jsx10("span", { style: { color: "var(--vj-text-muted, #999999)" }, children: entries.length === 0 ? "No changes" : `${entries.length} changes` }),
3272
+ added > 0 && /* @__PURE__ */ jsxs8("span", { style: { color: "#4ec94e" }, children: [
3097
3273
  "+",
3098
3274
  added,
3099
3275
  " added"
3100
3276
  ] }),
3101
- removed > 0 && /* @__PURE__ */ jsxs7("span", { style: { color: "#f48771" }, children: [
3277
+ removed > 0 && /* @__PURE__ */ jsxs8("span", { style: { color: "#f48771" }, children: [
3102
3278
  "-",
3103
3279
  removed,
3104
3280
  " removed"
3105
3281
  ] }),
3106
- changed > 0 && /* @__PURE__ */ jsxs7("span", { style: { color: "#dcdcaa" }, children: [
3282
+ changed > 0 && /* @__PURE__ */ jsxs8("span", { style: { color: "#dcdcaa" }, children: [
3107
3283
  "~",
3108
3284
  changed,
3109
3285
  " modified"
@@ -3111,7 +3287,7 @@ function DiffView({
3111
3287
  ]
3112
3288
  }
3113
3289
  ),
3114
- /* @__PURE__ */ jsx9("div", { style: { flex: 1, overflow: "auto" }, children: entries.length === 0 ? /* @__PURE__ */ jsx9(
3290
+ /* @__PURE__ */ jsx10("div", { style: { flex: 1, overflow: "auto" }, children: entries.length === 0 ? /* @__PURE__ */ jsx10(
3115
3291
  "div",
3116
3292
  {
3117
3293
  style: {
@@ -3125,7 +3301,7 @@ function DiffView({
3125
3301
  },
3126
3302
  children: "No differences detected"
3127
3303
  }
3128
- ) : entries.map((entry, i) => /* @__PURE__ */ jsx9(DiffRow, { entry }, i)) })
3304
+ ) : entries.map((entry, i) => /* @__PURE__ */ jsx10(DiffRow, { entry }, i)) })
3129
3305
  ]
3130
3306
  }
3131
3307
  );