diff --git a/package-lock.json b/package-lock.json
index f177efec..479b592c 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -25,6 +25,7 @@
"papaparse": "^5.4.1",
"react": "^18.2.0",
"react-codemirror2": "^8.0.1",
+ "react-diff-view": "^3.3.2",
"react-dom": "^18.2.0",
"react-router-dom": "^6.3.0",
"react-scripts": "5.0.1",
@@ -6888,6 +6889,12 @@
"integrity": "sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==",
"license": "MIT"
},
+ "node_modules/classnames": {
+ "version": "2.5.1",
+ "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz",
+ "integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==",
+ "license": "MIT"
+ },
"node_modules/clean-css": {
"version": "5.3.3",
"resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.3.tgz",
@@ -10162,6 +10169,12 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/gitdiff-parser": {
+ "version": "0.3.1",
+ "resolved": "https://registry.npmjs.org/gitdiff-parser/-/gitdiff-parser-0.3.1.tgz",
+ "integrity": "sha512-YQJnY8aew65id8okGxKCksH3efDCJ9HzV7M9rsvd65habf39Pkh4cgYJ27AaoDMqo1X98pgNJhNMrm/kpV7UVQ==",
+ "license": "MIT"
+ },
"node_modules/glob": {
"version": "10.4.5",
"resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz",
@@ -17508,6 +17521,23 @@
"node": ">=8"
}
},
+ "node_modules/react-diff-view": {
+ "version": "3.3.2",
+ "resolved": "https://registry.npmjs.org/react-diff-view/-/react-diff-view-3.3.2.tgz",
+ "integrity": "sha512-wPVq4ktTcGOHbhnWKU/gHLtd3N2Xd+OZ/XQWcKA06dsxlSsESePAumQILwHtiak2nMCMiWcIfBpqZ5OiharUPA==",
+ "license": "MIT",
+ "dependencies": {
+ "classnames": "^2.3.2",
+ "diff-match-patch": "^1.0.5",
+ "gitdiff-parser": "^0.3.1",
+ "lodash": "^4.17.21",
+ "shallow-equal": "^3.1.0",
+ "warning": "^4.0.3"
+ },
+ "peerDependencies": {
+ "react": ">=16.14.0"
+ }
+ },
"node_modules/react-dom": {
"version": "18.3.1",
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz",
@@ -18624,6 +18654,12 @@
"integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==",
"license": "ISC"
},
+ "node_modules/shallow-equal": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/shallow-equal/-/shallow-equal-3.1.0.tgz",
+ "integrity": "sha512-pfVOw8QZIXpMbhBWvzBISicvToTiM5WBF1EeAUZDDSb5Dt29yl4AYbyywbJFSEsRUMr7gJaxqCdr4L3tQf9wVg==",
+ "license": "MIT"
+ },
"node_modules/shebang-command": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
@@ -20429,6 +20465,15 @@
"makeerror": "1.0.12"
}
},
+ "node_modules/warning": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz",
+ "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==",
+ "license": "MIT",
+ "dependencies": {
+ "loose-envify": "^1.0.0"
+ }
+ },
"node_modules/watchpack": {
"version": "2.4.4",
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.4.tgz",
diff --git a/package.json b/package.json
index 6b112f83..a88e0aae 100644
--- a/package.json
+++ b/package.json
@@ -21,6 +21,7 @@
"papaparse": "^5.4.1",
"react": "^18.2.0",
"react-codemirror2": "^8.0.1",
+ "react-diff-view": "^3.3.2",
"react-dom": "^18.2.0",
"react-router-dom": "^6.3.0",
"react-scripts": "5.0.1",
diff --git a/src/components/StructuredEditor.js b/src/components/StructuredEditor.js
index e97bd03b..54a935a3 100644
--- a/src/components/StructuredEditor.js
+++ b/src/components/StructuredEditor.js
@@ -78,15 +78,21 @@ const StructuredEditor = ({ onDataChange, initialData = {} }) => {
}
const key = pathParts[pathParts.length - 1];
+ const currentValue = current[key];
- // Auto-detect type
- if (value === 'true' || value === 'false') {
- current[key] = value === 'true';
- } else if (value === 'null') {
- current[key] = null;
- } else if (!isNaN(value) && value !== '') {
- current[key] = Number(value);
+ // Auto-detect type only when current value is empty/null/undefined
+ if (currentValue === '' || currentValue === null || currentValue === undefined) {
+ if (value === 'true' || value === 'false') {
+ current[key] = value === 'true';
+ } else if (value === 'null') {
+ current[key] = null;
+ } else if (!isNaN(value) && value !== '') {
+ current[key] = Number(value);
+ } else {
+ current[key] = value;
+ }
} else {
+ // If current value exists, preserve as string unless explicitly changed via type dropdown
current[key] = value;
}
@@ -170,9 +176,19 @@ const StructuredEditor = ({ onDataChange, initialData = {} }) => {
const renderValue = (value, key, path, parentPath) => {
const isExpanded = expandedNodes.has(path);
const canExpand = typeof value === 'object' && value !== null;
+
+ // Check if parent is an array by looking at the parent path
+ const isArrayItem = parentPath !== 'root' && (() => {
+ const parentPathParts = parentPath.split('.');
+ let current = data;
+ for (let i = 1; i < parentPathParts.length; i++) {
+ current = current[parentPathParts[i]];
+ }
+ return Array.isArray(current);
+ })();
return (
-
+
{canExpand && (