From 058c16b99f9d189647232b89ee27decf21d79355 Mon Sep 17 00:00:00 2001 From: zihluwang Date: Tue, 24 Feb 2026 09:52:34 +0800 Subject: [PATCH] feat: add JsonCodeEditor component and integrate it into JsonGrid and JsonViewer --- .vscode/settings.json | 5 ++ src/components/json-code-editor/index.tsx | 75 +++++++++++++++++++++++ src/page/json-grid/index.tsx | 8 +-- src/page/json-viewer/index.tsx | 8 +-- 4 files changed, 84 insertions(+), 12 deletions(-) create mode 100644 .vscode/settings.json create mode 100644 src/components/json-code-editor/index.tsx diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..f60bf45 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "chat.tools.terminal.autoApprove": { + "pnpm": true + } +} \ No newline at end of file diff --git a/src/components/json-code-editor/index.tsx b/src/components/json-code-editor/index.tsx new file mode 100644 index 0000000..1f0c1e8 --- /dev/null +++ b/src/components/json-code-editor/index.tsx @@ -0,0 +1,75 @@ +import { useMemo, useRef } from "react" + +type JsonCodeEditorProps = { + value: string + onChange: (value: string) => void +} + +const tokenRegex = /"(?:\\u[a-fA-F0-9]{4}|\\[^u]|[^\\"])*"\s*:?|\btrue\b|\bfalse\b|\bnull\b|-?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?/g + +function escapeHtml(input: string): string { + return input + .replace(/&/g, "&") + .replace(//g, ">") +} + +function getTokenClass(token: string): string { + if (token.startsWith('"')) { + return token.endsWith(":") ? "text-indigo-600" : "text-emerald-600" + } + if (token === "true" || token === "false") { + return "text-violet-600" + } + if (token === "null") { + return "text-slate-500 italic" + } + return "text-amber-600" +} + +function highlightJson(input: string): string { + let result = "" + let lastIndex = 0 + + for (const match of input.matchAll(tokenRegex)) { + const token = match[0] + const index = match.index ?? 0 + + result += escapeHtml(input.slice(lastIndex, index)) + result += `${escapeHtml(token)}` + lastIndex = index + token.length + } + + result += escapeHtml(input.slice(lastIndex)) + return result || " " +} + +export default function JsonCodeEditor({ value, onChange }: JsonCodeEditorProps) { + const highlighted = useMemo(() => highlightJson(value), [value]) + const preRef = useRef(null) + + const syncScroll = (event: React.UIEvent) => { + if (!preRef.current) return + preRef.current.scrollTop = event.currentTarget.scrollTop + preRef.current.scrollLeft = event.currentTarget.scrollLeft + } + + return ( +
+
+        
+      
+