From 52780f08fdc49f7fd02c25de7dc7a91d59b38fa5 Mon Sep 17 00:00:00 2001 From: zihluwang Date: Tue, 24 Feb 2026 14:57:58 +0800 Subject: [PATCH] feat: enhance JSON path highlighting and improve input styling in JsonViewer --- src/page/json-viewer/index.tsx | 67 +++++++++++++++++++++++++++++----- 1 file changed, 58 insertions(+), 9 deletions(-) diff --git a/src/page/json-viewer/index.tsx b/src/page/json-viewer/index.tsx index 0604347..b5ee6f6 100644 --- a/src/page/json-viewer/index.tsx +++ b/src/page/json-viewer/index.tsx @@ -5,6 +5,43 @@ import JsonTreeNode from "@/components/json-tree-node" import JsonCodeEditor from "@/components/json-code-editor" import Seo from "@/components/seo" +const jsonPathTokenRegex = /(\$)|(\.\.)|(\.)|(\[\*\])|(\[\d+\])|(\[(?:'[^']*'|"[^"]*")\])|(\*)|(@)|(\?)|(\(|\))|([A-Za-z_][\w-]*)/g + +function escapeHtml(input: string): string { + return input + .replace(/&/g, "&") + .replace(//g, ">") +} + +function getJsonPathTokenClass(token: string): string { + if (token === "$" || token === "@") return "text-indigo-600" + if (token === "." || token === "..") return "text-slate-400" + if (token === "*" || token === "[*]") return "text-violet-600" + if (token.startsWith("[") && token.endsWith("]")) return "text-amber-600" + if (token === "?" || token === "(" || token === ")") return "text-rose-600" + return "text-emerald-600" +} + +function highlightJsonPath(input: string): string { + if (!input) return " " + + let result = "" + let lastIndex = 0 + + for (const match of input.matchAll(jsonPathTokenRegex)) { + 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 +} + /** * JSON Viewer page component that displays the JSON visualisation tool in DevLab. */ @@ -29,6 +66,7 @@ export default function JsonViewer() { const [query, setQuery] = useState("$.staff_members[*].name") const [copiedCsv, setCopiedCsv] = useState(false) const [copiedRawJson, setCopiedRawJson] = useState(false) + const highlightedQuery = useMemo(() => highlightJsonPath(query), [query]) const isPlainObject = (value: unknown): value is Record => { return value !== null && typeof value === "object" && !Array.isArray(value) @@ -147,17 +185,28 @@ export default function JsonViewer() { {t("jsonViewer.invalidSyntax")} )} - setQuery(e.target.value)} - placeholder={t("jsonViewer.placeholder")} - /> + > +
+              
+            
+ setQuery(e.target.value)} + placeholder={t("jsonViewer.placeholder")} + spellCheck={false} + /> +