From 91d743bafce54fd1cc78be3869a0263274cb2724 Mon Sep 17 00:00:00 2001 From: Bhavesh Kshatriya Date: Wed, 16 Jul 2025 00:45:31 +0530 Subject: [PATCH] fix: improve JSON comparison error reporting and add line numbers --- src/components/input/LineNumberInput.tsx | 127 ++++++++++++++++++ .../tools/json/json-comparison/index.tsx | 6 +- .../tools/json/json-comparison/service.ts | 32 +++-- 3 files changed, 150 insertions(+), 15 deletions(-) create mode 100644 src/components/input/LineNumberInput.tsx diff --git a/src/components/input/LineNumberInput.tsx b/src/components/input/LineNumberInput.tsx new file mode 100644 index 0000000..7adc33e --- /dev/null +++ b/src/components/input/LineNumberInput.tsx @@ -0,0 +1,127 @@ +import { Box, styled, TextField } from '@mui/material'; +import React, { useContext, useRef } from 'react'; +import { CustomSnackBarContext } from '../../contexts/CustomSnackBarContext'; +import InputHeader from '../InputHeader'; +import InputFooter from './InputFooter'; +import { useTranslation } from 'react-i18next'; + +const LineNumberWrapper = styled(Box)(({ theme }) => ({ + position: 'relative', + display: 'flex', + backgroundColor: theme.palette.background.paper, + '.line-numbers': { + whiteSpace: 'pre', + position: 'absolute', + left: 0, + top: 0, + bottom: 0, + width: '40px', + backgroundColor: theme.palette.action.hover, + borderRight: `1px solid ${theme.palette.divider}`, + textAlign: 'right', + paddingRight: '8px', + paddingTop: '16px', // Align with TextField content + paddingBottom: '8px', + color: theme.palette.text.secondary, + userSelect: 'none', + fontSize: '14px', + lineHeight: '1.5em', + fontFamily: 'monospace', + zIndex: 1, + overflow: 'hidden' + }, + '.MuiTextField-root': { + position: 'relative', + '& .MuiInputBase-root': { + paddingLeft: '48px', + fontFamily: 'monospace', + fontSize: '14px', + lineHeight: '1.5em' + }, + '& .MuiInputBase-input': { + lineHeight: '1.5em' + } + } +})); + +export default function LineNumberInput({ + value, + onChange, + title = 'Input text', + placeholder +}: { + title?: string; + value: string; + onChange: (value: string) => void; + placeholder?: string; +}) { + const { t } = useTranslation(); + const { showSnackBar } = useContext(CustomSnackBarContext); + const fileInputRef = useRef(null); + + const handleCopy = () => { + navigator.clipboard + .writeText(value) + .then(() => showSnackBar(t('toolTextInput.copied'), 'success')) + .catch((err) => { + showSnackBar(t('toolTextInput.copyFailed', { error: err }), 'error'); + }); + }; + + const handleFileChange = (event: React.ChangeEvent) => { + const file = event.target.files?.[0]; + if (file) { + const reader = new FileReader(); + reader.onload = (e) => { + const text = e.target?.result; + if (typeof text === 'string') { + onChange(text); + } + }; + reader.readAsText(file); + } + }; + + const handleImportClick = () => { + fileInputRef.current?.click(); + }; + + // Generate line numbers based on the content + const lineCount = value.split('\n').length; + const lineNumbers = Array.from({ length: lineCount }, (_, i) => i + 1).join( + '\n' + ); + + return ( + + + +
{lineNumbers}
+ onChange(event.target.value)} + fullWidth + multiline + rows={10} + placeholder={placeholder || t('toolTextInput.placeholder')} + sx={{ + '&.MuiTextField-root': { + backgroundColor: 'background.paper' + } + }} + inputProps={{ + 'data-testid': 'text-input' + }} + /> +
+ + +
+ ); +} diff --git a/src/pages/tools/json/json-comparison/index.tsx b/src/pages/tools/json/json-comparison/index.tsx index 1c5e97b..a51af22 100644 --- a/src/pages/tools/json/json-comparison/index.tsx +++ b/src/pages/tools/json/json-comparison/index.tsx @@ -1,6 +1,6 @@ import { useState, useEffect } from 'react'; import ToolContent from '@components/ToolContent'; -import ToolTextInput from '@components/input/ToolTextInput'; +import LineNumberInput from '@components/input/LineNumberInput'; import ToolTextResult from '@components/result/ToolTextResult'; import { compareJson } from './service'; import { CardExampleType } from '@components/examples/ToolExamples'; @@ -122,7 +122,7 @@ export default function JsonComparison({ title }: ToolComponentProps) { - -