From b547dd94dd609af806cd8fc2718699ee38f3711b Mon Sep 17 00:00:00 2001 From: Ajay Kumar Date: Fri, 25 Jul 2025 14:48:14 +0530 Subject: [PATCH 01/17] feat(ui): add search clear icon and update button shapes - Add clear icon inside search input field - Remove separate clear button - Change expand/collapse buttons from circles to squares - Improve search input padding for icon placement --- src/App.tsx | 144 ++++++++++++++++++++++++++++-------- src/components/JsonNode.tsx | 2 +- 2 files changed, 113 insertions(+), 33 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 6fc1450..d22b6a2 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,5 +1,5 @@ import { useState, useCallback, useEffect } from 'react'; -import { FileCode, BarChart3, Upload, Copy, Square, Trash2, FileText, FoldVertical, UnfoldVertical, ChevronUp, ChevronDown } from 'lucide-react'; +import { FileCode, BarChart3, Upload, Copy, Square, Trash2, FileText, FoldVertical, UnfoldVertical, ChevronUp, ChevronDown, X, Github, Linkedin, Twitter, Globe, Heart } from 'lucide-react'; import { JsonInput } from './components/JsonInput'; import { JsonTree } from './components/JsonTree'; import { ThemeToggle } from './components/ThemeToggle'; @@ -558,7 +558,7 @@ function App() { "timestamp": "2024-01-24T10:30:00Z", "version": "1.0.0", "environment": "production", - "generatedBy": "NONSTOPIO JSON Viewer Test Suite", + "generatedBy": "NonStop io JSON Viewer Test Suite", "dataPoints": 15847, "processingTime": 0.234, "checksum": "sha256:a1b2c3d4e5f6789012345678901234567890abcdef123456789012345678901234" @@ -756,30 +756,41 @@ function App() { > - handleSearch(e.target.value, caseSensitive)} - onKeyDown={(e) => { - if (e.key === 'Enter') { - if (e.shiftKey) { - handleNavigateToPrevMatch(); - } else { - handleNavigateToNextMatch(); +
+ handleSearch(e.target.value, caseSensitive)} + onKeyDown={(e) => { + if (e.key === 'Enter') { + if (e.shiftKey) { + handleNavigateToPrevMatch(); + } else { + handleNavigateToNextMatch(); + } + e.preventDefault(); + } else if (e.key === 'F3') { + if (e.shiftKey) { + handleNavigateToPrevMatch(); + } else { + handleNavigateToNextMatch(); + } + e.preventDefault(); } - e.preventDefault(); - } else if (e.key === 'F3') { - if (e.shiftKey) { - handleNavigateToPrevMatch(); - } else { - handleNavigateToNextMatch(); - } - e.preventDefault(); - } - }} - placeholder="Search JSON... (Enter: next, Shift+Enter: prev)" - className="flex-1 px-3 py-2 text-sm border border-gray-300 dark:border-gray-600 rounded bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100 focus:outline-none focus:ring-2 focus:ring-blue-500" - /> + }} + placeholder="Search JSON... (Enter: next, Shift+Enter: prev)" + className="w-full px-3 pr-10 py-2 text-sm border border-gray-300 dark:border-gray-600 rounded bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100 focus:outline-none focus:ring-2 focus:ring-blue-500" + /> + {searchQuery && ( + + )} +
- {searchQuery && searchMatchIndices.length > 0 && ( <>
@@ -833,6 +837,7 @@ function App() { isLoading={isLoading} error={error} initialValue={inputText} + onError={setError} />
)} @@ -898,6 +903,81 @@ function App() { )} + {/* Footer */} +
+
+
+ {/* Logo and Company Name */} +
+ NonStop io Logo + + NonStop io Technologies Pvt. Ltd. + +
+ + {/* Social Links */} + + + {/* Visit Counter with Creative Label */} +
+
+ + Helped +
+ developers helped + + developers parse JSON + +
+
+
+
+ ); } diff --git a/src/components/JsonNode.tsx b/src/components/JsonNode.tsx index 2ea06c5..939dcc0 100644 --- a/src/components/JsonNode.tsx +++ b/src/components/JsonNode.tsx @@ -239,7 +239,7 @@ export const JsonNode: React.FC = ({ {canExpand ? ( + + {/* Property Details Button */} +
+ + + {/* Property Details Popup */} + {showDetails && ( +
e.stopPropagation()} + > +
+ Property Details +
+
+ {getPropertyDetails().map((detail, index) => ( +
+ + {detail.label}: + +
+ + {detail.value} + + +
+
+ ))} +
+ +
+ )} +
); diff --git a/src/components/JsonTableView.tsx b/src/components/JsonTableView.tsx index d3e75fc..c58ea21 100644 --- a/src/components/JsonTableView.tsx +++ b/src/components/JsonTableView.tsx @@ -1,6 +1,8 @@ -import React, { useMemo } from 'react'; +import React, { useMemo, useState } from 'react'; +import { Copy, Check } from 'lucide-react'; import { JsonValue } from '../types/json'; import { JsonNode } from '../types/json'; +import { trackEvent } from '../utils/analytics'; interface JsonTableViewProps { data: JsonValue; @@ -17,6 +19,23 @@ interface TableRow { } export const JsonTableView: React.FC = ({ data, searchQuery = '', selectedNodePath, nodes = [] }) => { + const [copiedValue, setCopiedValue] = useState(''); + + const copyToClipboard = (text: string, type: 'name' | 'value' | 'path') => { + navigator.clipboard.writeText(text).then(() => { + setCopiedValue(text); + setTimeout(() => setCopiedValue(''), 2000); + + trackEvent('property_detail_copied', { + property: type, + nodeType: 'table_view', + valueLength: text.length + }); + }).catch(err => { + console.error('Failed to copy text: ', err); + }); + }; + const getValueType = (value: any): string => { if (value === null) return 'null'; if (Array.isArray(value)) return 'array'; @@ -177,9 +196,10 @@ export const JsonTableView: React.FC = ({ data, searchQuery {/* Table Header - Fixed */}
-
+
Name
Value
+
Actions
@@ -190,7 +210,7 @@ export const JsonTableView: React.FC = ({ data, searchQuery {filteredRows.map((row, index) => (
@@ -205,6 +225,32 @@ export const JsonTableView: React.FC = ({ data, searchQuery {highlightText(row.value)}
+
+ {/* Copy Name Button */} + + {/* Copy Value Button */} + +
))}
@@ -239,6 +285,23 @@ export const JsonTableView: React.FC = ({ data, searchQuery {filteredRows.length} +
+
+ Path: + {selectedNodePath} +
+ +
)} diff --git a/src/types/analytics.ts b/src/types/analytics.ts index 1b014fb..9c4f310 100644 --- a/src/types/analytics.ts +++ b/src/types/analytics.ts @@ -39,6 +39,7 @@ export interface AnalyticsProperties { expandedCount?: number; collapsedCount?: number; totalMatches?: number; + property?: string; } export type AnalyticsEventType = @@ -54,6 +55,7 @@ export type AnalyticsEventType = | 'expand_all_nodes' | 'collapse_all_nodes' | 'value_copied' + | 'property_detail_copied' | 'theme_changed' | 'session_started' | 'feature_used' From 8d20d143a509a1737fe6ccbd5ae932b125037a27 Mon Sep 17 00:00:00 2001 From: Ajay Kumar Date: Fri, 25 Jul 2025 15:58:47 +0530 Subject: [PATCH 05/17] feat: add prettier formatting and enhance code quality - Add prettier configuration and npm scripts for code formatting - Update ESLint config with TypeScript support and consistent formatting - Enhance type safety with proper TypeScript types throughout components - Improve JSON table view with accurate value copying functionality - Add auto-parsing when switching to viewer tab with unsaved changes - Refactor components for better maintainability and type safety --- .eslintrc.cjs | 22 +- .prettierignore | 35 ++ .prettierrc | 20 ++ package-lock.json | 49 +++ package.json | 7 +- src/App.tsx | 24 +- src/components/JsonNode.tsx | 2 +- src/components/JsonTableView.tsx | 97 +++++- src/hooks/useTheme.ts | 85 ++--- src/types/analytics.ts | 46 +-- src/types/json.ts | 16 +- src/utils/analytics.ts | 85 ++--- src/utils/jsonParser.ts | 534 ++++++++++++++++++++----------- 13 files changed, 698 insertions(+), 324 deletions(-) create mode 100644 .prettierignore create mode 100644 .prettierrc diff --git a/.eslintrc.cjs b/.eslintrc.cjs index 00ceab6..a8ac529 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -1,18 +1,18 @@ module.exports = { root: true, - env: { browser: true, es2020: true }, + env: {browser: true, es2020: true}, extends: [ - 'eslint:recommended', - '@typescript-eslint/recommended', - 'eslint-plugin-react-hooks/recommended', + "eslint:recommended", + "plugin:@typescript-eslint/recommended", + "plugin:react-hooks/recommended", ], - ignorePatterns: ['dist', '.eslintrc.cjs'], - parser: '@typescript-eslint/parser', - plugins: ['react-refresh'], + ignorePatterns: ["dist", ".eslintrc.cjs"], + parser: "@typescript-eslint/parser", + plugins: ["react-refresh", "@typescript-eslint"], rules: { - 'react-refresh/only-export-components': [ - 'warn', - { allowConstantExport: true }, + "react-refresh/only-export-components": [ + "warn", + {allowConstantExport: true}, ], }, -} \ No newline at end of file +}; diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..c52bf21 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,35 @@ +# Build output +dist/ +build/ +*.js.map + +# Dependencies +node_modules/ + +# Logs +*.log + +# Environment files +.env +.env.local +.env.development.local +.env.test.local +.env.production.local + +# IDE +.vscode/ +.idea/ + +# OS +.DS_Store +Thumbs.db + +# Test coverage +coverage/ + +# Package lock files +package-lock.json +yarn.lock + +# Vite specific +dist/ \ No newline at end of file diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..89ac22d --- /dev/null +++ b/.prettierrc @@ -0,0 +1,20 @@ +{ + "semi": true, + "trailingComma": "es5", + "singleQuote": false, + "printWidth": 80, + "tabWidth": 2, + "useTabs": false, + "bracketSpacing": false, + "bracketSameLine": false, + "arrowParens": "always", + "endOfLine": "lf", + "quoteProps": "as-needed", + "jsxSingleQuote": false, + "embeddedLanguageFormatting": "auto", + "htmlWhitespaceSensitivity": "css", + "vueIndentScriptAndStyle": false, + "proseWrap": "preserve", + "insertPragma": false, + "requirePragma": false +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 8b7e294..c071b7f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -24,6 +24,7 @@ "eslint-plugin-react-hooks": "^4.6.0", "eslint-plugin-react-refresh": "^0.4.3", "postcss": "^8.4.27", + "prettier": "^3.6.2", "tailwindcss": "^3.3.3", "typescript": "^5.0.2", "vite": "^4.4.5" @@ -1390,6 +1391,7 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, "license": "MIT", "dependencies": { "color-convert": "^2.0.1" @@ -1412,6 +1414,7 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, "license": "ISC", "dependencies": { "normalize-path": "^3.0.0", @@ -1494,6 +1497,7 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -1516,6 +1520,7 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, "license": "MIT", "dependencies": { "fill-range": "^7.1.1" @@ -1602,6 +1607,7 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", @@ -1618,6 +1624,7 @@ "version": "3.6.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, "license": "MIT", "dependencies": { "anymatch": "~3.1.2", @@ -1642,6 +1649,7 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, "license": "ISC", "dependencies": { "is-glob": "^4.0.1" @@ -1654,6 +1662,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, "license": "MIT", "dependencies": { "color-name": "~1.1.4" @@ -1666,12 +1675,14 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, "license": "MIT" }, "node_modules/commander": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true, "license": "MIT", "engines": { "node": ">= 6" @@ -1730,6 +1741,7 @@ "version": "4.4.1", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "dev": true, "license": "MIT", "dependencies": { "ms": "^2.1.3" @@ -2148,6 +2160,7 @@ "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, "license": "MIT", "dependencies": { "to-regex-range": "^5.0.1" @@ -2160,6 +2173,7 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, "license": "MIT", "dependencies": { "locate-path": "^6.0.0", @@ -2236,6 +2250,7 @@ "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, "hasInstallScript": true, "license": "MIT", "optional": true, @@ -2373,6 +2388,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -2451,6 +2467,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, "license": "MIT", "dependencies": { "binary-extensions": "^2.0.0" @@ -2479,6 +2496,7 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -2498,6 +2516,7 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, "license": "MIT", "dependencies": { "is-extglob": "^2.1.1" @@ -2510,6 +2529,7 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, "license": "MIT", "engines": { "node": ">=0.12.0" @@ -2529,6 +2549,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, "license": "ISC" }, "node_modules/jackspeak": { @@ -2671,6 +2692,7 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, "license": "MIT", "dependencies": { "p-locate": "^5.0.0" @@ -2774,6 +2796,7 @@ "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, "license": "MIT" }, "node_modules/mz": { @@ -16344,6 +16367,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -16411,6 +16435,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, "license": "MIT", "dependencies": { "yocto-queue": "^0.1.0" @@ -16426,6 +16451,7 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, "license": "MIT", "dependencies": { "p-limit": "^3.0.2" @@ -16461,6 +16487,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -16538,6 +16565,7 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, "license": "MIT", "engines": { "node": ">=8.6" @@ -16726,6 +16754,22 @@ "node": ">= 0.8.0" } }, + "node_modules/prettier": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz", + "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -16806,6 +16850,7 @@ "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, "license": "MIT", "dependencies": { "picomatch": "^2.2.1" @@ -16927,6 +16972,7 @@ "version": "7.7.2", "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "dev": true, "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -17165,6 +17211,7 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, "license": "MIT", "dependencies": { "has-flag": "^4.0.0" @@ -17258,6 +17305,7 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, "license": "MIT", "dependencies": { "is-number": "^7.0.0" @@ -17588,6 +17636,7 @@ "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, "license": "MIT", "engines": { "node": ">=10" diff --git a/package.json b/package.json index bb1225b..81fa8df 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,11 @@ "scripts": { "dev": "vite", "build": "tsc && vite build", - "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", + "lint": "eslint --ext .js,.ts,.tsx .", + "lint:fix": "eslint --ext .js,.ts,.tsx . --fix", + "format": "prettier --write \"src/**/*.{js,ts,json}\" \".eslintrc.cjs\"", + "format:check": "prettier --check \"src/**/*.{js,ts,json}\" \".eslintrc.cjs\"", + "code-quality": "npm run format && npm run lint:fix && npm run build", "preview": "vite preview" }, "dependencies": { @@ -26,6 +30,7 @@ "eslint-plugin-react-hooks": "^4.6.0", "eslint-plugin-react-refresh": "^0.4.3", "postcss": "^8.4.27", + "prettier": "^3.6.2", "tailwindcss": "^3.3.3", "typescript": "^5.0.2", "vite": "^4.4.5" diff --git a/src/App.tsx b/src/App.tsx index 02c902c..f13d4b0 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -7,10 +7,10 @@ import { JsonTableView } from './components/JsonTableView'; import { ResizablePanel } from './components/ResizablePanel'; import { jsonParser } from './utils/jsonParser'; import { trackEvent } from './utils/analytics'; -import { JsonNode } from './types/json'; +import { JsonNode, JsonValue } from './types/json'; function App() { - const [jsonData, setJsonData] = useState(null); + const [jsonData, setJsonData] = useState(null); const [nodes, setNodes] = useState([]); const [filteredNodes, setFilteredNodes] = useState([]); const [originalNodes, setOriginalNodes] = useState([]); @@ -22,6 +22,7 @@ function App() { const [currentMatchIndex, setCurrentMatchIndex] = useState(0); const [activeTab, setActiveTab] = useState<'viewer' | 'text'>('text'); const [inputText, setInputText] = useState(''); + const [lastParsedInput, setLastParsedInput] = useState(''); const [selectedNodePath, setSelectedNodePath] = useState(''); useEffect(() => { @@ -50,6 +51,8 @@ function App() { setNodes(newNodes); setFilteredNodes(newNodes); setOriginalNodes(newNodes); + // Track the input that was successfully parsed + setLastParsedInput(jsonText); // Auto-select root node when data is loaded setSelectedNodePath('root'); // Switch to viewer tab only if parsing was successful and requested @@ -171,12 +174,27 @@ function App() { } }, [jsonData]); + const handleViewerTabClick = useCallback(() => { + // Check if there's unparsed text or text that has changed since last parse + const currentInputText = inputText.trim(); + + if (currentInputText && currentInputText !== lastParsedInput) { + // Auto-parse the text when switching to viewer tab if: + // 1. There's input text AND it's different from what was last successfully parsed + handleJsonSubmit(currentInputText, true); + } else { + // Just switch to viewer tab if input is the same as last parsed or no input text + setActiveTab('viewer'); + } + }, [inputText, lastParsedInput, handleJsonSubmit]); + const handleClear = useCallback(() => { setJsonData(null); setNodes([]); setFilteredNodes([]); setOriginalNodes([]); setInputText(''); + setLastParsedInput(''); setError(''); setSearchQuery(''); setSearchMatchIndices([]); @@ -659,7 +677,7 @@ function App() { JSON {/* Copy Value Button */}