From 97b2093b86bf1a586a05085c17fbe23a660a5462 Mon Sep 17 00:00:00 2001 From: typist <1003659191@qq.com> Date: Tue, 28 Oct 2025 03:48:37 +0800 Subject: [PATCH] feat: add tool json (#5) Co-authored-by: typist Reviewed-on: https://gitea.typist.cc/typist/litek/pulls/5 --- package.json | 2 ++ pnpm-lock.yaml | 28 +++++++++++++++ src/components/tool/index.tsx | 10 +++++- src/components/tool/json.tsx | 49 +++++++++++++++++++++++++ src/components/ui/alert.tsx | 66 ++++++++++++++++++++++++++++++++++ src/components/ui/sonner.tsx | 38 ++++++++++++++++++++ src/components/ui/textarea.tsx | 18 ++++++++++ src/main.tsx | 4 +++ 8 files changed, 214 insertions(+), 1 deletion(-) create mode 100644 src/components/tool/json.tsx create mode 100644 src/components/ui/alert.tsx create mode 100644 src/components/ui/sonner.tsx create mode 100644 src/components/ui/textarea.tsx diff --git a/package.json b/package.json index 019aa26..9b221bc 100644 --- a/package.json +++ b/package.json @@ -19,9 +19,11 @@ "clsx": "^2.1.1", "lucide-react": "^0.548.0", "nanoid": "^5.1.6", + "next-themes": "^0.4.6", "react": "^19.1.1", "react-dom": "^19.1.1", "react-router-dom": "^7.9.4", + "sonner": "^2.0.7", "tailwind-merge": "^3.3.1", "tailwindcss": "^4.1.16", "uuid": "^13.0.0" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index bca26d2..b195b49 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -38,6 +38,9 @@ importers: nanoid: specifier: ^5.1.6 version: 5.1.6 + next-themes: + specifier: ^0.4.6 + version: 0.4.6(react-dom@19.2.0(react@19.2.0))(react@19.2.0) react: specifier: ^19.1.1 version: 19.2.0 @@ -47,6 +50,9 @@ importers: react-router-dom: specifier: ^7.9.4 version: 7.9.4(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + sonner: + specifier: ^2.0.7 + version: 2.0.7(react-dom@19.2.0(react@19.2.0))(react@19.2.0) tailwind-merge: specifier: ^3.3.1 version: 3.3.1 @@ -1280,6 +1286,12 @@ packages: natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + next-themes@0.4.6: + resolution: {integrity: sha512-pZvgD5L0IEvX5/9GWyHMf3m8BKiVQwsCMHfoFosXtXBMnaS0ZnIJ9ST4b4NqLVKDEm8QBxoNNGNaBv2JNF6XNA==} + peerDependencies: + react: ^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc + react-dom: ^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc + node-releases@2.0.26: resolution: {integrity: sha512-S2M9YimhSjBSvYnlr5/+umAnPHE++ODwt5e2Ij6FoX45HA/s4vHdkDx1eax2pAPeAOqu4s9b7ppahsyEFdVqQA==} @@ -1472,6 +1484,12 @@ packages: resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} engines: {node: '>=8'} + sonner@2.0.7: + resolution: {integrity: sha512-W6ZN4p58k8aDKA4XPcx2hpIQXBRAgyiWVkYhT7CvK6D3iAu7xjvVyhQHg2/iaKJZ1XVJ4r7XuwGL+WGEK37i9w==} + peerDependencies: + react: ^18.0.0 || ^19.0.0 || ^19.0.0-rc + react-dom: ^18.0.0 || ^19.0.0 || ^19.0.0-rc + source-map-js@1.2.1: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} @@ -2702,6 +2720,11 @@ snapshots: natural-compare@1.4.0: {} + next-themes@0.4.6(react-dom@19.2.0(react@19.2.0))(react@19.2.0): + dependencies: + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) + node-releases@2.0.26: {} optionator@0.9.4: @@ -2854,6 +2877,11 @@ snapshots: shebang-regex@3.0.0: {} + sonner@2.0.7(react-dom@19.2.0(react@19.2.0))(react@19.2.0): + dependencies: + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) + source-map-js@1.2.1: {} strip-json-comments@3.1.1: {} diff --git a/src/components/tool/index.tsx b/src/components/tool/index.tsx index 64aa641..44e56ac 100644 --- a/src/components/tool/index.tsx +++ b/src/components/tool/index.tsx @@ -1,7 +1,8 @@ import type { ReactNode } from 'react'; -import { Hash } from 'lucide-react' +import { FileJson, Hash } from 'lucide-react' import UUID from './uuid' +import JSON from './json' export interface Tool { path: string; @@ -18,5 +19,12 @@ export const tools: Tool[] = [ description: "Generate a UUID", icon: , component: , + }, + { + path: "json", + name: "JSON Formatter", + description: "Format and validate JSON", + icon: , + component: , } ]; \ No newline at end of file diff --git a/src/components/tool/json.tsx b/src/components/tool/json.tsx new file mode 100644 index 0000000..d7760a5 --- /dev/null +++ b/src/components/tool/json.tsx @@ -0,0 +1,49 @@ +import { useState, type FC } from "react"; +import { Textarea } from "@/components/ui/textarea"; +import { Button } from "@/components/ui/button"; +import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"; +import { toast } from "sonner"; + +const Tool: FC = () => { + const [json, setJson] = useState(""); + + const validateJson = () => { + try { + JSON.parse(json); + return true; + } catch (error: unknown) { + if (error instanceof Error) { + toast.error(error.message); + } else { + toast.error("Invalid JSON"); + } + return false; + } + }; + + const minifyJson = () => { + if (!validateJson()) return; + const formattedJson = JSON.stringify(JSON.parse(json), null, 0); + setJson(formattedJson); + toast.success("Minified successfully"); + }; + + const prettifyJson = () => { + if (!validateJson()) return; + const formattedJson = JSON.stringify(JSON.parse(json), null, 2); + setJson(formattedJson); + toast.success("JSON prettified successfully"); + }; + + return ( +
+