diff --git a/demo/components/ui/sheet.tsx b/demo/components/ui/sheet.tsx index 0ea7d04..654ba65 100644 --- a/demo/components/ui/sheet.tsx +++ b/demo/components/ui/sheet.tsx @@ -1,5 +1,5 @@ import * as SheetPrimitive from "@radix-ui/react-dialog"; -import { type VariantProps, cva } from "class-variance-authority"; +import { cva, type VariantProps } from "class-variance-authority"; import { X } from "lucide-react"; import * as React from "react"; diff --git a/demo/components/ui/sidebar.tsx b/demo/components/ui/sidebar.tsx index 33efa54..9d4f898 100644 --- a/demo/components/ui/sidebar.tsx +++ b/demo/components/ui/sidebar.tsx @@ -1,6 +1,6 @@ import { Slot } from "@radix-ui/react-slot"; -import { cva } from "class-variance-authority"; import type { VariantProps } from "class-variance-authority"; +import { cva } from "class-variance-authority"; import { PanelLeft } from "lucide-react"; import * as React from "react"; diff --git a/demo/components/ui/toast.tsx b/demo/components/ui/toast.tsx index ca65e27..ef2cf6f 100644 --- a/demo/components/ui/toast.tsx +++ b/demo/components/ui/toast.tsx @@ -1,14 +1,13 @@ import * as ToastPrimitives from "@radix-ui/react-toast"; -import { type VariantProps, cva } from "class-variance-authority"; +import { cva, type VariantProps } from "class-variance-authority"; import { X } from "lucide-react"; - -import { cn } from "../../../src/lib/utils.ts"; import { - forwardRef, type ComponentPropsWithoutRef, type ComponentRef, + forwardRef, type ReactElement, } from "react"; +import { cn } from "../../../src/lib/utils.ts"; const ToastProvider = ToastPrimitives.Provider; diff --git a/demo/pages/Index.tsx b/demo/pages/Index.tsx index eb79879..7fd3eff 100644 --- a/demo/pages/Index.tsx +++ b/demo/pages/Index.tsx @@ -4,18 +4,18 @@ import { Code, FileJson, GitBranch, - User, Package, + Pencil, + PencilOff, RefreshCw, + User, } from "lucide-react"; import React, { useState } from "react"; import { exampleSchema } from "../../demo/utils/schemaExample.ts"; -import JsonSchemaEditor from "../../src/components/SchemaEditor/JsonSchemaEditor.tsx"; import { JsonValidator } from "../../src/components/features/JsonValidator.tsx"; import { SchemaInferencer } from "../../src/components/features/SchemaInferencer.tsx"; +import JsonSchemaEditor from "../../src/components/SchemaEditor/JsonSchemaEditor.tsx"; import { Button } from "../../src/components/ui/button.tsx"; -import type { JSONSchema } from "../../src/types/jsonSchema.ts"; -import { en } from "../../src/i18n/locales/en.ts"; import { Select, SelectContent, @@ -23,10 +23,13 @@ import { SelectTrigger, SelectValue, } from "../../src/components/ui/select.tsx"; +import { en } from "../../src/i18n/locales/en.ts"; import { TranslationContext } from "../../src/i18n/translation-context.ts"; +import type { JSONSchema } from "../../src/types/jsonSchema.ts"; const Index = () => { const [schema, setSchema] = useState(exampleSchema); + const [readOnly, setReadOnly] = useState(false); const [inferDialogOpen, setInferDialogOpen] = useState(false); const [validateDialogOpen, setValidateDialogOpen] = useState(false); const [language, setLanguage] = useState("en"); @@ -34,6 +37,8 @@ const Index = () => { const handleReset = () => setSchema(exampleSchema); + const handleReadOnlyToggle = () => setReadOnly(!readOnly); + const handleClear = () => setSchema({ type: "object", @@ -90,42 +95,68 @@ const Index = () => { {/* Action Buttons */}
- - - - -
- +
+ + + + + +
+ +
@@ -137,6 +168,7 @@ const Index = () => {
@@ -427,7 +459,9 @@ const Index = () => { {link.text} - {index < array.length - 1 && } + {index < array.length - 1 && ( + + )} ))}
diff --git a/package-lock.json b/package-lock.json index 5efebf4..5a7fb9c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -37,9 +37,11 @@ "@rslib/core": "^0.12.2", "@tailwindcss/postcss": "^4.1.12", "@tanstack/react-query": "^5.85.0", + "@testing-library/react": "^16.3.0", "@types/node": "^22.17.2", "@types/react": "^19.1.10", "@types/react-dom": "^19.1.7", + "global-jsdom": "^27.0.0", "monaco-editor": "^0.52.2", "next-themes": "^0.4.6", "postcss": "^8.5.6", @@ -49,6 +51,7 @@ "sonner": "^2.0.7", "tailwindcss": "^4.1.12", "tailwindcss-animate": "^1.0.7", + "tsx": "^4.20.6", "typescript": "^5.9.2" }, "peerDependencies": { @@ -57,6 +60,14 @@ "react-dom": ">=19.0.0" } }, + "node_modules/@acemir/cssom": { + "version": "0.9.24", + "resolved": "https://registry.npmjs.org/@acemir/cssom/-/cssom-0.9.24.tgz", + "integrity": "sha512-5YjgMmAiT2rjJZU7XK1SNI7iqTy92DpaYVgG6x63FxkJ11UpYfLndHJATtinWJClAXiOlW9XWaUyAQf8pMrQPg==", + "dev": true, + "license": "MIT", + "peer": true + }, "node_modules/@alloc/quick-lru": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", @@ -70,6 +81,66 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@asamuzakjp/css-color": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-4.1.0.tgz", + "integrity": "sha512-9xiBAtLn4aNsa4mDnpovJvBn72tNEIACyvlqaNJ+ADemR+yeMJWnBudOi2qGDviJa7SwcDOU/TRh5dnET7qk0w==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@csstools/css-calc": "^2.1.4", + "@csstools/css-color-parser": "^3.1.0", + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4", + "lru-cache": "^11.2.2" + } + }, + "node_modules/@asamuzakjp/css-color/node_modules/lru-cache": { + "version": "11.2.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.2.tgz", + "integrity": "sha512-F9ODfyqML2coTIsQpSkRHnLSZMtkU8Q+mSfcaIyKwy58u+8k5nvAYeiNhsyMARvzNcXJ9QfWVrcPsC9e9rAxtg==", + "dev": true, + "license": "ISC", + "peer": true, + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@asamuzakjp/dom-selector": { + "version": "6.7.4", + "resolved": "https://registry.npmjs.org/@asamuzakjp/dom-selector/-/dom-selector-6.7.4.tgz", + "integrity": "sha512-buQDjkm+wDPXd6c13534URWZqbz0RP5PAhXZ+LIoa5LgwInT9HVJvGIJivg75vi8I13CxDGdTnz+aY5YUJlIAA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@asamuzakjp/nwsapi": "^2.3.9", + "bidi-js": "^1.0.3", + "css-tree": "^3.1.0", + "is-potential-custom-element-name": "^1.0.1", + "lru-cache": "^11.2.2" + } + }, + "node_modules/@asamuzakjp/dom-selector/node_modules/lru-cache": { + "version": "11.2.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.2.tgz", + "integrity": "sha512-F9ODfyqML2coTIsQpSkRHnLSZMtkU8Q+mSfcaIyKwy58u+8k5nvAYeiNhsyMARvzNcXJ9QfWVrcPsC9e9rAxtg==", + "dev": true, + "license": "ISC", + "peer": true, + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@asamuzakjp/nwsapi": { + "version": "2.3.9", + "resolved": "https://registry.npmjs.org/@asamuzakjp/nwsapi/-/nwsapi-2.3.9.tgz", + "integrity": "sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==", + "dev": true, + "license": "MIT", + "peer": true + }, "node_modules/@ast-grep/napi": { "version": "0.37.0", "resolved": "https://registry.npmjs.org/@ast-grep/napi/-/napi-0.37.0.tgz", @@ -224,13 +295,677 @@ "win32" ], "engines": { - "node": ">= 10" + "node": ">= 10" + } + }, + "node_modules/@ast-grep/napi-win32-x64-msvc": { + "version": "0.37.0", + "resolved": "https://registry.npmjs.org/@ast-grep/napi-win32-x64-msvc/-/napi-win32-x64-msvc-0.37.0.tgz", + "integrity": "sha512-vCiFOT3hSCQuHHfZ933GAwnPzmL0G04JxQEsBRfqONywyT8bSdDc/ECpAfr3S9VcS4JZ9/F6tkePKW/Om2Dq2g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.27.1", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.4.tgz", + "integrity": "sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@biomejs/biome": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-2.2.0.tgz", + "integrity": "sha512-3On3RSYLsX+n9KnoSgfoYlckYBoU6VRM22cw1gB4Y0OuUVSYd/O/2saOJMrA4HFfA1Ff0eacOvMN1yAAvHtzIw==", + "dev": true, + "license": "MIT OR Apache-2.0", + "bin": { + "biome": "bin/biome" + }, + "engines": { + "node": ">=14.21.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/biome" + }, + "optionalDependencies": { + "@biomejs/cli-darwin-arm64": "2.2.0", + "@biomejs/cli-darwin-x64": "2.2.0", + "@biomejs/cli-linux-arm64": "2.2.0", + "@biomejs/cli-linux-arm64-musl": "2.2.0", + "@biomejs/cli-linux-x64": "2.2.0", + "@biomejs/cli-linux-x64-musl": "2.2.0", + "@biomejs/cli-win32-arm64": "2.2.0", + "@biomejs/cli-win32-x64": "2.2.0" + } + }, + "node_modules/@biomejs/cli-darwin-arm64": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-2.2.0.tgz", + "integrity": "sha512-zKbwUUh+9uFmWfS8IFxmVD6XwqFcENjZvEyfOxHs1epjdH3wyyMQG80FGDsmauPwS2r5kXdEM0v/+dTIA9FXAg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-darwin-x64": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-2.2.0.tgz", + "integrity": "sha512-+OmT4dsX2eTfhD5crUOPw3RPhaR+SKVspvGVmSdZ9y9O/AgL8pla6T4hOn1q+VAFBHuHhsdxDRJgFCSC7RaMOw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-linux-arm64": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-2.2.0.tgz", + "integrity": "sha512-6eoRdF2yW5FnW9Lpeivh7Mayhq0KDdaDMYOJnH9aT02KuSIX5V1HmWJCQQPwIQbhDh68Zrcpl8inRlTEan0SXw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-linux-arm64-musl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.2.0.tgz", + "integrity": "sha512-egKpOa+4FL9YO+SMUMLUvf543cprjevNc3CAgDNFLcjknuNMcZ0GLJYa3EGTCR2xIkIUJDVneBV3O9OcIlCEZQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-linux-x64": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-2.2.0.tgz", + "integrity": "sha512-5UmQx/OZAfJfi25zAnAGHUMuOd+LOsliIt119x2soA2gLggQYrVPA+2kMUxR6Mw5M1deUF/AWWP2qpxgH7Nyfw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-linux-x64-musl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-2.2.0.tgz", + "integrity": "sha512-I5J85yWwUWpgJyC1CcytNSGusu2p9HjDnOPAFG4Y515hwRD0jpR9sT9/T1cKHtuCvEQ/sBvx+6zhz9l9wEJGAg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-win32-arm64": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-2.2.0.tgz", + "integrity": "sha512-n9a1/f2CwIDmNMNkFs+JI0ZjFnMO0jdOyGNtihgUNFnlmd84yIYY2KMTBmMV58ZlVHjgmY5Y6E1hVTnSRieggA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-win32-x64": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-2.2.0.tgz", + "integrity": "sha512-Nawu5nHjP/zPKTIryh2AavzTc/KEg4um/MxWdXW0A6P/RZOyIpa7+QSjeXwAwX/utJGaCoXRPWtF3m5U/bB3Ww==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@csstools/color-helpers": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-5.1.0.tgz", + "integrity": "sha512-S11EXWJyy0Mz5SYvRmY8nJYTFFd1LCNV+7cXyAgQtOOuzb4EsgfqDufL+9esx72/eLhsRdGZwaldu/h+E4t4BA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@csstools/css-calc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-2.1.4.tgz", + "integrity": "sha512-3N8oaj+0juUw/1H3YwmDDJXCgTB1gKU6Hc/bB502u9zR0q2vd786XJH9QfrKIEgFlZmhZiq6epXl4rHqhzsIgQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "peer": true, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4" + } + }, + "node_modules/@csstools/css-color-parser": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-3.1.0.tgz", + "integrity": "sha512-nbtKwh3a6xNVIp/VRuXV64yTKnb1IjTAEEh3irzS+HkKjAOYLTGNb9pmVNntZ8iVBHcWDA2Dof0QtPgFI1BaTA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "peer": true, + "dependencies": { + "@csstools/color-helpers": "^5.1.0", + "@csstools/css-calc": "^2.1.4" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4" + } + }, + "node_modules/@csstools/css-parser-algorithms": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.5.tgz", + "integrity": "sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "peer": true, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-tokenizer": "^3.0.4" + } + }, + "node_modules/@csstools/css-syntax-patches-for-csstree": { + "version": "1.0.17", + "resolved": "https://registry.npmjs.org/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.0.17.tgz", + "integrity": "sha512-LCC++2h8pLUSPY+EsZmrrJ1EOUu+5iClpEiDhhdw3zRJpPbABML/N5lmRuBHjxtKm9VnRcsUzioyD0sekFMF0A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@csstools/css-tokenizer": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-3.0.4.tgz", + "integrity": "sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@emnapi/core": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.4.5.tgz", + "integrity": "sha512-XsLw1dEOpkSX/WucdqUhPWP7hDxSvZiY+fsUC14h+FtQ2Ifni4znbBt8punRX+Uj2JG/uDb8nEHVKvrVlvdZ5Q==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.0.4", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.4.5.tgz", + "integrity": "sha512-++LApOtY0pEEz1zrd9vy1/zXVaVJJ/EbAF3u0fXIzPJEDtnITsBGbbK0EkM72amhl/R5b+5xx0Y/QhcVOpuulg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/wasi-threads": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.0.4.tgz", + "integrity": "sha512-PJR+bOmMOPH8AtcTGAyYNiuJ3/Fcoj2XN/gBEWzDIKh254XO+mM9XoXHk5GNEhodxeMznbg7BlRojVbKN+gC6g==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", + "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz", + "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz", + "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz", + "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz", + "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz", + "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz", + "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz", + "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz", + "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz", + "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz", + "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz", + "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz", + "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz", + "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz", + "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz", + "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" } }, - "node_modules/@ast-grep/napi-win32-x64-msvc": { - "version": "0.37.0", - "resolved": "https://registry.npmjs.org/@ast-grep/napi-win32-x64-msvc/-/napi-win32-x64-msvc-0.37.0.tgz", - "integrity": "sha512-vCiFOT3hSCQuHHfZ933GAwnPzmL0G04JxQEsBRfqONywyT8bSdDc/ECpAfr3S9VcS4JZ9/F6tkePKW/Om2Dq2g==", + "node_modules/@esbuild/linux-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz", + "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", "cpu": [ "x64" ], @@ -238,207 +973,163 @@ "license": "MIT", "optional": true, "os": [ - "win32" + "linux" ], "engines": { - "node": ">= 10" - } - }, - "node_modules/@biomejs/biome": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-2.2.0.tgz", - "integrity": "sha512-3On3RSYLsX+n9KnoSgfoYlckYBoU6VRM22cw1gB4Y0OuUVSYd/O/2saOJMrA4HFfA1Ff0eacOvMN1yAAvHtzIw==", - "dev": true, - "license": "MIT OR Apache-2.0", - "bin": { - "biome": "bin/biome" - }, - "engines": { - "node": ">=14.21.3" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/biome" - }, - "optionalDependencies": { - "@biomejs/cli-darwin-arm64": "2.2.0", - "@biomejs/cli-darwin-x64": "2.2.0", - "@biomejs/cli-linux-arm64": "2.2.0", - "@biomejs/cli-linux-arm64-musl": "2.2.0", - "@biomejs/cli-linux-x64": "2.2.0", - "@biomejs/cli-linux-x64-musl": "2.2.0", - "@biomejs/cli-win32-arm64": "2.2.0", - "@biomejs/cli-win32-x64": "2.2.0" + "node": ">=18" } }, - "node_modules/@biomejs/cli-darwin-arm64": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-2.2.0.tgz", - "integrity": "sha512-zKbwUUh+9uFmWfS8IFxmVD6XwqFcENjZvEyfOxHs1epjdH3wyyMQG80FGDsmauPwS2r5kXdEM0v/+dTIA9FXAg==", + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz", + "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==", "cpu": [ "arm64" ], "dev": true, - "license": "MIT OR Apache-2.0", + "license": "MIT", "optional": true, "os": [ - "darwin" + "netbsd" ], "engines": { - "node": ">=14.21.3" + "node": ">=18" } }, - "node_modules/@biomejs/cli-darwin-x64": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-2.2.0.tgz", - "integrity": "sha512-+OmT4dsX2eTfhD5crUOPw3RPhaR+SKVspvGVmSdZ9y9O/AgL8pla6T4hOn1q+VAFBHuHhsdxDRJgFCSC7RaMOw==", + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz", + "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==", "cpu": [ "x64" ], "dev": true, - "license": "MIT OR Apache-2.0", + "license": "MIT", "optional": true, "os": [ - "darwin" + "netbsd" ], "engines": { - "node": ">=14.21.3" + "node": ">=18" } }, - "node_modules/@biomejs/cli-linux-arm64": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-2.2.0.tgz", - "integrity": "sha512-6eoRdF2yW5FnW9Lpeivh7Mayhq0KDdaDMYOJnH9aT02KuSIX5V1HmWJCQQPwIQbhDh68Zrcpl8inRlTEan0SXw==", + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz", + "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==", "cpu": [ "arm64" ], "dev": true, - "license": "MIT OR Apache-2.0", + "license": "MIT", "optional": true, "os": [ - "linux" + "openbsd" ], "engines": { - "node": ">=14.21.3" + "node": ">=18" } }, - "node_modules/@biomejs/cli-linux-arm64-musl": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.2.0.tgz", - "integrity": "sha512-egKpOa+4FL9YO+SMUMLUvf543cprjevNc3CAgDNFLcjknuNMcZ0GLJYa3EGTCR2xIkIUJDVneBV3O9OcIlCEZQ==", + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz", + "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==", "cpu": [ - "arm64" + "x64" ], "dev": true, - "license": "MIT OR Apache-2.0", + "license": "MIT", "optional": true, "os": [ - "linux" + "openbsd" ], "engines": { - "node": ">=14.21.3" + "node": ">=18" } }, - "node_modules/@biomejs/cli-linux-x64": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-2.2.0.tgz", - "integrity": "sha512-5UmQx/OZAfJfi25zAnAGHUMuOd+LOsliIt119x2soA2gLggQYrVPA+2kMUxR6Mw5M1deUF/AWWP2qpxgH7Nyfw==", + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz", + "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==", "cpu": [ - "x64" + "arm64" ], "dev": true, - "license": "MIT OR Apache-2.0", + "license": "MIT", "optional": true, "os": [ - "linux" + "openharmony" ], "engines": { - "node": ">=14.21.3" + "node": ">=18" } }, - "node_modules/@biomejs/cli-linux-x64-musl": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-2.2.0.tgz", - "integrity": "sha512-I5J85yWwUWpgJyC1CcytNSGusu2p9HjDnOPAFG4Y515hwRD0jpR9sT9/T1cKHtuCvEQ/sBvx+6zhz9l9wEJGAg==", + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz", + "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==", "cpu": [ "x64" ], "dev": true, - "license": "MIT OR Apache-2.0", + "license": "MIT", "optional": true, "os": [ - "linux" + "sunos" ], "engines": { - "node": ">=14.21.3" + "node": ">=18" } }, - "node_modules/@biomejs/cli-win32-arm64": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-2.2.0.tgz", - "integrity": "sha512-n9a1/f2CwIDmNMNkFs+JI0ZjFnMO0jdOyGNtihgUNFnlmd84yIYY2KMTBmMV58ZlVHjgmY5Y6E1hVTnSRieggA==", + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz", + "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==", "cpu": [ "arm64" ], "dev": true, - "license": "MIT OR Apache-2.0", + "license": "MIT", "optional": true, "os": [ "win32" ], "engines": { - "node": ">=14.21.3" + "node": ">=18" } }, - "node_modules/@biomejs/cli-win32-x64": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-2.2.0.tgz", - "integrity": "sha512-Nawu5nHjP/zPKTIryh2AavzTc/KEg4um/MxWdXW0A6P/RZOyIpa7+QSjeXwAwX/utJGaCoXRPWtF3m5U/bB3Ww==", + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz", + "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==", "cpu": [ - "x64" + "ia32" ], "dev": true, - "license": "MIT OR Apache-2.0", + "license": "MIT", "optional": true, "os": [ "win32" ], "engines": { - "node": ">=14.21.3" - } - }, - "node_modules/@emnapi/core": { - "version": "1.4.5", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.4.5.tgz", - "integrity": "sha512-XsLw1dEOpkSX/WucdqUhPWP7hDxSvZiY+fsUC14h+FtQ2Ifni4znbBt8punRX+Uj2JG/uDb8nEHVKvrVlvdZ5Q==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/wasi-threads": "1.0.4", - "tslib": "^2.4.0" - } - }, - "node_modules/@emnapi/runtime": { - "version": "1.4.5", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.4.5.tgz", - "integrity": "sha512-++LApOtY0pEEz1zrd9vy1/zXVaVJJ/EbAF3u0fXIzPJEDtnITsBGbbK0EkM72amhl/R5b+5xx0Y/QhcVOpuulg==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" + "node": ">=18" } }, - "node_modules/@emnapi/wasi-threads": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.0.4.tgz", - "integrity": "sha512-PJR+bOmMOPH8AtcTGAyYNiuJ3/Fcoj2XN/gBEWzDIKh254XO+mM9XoXHk5GNEhodxeMznbg7BlRojVbKN+gC6g==", + "node_modules/@esbuild/win32-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz", + "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", "optional": true, - "dependencies": { - "tslib": "^2.4.0" + "os": [ + "win32" + ], + "engines": { + "node": ">=18" } }, "node_modules/@floating-ui/core": { @@ -2511,6 +3202,55 @@ "react": "^18 || ^19" } }, + "node_modules/@testing-library/dom": { + "version": "10.4.1", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.1.tgz", + "integrity": "sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/code-frame": "^7.10.4", + "@babel/runtime": "^7.12.5", + "@types/aria-query": "^5.0.1", + "aria-query": "5.3.0", + "dom-accessibility-api": "^0.5.9", + "lz-string": "^1.5.0", + "picocolors": "1.1.1", + "pretty-format": "^27.0.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@testing-library/react": { + "version": "16.3.0", + "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-16.3.0.tgz", + "integrity": "sha512-kFSyxiEDwv1WLl2fgsq6pPBbw5aWKrsY2/noi1Id0TK0UParSF62oFQFGHXIyaG4pp2tEub/Zlel+fjjZILDsw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.12.5" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@testing-library/dom": "^10.0.0", + "@types/react": "^18.0.0 || ^19.0.0", + "@types/react-dom": "^18.0.0 || ^19.0.0", + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@tybys/wasm-util": { "version": "0.10.0", "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.0.tgz", @@ -2529,6 +3269,14 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/aria-query": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", + "integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==", + "dev": true, + "license": "MIT", + "peer": true + }, "node_modules/@types/node": { "version": "22.17.2", "resolved": "https://registry.npmjs.org/@types/node/-/node-22.17.2.tgz", @@ -2559,6 +3307,17 @@ "@types/react": "^19.0.0" } }, + "node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 14" + } + }, "node_modules/ajv": { "version": "8.17.1", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", @@ -2607,6 +3366,31 @@ } } }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, "node_modules/argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", @@ -2629,6 +3413,28 @@ "node": ">=10" } }, + "node_modules/aria-query": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", + "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", + "dev": true, + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "dequal": "^2.0.3" + } + }, + "node_modules/bidi-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/bidi-js/-/bidi-js-1.0.3.tgz", + "integrity": "sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "require-from-string": "^2.0.2" + } + }, "node_modules/chownr": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", @@ -2682,12 +3488,96 @@ "url": "https://opencollective.com/core-js" } }, - "node_modules/csstype": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", - "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", - "devOptional": true, - "license": "MIT" + "node_modules/css-tree": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.1.0.tgz", + "integrity": "sha512-0eW44TGN5SQXU1mWSkKwFstI/22X2bG1nYzZTYMAWjylYURhse752YgbE4Cx46AC+bAvI+/dYTPRk1LqSUnu6w==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "mdn-data": "2.12.2", + "source-map-js": "^1.0.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" + } + }, + "node_modules/cssstyle": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-5.3.3.tgz", + "integrity": "sha512-OytmFH+13/QXONJcC75QNdMtKpceNk3u8ThBjyyYjkEcy/ekBwR1mMAuNvi3gdBPW3N5TlCzQ0WZw8H0lN/bDw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@asamuzakjp/css-color": "^4.0.3", + "@csstools/css-syntax-patches-for-csstree": "^1.0.14", + "css-tree": "^3.1.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/data-urls": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-6.0.0.tgz", + "integrity": "sha512-BnBS08aLUM+DKamupXs3w2tJJoqU+AkaE/+6vQxi/G/DPmIZFJJp9Dkb1kM03AZx8ADehDUZgsNxju3mPXZYIA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "whatwg-mimetype": "^4.0.0", + "whatwg-url": "^15.0.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decimal.js": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz", + "integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=6" + } }, "node_modules/detect-libc": { "version": "2.0.4", @@ -2705,6 +3595,14 @@ "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==", "license": "MIT" }, + "node_modules/dom-accessibility-api": { + "version": "0.5.16", + "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", + "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==", + "dev": true, + "license": "MIT", + "peer": true + }, "node_modules/enhanced-resolve": { "version": "5.18.3", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.3.tgz", @@ -2719,6 +3617,20 @@ "node": ">=10.13.0" } }, + "node_modules/entities": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", + "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", + "dev": true, + "license": "BSD-2-Clause", + "peer": true, + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, "node_modules/error-stack-parser": { "version": "2.1.4", "resolved": "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.1.4.tgz", @@ -2729,6 +3641,48 @@ "stackframe": "^1.3.4" } }, + "node_modules/esbuild": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz", + "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.12", + "@esbuild/android-arm": "0.25.12", + "@esbuild/android-arm64": "0.25.12", + "@esbuild/android-x64": "0.25.12", + "@esbuild/darwin-arm64": "0.25.12", + "@esbuild/darwin-x64": "0.25.12", + "@esbuild/freebsd-arm64": "0.25.12", + "@esbuild/freebsd-x64": "0.25.12", + "@esbuild/linux-arm": "0.25.12", + "@esbuild/linux-arm64": "0.25.12", + "@esbuild/linux-ia32": "0.25.12", + "@esbuild/linux-loong64": "0.25.12", + "@esbuild/linux-mips64el": "0.25.12", + "@esbuild/linux-ppc64": "0.25.12", + "@esbuild/linux-riscv64": "0.25.12", + "@esbuild/linux-s390x": "0.25.12", + "@esbuild/linux-x64": "0.25.12", + "@esbuild/netbsd-arm64": "0.25.12", + "@esbuild/netbsd-x64": "0.25.12", + "@esbuild/openbsd-arm64": "0.25.12", + "@esbuild/openbsd-x64": "0.25.12", + "@esbuild/openharmony-arm64": "0.25.12", + "@esbuild/sunos-x64": "0.25.12", + "@esbuild/win32-arm64": "0.25.12", + "@esbuild/win32-ia32": "0.25.12", + "@esbuild/win32-x64": "0.25.12" + } + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -2766,6 +3720,21 @@ "node": ">=14.14" } }, + "node_modules/fsevents": { + "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, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", @@ -2785,6 +3754,32 @@ "node": ">=6" } }, + "node_modules/get-tsconfig": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.0.tgz", + "integrity": "sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, + "node_modules/global-jsdom": { + "version": "27.0.0", + "resolved": "https://registry.npmjs.org/global-jsdom/-/global-jsdom-27.0.0.tgz", + "integrity": "sha512-0lIJfZACEKdBZ1VKSY2L4T2sYhnQ4VfzbULUiEMPcUa3ZWv4ywOCNZAibZyKIDwysIr+WjRyM4U9L9vM6oQQ+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20" + }, + "peerDependencies": { + "jsdom": ">=27 <28" + } + }, "node_modules/graceful-fs": { "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", @@ -2815,6 +3810,20 @@ "node": ">= 0.4" } }, + "node_modules/html-encoding-sniffer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz", + "integrity": "sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "whatwg-encoding": "^3.1.1" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/html-entities": { "version": "2.6.0", "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.6.0.tgz", @@ -2832,6 +3841,50 @@ ], "license": "MIT" }, + "node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/import-lazy": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-4.0.0.tgz", @@ -2858,6 +3911,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", + "dev": true, + "license": "MIT", + "peer": true + }, "node_modules/jiti": { "version": "2.5.1", "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.5.1.tgz", @@ -2875,6 +3936,55 @@ "dev": true, "license": "MIT" }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/jsdom": { + "version": "27.2.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-27.2.0.tgz", + "integrity": "sha512-454TI39PeRDW1LgpyLPyURtB4Zx1tklSr6+OFOipsxGUH1WMTvk6C65JQdrj455+DP2uJ1+veBEHTGFKWVLFoA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@acemir/cssom": "^0.9.23", + "@asamuzakjp/dom-selector": "^6.7.4", + "cssstyle": "^5.3.3", + "data-urls": "^6.0.0", + "decimal.js": "^10.6.0", + "html-encoding-sniffer": "^4.0.0", + "http-proxy-agent": "^7.0.2", + "https-proxy-agent": "^7.0.6", + "is-potential-custom-element-name": "^1.0.1", + "parse5": "^8.0.0", + "saxes": "^6.0.0", + "symbol-tree": "^3.2.4", + "tough-cookie": "^6.0.0", + "w3c-xmlserializer": "^5.0.0", + "webidl-conversions": "^8.0.0", + "whatwg-encoding": "^3.1.1", + "whatwg-mimetype": "^4.0.0", + "whatwg-url": "^15.1.0", + "ws": "^8.18.3", + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + }, + "peerDependencies": { + "canvas": "^3.0.0" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, "node_modules/json-schema-traverse": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", @@ -3175,6 +4285,17 @@ "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, + "node_modules/lz-string": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz", + "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==", + "dev": true, + "license": "MIT", + "peer": true, + "bin": { + "lz-string": "bin/bin.js" + } + }, "node_modules/magic-string": { "version": "0.30.18", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.18.tgz", @@ -3185,6 +4306,14 @@ "@jridgewell/sourcemap-codec": "^1.5.5" } }, + "node_modules/mdn-data": { + "version": "2.12.2", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.12.2.tgz", + "integrity": "sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA==", + "dev": true, + "license": "CC0-1.0", + "peer": true + }, "node_modules/minimatch": { "version": "10.0.3", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.3.tgz", @@ -3256,6 +4385,14 @@ "integrity": "sha512-GEQWEZmfkOGLdd3XK8ryrfWz3AIP8YymVXiPHEdewrUq7mh0qrKrfHLNCXcbB6sTnMLnOZ3ztSiKcciFUkIJwQ==", "license": "MIT" }, + "node_modules/ms": { + "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", + "peer": true + }, "node_modules/nanoid": { "version": "3.3.11", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", @@ -3286,6 +4423,20 @@ "react-dom": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc" } }, + "node_modules/parse5": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-8.0.0.tgz", + "integrity": "sha512-9m4m5GSgXjL4AjumKzq1Fgfp3Z8rsvjRNbnkVwfu2ImRqE5D0LnY2QfDen18FSY9C573YU5XxSapdHZTZ2WolA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "entities": "^6.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, "node_modules/path-parse": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", @@ -3329,6 +4480,22 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/pretty-format": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", + "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -3360,6 +4527,14 @@ "react": "^19.1.1" } }, + "node_modules/react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", + "dev": true, + "license": "MIT", + "peer": true + }, "node_modules/react-refresh": { "version": "0.17.0", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz", @@ -3509,6 +4684,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, "node_modules/rsbuild-plugin-dts": { "version": "0.12.2", "resolved": "https://registry.npmjs.org/rsbuild-plugin-dts/-/rsbuild-plugin-dts-0.12.2.tgz", @@ -3539,6 +4724,28 @@ } } }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/saxes": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", + "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", + "dev": true, + "license": "ISC", + "peer": true, + "dependencies": { + "xmlchars": "^2.2.0" + }, + "engines": { + "node": ">=v12.22.7" + } + }, "node_modules/scheduler": { "version": "0.26.0", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz", @@ -3681,6 +4888,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", + "dev": true, + "license": "MIT", + "peer": true + }, "node_modules/tailwind-merge": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.3.1.tgz", @@ -3794,6 +5009,56 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/tldts": { + "version": "7.0.19", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-7.0.19.tgz", + "integrity": "sha512-8PWx8tvC4jDB39BQw1m4x8y5MH1BcQ5xHeL2n7UVFulMPH/3Q0uiamahFJ3lXA0zO2SUyRXuVVbWSDmstlt9YA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "tldts-core": "^7.0.19" + }, + "bin": { + "tldts": "bin/cli.js" + } + }, + "node_modules/tldts-core": { + "version": "7.0.19", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-7.0.19.tgz", + "integrity": "sha512-lJX2dEWx0SGH4O6p+7FPwYmJ/bu1JbcGJ8RLaG9b7liIgZ85itUVEPbMtWRVrde/0fnDPEPHW10ZsKW3kVsE9A==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/tough-cookie": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-6.0.0.tgz", + "integrity": "sha512-kXuRi1mtaKMrsLUxz3sQYvVl37B0Ns6MzfrtV5DvJceE9bPyspOqk9xxv7XbZWcfLWbFmm997vl83qUWVJA64w==", + "dev": true, + "license": "BSD-3-Clause", + "peer": true, + "dependencies": { + "tldts": "^7.0.5" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/tr46": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-6.0.0.tgz", + "integrity": "sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "punycode": "^2.3.1" + }, + "engines": { + "node": ">=20" + } + }, "node_modules/tsconfig-paths": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz", @@ -3815,6 +5080,26 @@ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", "license": "0BSD" }, + "node_modules/tsx": { + "version": "4.20.6", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.20.6.tgz", + "integrity": "sha512-ytQKuwgmrrkDTFP4LjR0ToE2nqgy886GpvRSpU0JAnrdBYppuY5rLkRUYPU1yCryb24SsKBTL/hlDQAEFVwtZg==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "~0.25.0", + "get-tsconfig": "^4.7.5" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + }, "node_modules/typescript": { "version": "5.9.2", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.2.tgz", @@ -3899,6 +5184,113 @@ } } }, + "node_modules/w3c-xmlserializer": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", + "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/webidl-conversions": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-8.0.0.tgz", + "integrity": "sha512-n4W4YFyz5JzOfQeA8oN7dUYpR+MBP3PIUsn2jLjWXwK5ASUzt0Jc/A5sAUZoCYFJRGF0FBKJ+1JjN43rNdsQzA==", + "dev": true, + "license": "BSD-2-Clause", + "peer": true, + "engines": { + "node": ">=20" + } + }, + "node_modules/whatwg-encoding": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz", + "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "iconv-lite": "0.6.3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/whatwg-mimetype": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", + "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/whatwg-url": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-15.1.0.tgz", + "integrity": "sha512-2ytDk0kiEj/yu90JOAp44PVPUkO9+jVhyf+SybKlRHSDlvOOZhdPIrr7xTH64l4WixO2cP+wQIcgujkGBPPz6g==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "tr46": "^6.0.0", + "webidl-conversions": "^8.0.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/ws": { + "version": "8.18.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", + "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xml-name-validator": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz", + "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==", + "dev": true, + "license": "Apache-2.0", + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "dev": true, + "license": "MIT", + "peer": true + }, "node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", diff --git a/package.json b/package.json index f6b6209..c4f2ffc 100644 --- a/package.json +++ b/package.json @@ -53,7 +53,8 @@ "check": "biome check .", "fix": "biome check --fix --unsafe", "typecheck": "tsc --build --force", - "test": "node --test test/*.test.{js,ts}", + "test": "tsx --tsconfig tsconfig.test.json --test test/**/*.test.{js,ts,tsx}", + "test:snapshots": "tsx --test-update-snapshots --tsconfig tsconfig.test.json --test test/**/*.test.{js,ts,tsx}", "prepack": "npm run build" }, "dependencies": { @@ -90,9 +91,11 @@ "@rslib/core": "^0.12.2", "@tailwindcss/postcss": "^4.1.12", "@tanstack/react-query": "^5.85.0", + "@testing-library/react": "^16.3.0", "@types/node": "^22.17.2", "@types/react": "^19.1.10", "@types/react-dom": "^19.1.7", + "global-jsdom": "^27.0.0", "monaco-editor": "^0.52.2", "next-themes": "^0.4.6", "postcss": "^8.5.6", @@ -102,6 +105,7 @@ "sonner": "^2.0.7", "tailwindcss": "^4.1.12", "tailwindcss-animate": "^1.0.7", + "tsx": "^4.20.6", "typescript": "^5.9.2" }, "packageManager": "npm@10.9.3+sha512.e84875bb943e908557780f1eee5d9cfc7a67145730ae4b77ef10ccba30f96ded6096859af69ea3dc5b2fde60725d79aa247cbed9c12544c30bf28a4d4fbc4825" diff --git a/postcss.config.js b/postcss.config.js index 5d2d571..644f7fe 100644 --- a/postcss.config.js +++ b/postcss.config.js @@ -10,11 +10,14 @@ const AllowedAtRules = new Set(["media", "supports", "layer"]); /** @type {() => import("postcss").Plugin} */ const cssScopingPlugin = () => { return { - postcssPlugin: 'replace-root-with-new_design', + postcssPlugin: "replace-root-with-new_design", Once(root) { // Add .jsonjoy class selector to all selectors - root.walkRules(rule => { - if (rule.parent?.type === "atrule" && !AllowedAtRules.has(rule.parent.name)) { + root.walkRules((rule) => { + if ( + rule.parent?.type === "atrule" && + !AllowedAtRules.has(rule.parent.name) + ) { return; } const newSelectors = new Set(); @@ -45,18 +48,21 @@ const cssScopingPlugin = () => { // Prefix built-in animation names from tailwind with jsonjoy- // See https://tailwindcss.com/docs/animation - root.walkDecls(decl => { + root.walkDecls((decl) => { if (decl.variable) { const animateMatch = /--animate-([a-zA-Z0-9_-]+)/.exec(decl.prop); if (animateMatch) { const animationName = animateMatch[1]; - decl.value = decl.value.replace(new RegExp(`\\b${animationName}\\b`, "g"), `jsonjoy-${animationName}`); + decl.value = decl.value.replace( + new RegExp(`\\b${animationName}\\b`, "g"), + `jsonjoy-${animationName}`, + ); } } }); // Prefix @layer with jsonjoy- - root.walkAtRules(atRule => { + root.walkAtRules((atRule) => { if (atRule.name === "layer" && !atRule.params.startsWith("jsonjoy-")) { atRule.params = `jsonjoy-${atRule.params}`; } @@ -64,50 +70,66 @@ const cssScopingPlugin = () => { // Prefix built-in keyframe names from tailwind with jsonjoy- // See https://tailwindcss.com/docs/animation - root.walkAtRules(atRule => { - if (atRule.name === "keyframes" && !atRule.params.startsWith("jsonjoy-")) { + root.walkAtRules((atRule) => { + if ( + atRule.name === "keyframes" && + !atRule.params.startsWith("jsonjoy-") + ) { atRule.params = `jsonjoy-${atRule.params}`; } }); // Prefix CSS custom properties with jsonjoy- - root.walkDecls(decl => { + root.walkDecls((decl) => { if (decl.variable && !decl.prop.startsWith("--jsonjoy-")) { decl.prop = `--jsonjoy-${decl.prop.substring(2)}`; } }); // Prefix usages of CSS custom properties [var(--name)] with jsonjoy- - root.walkDecls(decl => { - decl.value = decl.value.replace(/var\(--([a-zA-Z0-9_\-]+)/g, (match, name) => { - return name.startsWith("jsonjoy-") ? match : `var(--jsonjoy-${name}`; - }); + root.walkDecls((decl) => { + decl.value = decl.value.replace( + /var\(--([a-zA-Z0-9_-]+)/g, + (match, name) => { + return name.startsWith("jsonjoy-") + ? match + : `var(--jsonjoy-${name}`; + }, + ); }); // Prefix custom @property rules with jsonjoy- - root.walkAtRules(atRule => { - if (atRule.name === "property" && !atRule.params.startsWith("--jsonjoy-")) { + root.walkAtRules((atRule) => { + if ( + atRule.name === "property" && + !atRule.params.startsWith("--jsonjoy-") + ) { atRule.params = `--jsonjoy-${atRule.params.substring(2)}`; } }); - } + }, }; }; /** * Adds the class name as a scope to the selector. - * + * * - `table foo` => `table.jsonjoy foo` * - `#foo .bar` => `.jsonjoy#foo .bar` * - `.foo .bar` => `.jsonjoy.foo .bar` * - `[data-attr="foo bar"] baz` => `.jsonjoy[data-attr="foo bar"] baz` * - `:is(.foo, .bar) baz` => `.jsonjoy:is(.foo, .bar) baz` - * @param {string} className - * @param {string} selector + * @param {string} className + * @param {string} selector */ function addClassSelectorScope(className, selector) { // ID selector, class selector, attribute selector or pseudo-class / pseudo-element - if (selector.startsWith(".") || selector.startsWith("#") || selector.startsWith("[") || selector.startsWith(":")) { + if ( + selector.startsWith(".") || + selector.startsWith("#") || + selector.startsWith("[") || + selector.startsWith(":") + ) { return `.${className}${selector}`; } @@ -121,7 +143,6 @@ function addClassSelectorScope(className, selector) { } return selector; - } /** @type {{plugins:import("postcss").AcceptedPlugin[] }} */ diff --git a/src/components/SchemaEditor/AddFieldButton.tsx b/src/components/SchemaEditor/AddFieldButton.tsx index 83022a8..1366ad2 100644 --- a/src/components/SchemaEditor/AddFieldButton.tsx +++ b/src/components/SchemaEditor/AddFieldButton.tsx @@ -17,9 +17,9 @@ import { TooltipProvider, TooltipTrigger, } from "../../components/ui/tooltip.tsx"; +import { useTranslation } from "../../hooks/use-translation.ts"; import type { NewField, SchemaType } from "../../types/jsonSchema.ts"; import SchemaTypeSelector from "./SchemaTypeSelector.tsx"; -import { useTranslation } from "../../hooks/use-translation.ts"; interface AddFieldButtonProps { onAddField: (field: NewField) => void; diff --git a/src/components/SchemaEditor/JsonSchemaEditor.tsx b/src/components/SchemaEditor/JsonSchemaEditor.tsx index 8a0cea3..87c8228 100644 --- a/src/components/SchemaEditor/JsonSchemaEditor.tsx +++ b/src/components/SchemaEditor/JsonSchemaEditor.tsx @@ -11,15 +11,16 @@ import { TabsList, TabsTrigger, } from "../../components/ui/tabs.tsx"; +import { useTranslation } from "../../hooks/use-translation.ts"; import { cn } from "../../lib/utils.ts"; import type { JSONSchema } from "../../types/jsonSchema.ts"; import JsonSchemaVisualizer from "./JsonSchemaVisualizer.tsx"; import SchemaVisualEditor from "./SchemaVisualEditor.tsx"; -import { useTranslation } from "../../hooks/use-translation.ts"; /** @public */ export interface JsonSchemaEditorProps { schema?: JSONSchema; + readOnly: boolean; setSchema?: (schema: JSONSchema) => void; className?: string; } @@ -27,6 +28,7 @@ export interface JsonSchemaEditorProps { /** @public */ const JsonSchemaEditor: FC = ({ schema = { type: "object" }, + readOnly = false, setSchema, className, }) => { @@ -79,7 +81,12 @@ const JsonSchemaEditor: FC = ({ return (
{/* For mobile screens - show as tabs */}
@@ -113,7 +120,11 @@ const JsonSchemaEditor: FC = ({ isFullscreen ? "h-screen" : "h-[500px]", )} > - + = ({ className="h-full min-h-0" style={{ width: `${leftPanelWidth}%` }} > - +
{/** biome-ignore lint/a11y/noStaticElementInteractions: What exactly does this div do? */}
= ({ return (
diff --git a/src/components/SchemaEditor/SchemaField.tsx b/src/components/SchemaEditor/SchemaField.tsx index 36adee3..41a881a 100644 --- a/src/components/SchemaEditor/SchemaField.tsx +++ b/src/components/SchemaEditor/SchemaField.tsx @@ -1,4 +1,5 @@ import React, { Suspense } from "react"; +import { useTranslation } from "../../hooks/use-translation.ts"; import type { JSONSchema as JSONSchemaType, NewField, @@ -11,7 +12,6 @@ import { withObjectSchema, } from "../../types/jsonSchema.ts"; import SchemaPropertyEditor from "./SchemaPropertyEditor.tsx"; -import { useTranslation } from "../../hooks/use-translation.ts"; // This component is now just a simple wrapper around SchemaPropertyEditor // to maintain backward compatibility during migration @@ -19,6 +19,7 @@ interface SchemaFieldProps { name: string; schema: JSONSchemaType; required?: boolean; + readOnly: boolean; onDelete: () => void; onEdit: (updatedField: NewField) => void; onAddField?: (newField: NewField) => void; @@ -27,7 +28,15 @@ interface SchemaFieldProps { } const SchemaField: React.FC = (props) => { - const { name, schema, required = false, onDelete, onEdit, depth = 0 } = props; + const { + name, + schema, + required = false, + onDelete, + onEdit, + depth = 0, + readOnly = false, + } = props; // Handle name change const handleNameChange = (newName: string) => { @@ -95,6 +104,7 @@ const SchemaField: React.FC = (props) => { return ( void; onEditField: (name: string, updatedField: NewField) => void; onDeleteField: (name: string) => void; @@ -21,6 +22,7 @@ const SchemaFieldList: FC = ({ schema, onEditField, onDeleteField, + readOnly = false, }) => { const t = useTranslation(); @@ -103,7 +105,7 @@ const SchemaFieldList: FC = ({ const validationTree = useMemo( () => buildValidationTree(schema, t), - [schema, t] + [schema, t], ); return ( @@ -121,6 +123,7 @@ const SchemaFieldList: FC = ({ handleRequiredChange(property.name, required) } onSchemaChange={(schema) => handleSchemaChange(property.name, schema)} + readOnly={readOnly} /> ))}
diff --git a/src/components/SchemaEditor/SchemaPropertyEditor.tsx b/src/components/SchemaEditor/SchemaPropertyEditor.tsx index e85a0da..dfe4a2e 100644 --- a/src/components/SchemaEditor/SchemaPropertyEditor.tsx +++ b/src/components/SchemaEditor/SchemaPropertyEditor.tsx @@ -22,6 +22,7 @@ export interface SchemaPropertyEditorProps { name: string; schema: JSONSchema; required: boolean; + readOnly: boolean; validationNode?: ValidationTreeNode; onDelete: () => void; onNameChange: (newName: string) => void; @@ -34,6 +35,7 @@ export const SchemaPropertyEditor: React.FC = ({ name, schema, required, + readOnly = false, validationNode, onDelete, onNameChange, @@ -113,7 +115,7 @@ export const SchemaPropertyEditor: React.FC = ({ {/* Property name */}
- {isEditingName ? ( + {!readOnly && isEditingName ? ( setTempName(e.target.value)} @@ -135,7 +137,7 @@ export const SchemaPropertyEditor: React.FC = ({ )} {/* Description */} - {isEditingDesc ? ( + {!readOnly && isEditingDesc ? ( setTempDesc(e.target.value)} @@ -171,6 +173,7 @@ export const SchemaPropertyEditor: React.FC = ({
{ onSchemaChange({ ...asObjectSchema(schema), @@ -182,7 +185,7 @@ export const SchemaPropertyEditor: React.FC = ({ {/* Required toggle */} -
+ {!readOnly && ( +
+ +
+ )}
{/* Type-specific editor */} {expanded && (
+ {readOnly && tempDesc &&

{tempDesc}

} void; } @@ -20,6 +21,7 @@ export interface SchemaVisualEditorProps { const SchemaVisualEditor: FC = ({ schema, onChange, + readOnly = false, }) => { const t = useTranslation(); // Handle adding a top-level field @@ -120,9 +122,11 @@ const SchemaVisualEditor: FC = ({ return (
-
- -
+ {!readOnly && ( +
+ +
+ )}
{!hasFields ? ( @@ -133,6 +137,7 @@ const SchemaVisualEditor: FC = ({ ) : ( void; className?: string; + readOnly: boolean; } const typeOptions: SchemaType[] = [ @@ -23,6 +24,7 @@ export const TypeDropdown: React.FC = ({ value, onChange, className, + readOnly, }) => { const t = useTranslation(); const [isOpen, setIsOpen] = useState(false); @@ -50,15 +52,16 @@ export const TypeDropdown: React.FC = ({ {isOpen && ( diff --git a/src/components/SchemaEditor/TypeEditor.tsx b/src/components/SchemaEditor/TypeEditor.tsx index bf0d0be..fb20c45 100644 --- a/src/components/SchemaEditor/TypeEditor.tsx +++ b/src/components/SchemaEditor/TypeEditor.tsx @@ -16,6 +16,7 @@ const ArrayEditor = lazy(() => import("./types/ArrayEditor.tsx")); export interface TypeEditorProps { schema: JSONSchema; + readOnly: boolean; validationNode: ValidationTreeNode | undefined; onChange: (schema: ObjectJSONSchema) => void; depth?: number; @@ -26,6 +27,7 @@ const TypeEditor: React.FC = ({ validationNode, onChange, depth = 0, + readOnly = false, }) => { const type = withObjectSchema( schema, @@ -37,6 +39,7 @@ const TypeEditor: React.FC = ({ Loading editor...
}> {type === "string" && ( = ({ )} {type === "number" && ( = ({ )} {type === "integer" && ( = ({ )} {type === "boolean" && ( = ({ )} {type === "object" && ( = ({ )} {type === "array" && ( = ({ schema, + readOnly = false, validationNode, onChange, depth = 0, @@ -111,80 +112,104 @@ const ArrayEditor: React.FC = ({ return (
{/* Array validation settings */} -
-
- - { - const value = e.target.value ? Number(e.target.value) : undefined; - setMinItems(value); - // Don't update immediately to avoid too many rerenders - }} - onBlur={handleValidationChange} - placeholder={t.arrayMinimumPlaceholder} - className={cn("h-8", !!minMaxError && "border-destructive")} - /> + {(!readOnly || !!maxItems || !!minItems) && ( +
+ {(!readOnly || !!minItems) && ( +
+ + { + const value = e.target.value + ? Number(e.target.value) + : undefined; + setMinItems(value); + // Don't update immediately to avoid too many rerenders + }} + onBlur={handleValidationChange} + placeholder={t.arrayMinimumPlaceholder} + className={cn("h-8", !!minMaxError && "border-destructive")} + /> +
+ )} + + {(!readOnly || !!maxItems) && ( +
+ + { + const value = e.target.value + ? Number(e.target.value) + : undefined; + setMaxItems(value); + // Don't update immediately to avoid too many rerenders + }} + onBlur={handleValidationChange} + placeholder={t.arrayMaximumPlaceholder} + className={cn("h-8", !!minMaxError && "border-destructive")} + /> +
+ )} + {(!!minMaxError || !!minItemsError || !!maxItemsError) && ( +
+ {[minMaxError, minItemsError ?? maxItemsError] + .filter(Boolean) + .join("\n")} +
+ )}
- -
- - { - const value = e.target.value ? Number(e.target.value) : undefined; - setMaxItems(value); - // Don't update immediately to avoid too many rerenders + )} + + {(!readOnly || !!uniqueItems) && ( +
+ { + setUniqueItems(checked); + setTimeout(handleValidationChange, 0); }} - onBlur={handleValidationChange} - placeholder={t.arrayMaximumPlaceholder} - className={cn("h-8", !!minMaxError && "border-destructive")} /> +
- {(!!minMaxError || !!minItemsError || !!maxItemsError) && ( -
- {[minMaxError, minItemsError ?? maxItemsError] - .filter(Boolean) - .join("\n")} -
- )} -
- -
- { - setUniqueItems(checked); - setTimeout(handleValidationChange, 0); - }} - /> - -
+ )} {/* Array item type editor */} -
+
{ handleItemSchemaChange({ @@ -197,6 +222,7 @@ const ArrayEditor: React.FC = ({ {/* Item schema editor */} = ({ schema, onChange }) => { +const BooleanEditor: React.FC = ({ + schema, + onChange, + readOnly = false, +}) => { const t = useTranslation(); const allowTrueId = useId(); const allowFalseId = useId(); @@ -73,41 +77,60 @@ const BooleanEditor: React.FC = ({ schema, onChange }) => { onChange(updatedValidation); }; + const hasEnum = enumValues && enumValues.length > 0; + return (
-
- - -
-
- handleAllowedChange(true, checked)} - /> - -
- -
- handleAllowedChange(false, checked)} - /> - -
+ {readOnly && !hasEnum && ( +

+ {t.booleanNoConstraint} +

+ )} + {(!readOnly || !allowsTrue || !allowsFalse) && ( +
+ {(!readOnly || hasEnum) && ( + <> + + +
+
+ + handleAllowedChange(true, checked) + } + /> + +
+ +
+ + handleAllowedChange(false, checked) + } + /> + +
+
+ + )} + + {!allowsTrue && !allowsFalse && ( +

+ {t.booleanNeitherWarning} +

+ )}
- - {!allowsTrue && !allowsFalse && ( -

- {t.booleanNeitherWarning} -

- )} -
+ )}
); }; diff --git a/src/components/SchemaEditor/types/NumberEditor.tsx b/src/components/SchemaEditor/types/NumberEditor.tsx index b847924..f8d7460 100644 --- a/src/components/SchemaEditor/types/NumberEditor.tsx +++ b/src/components/SchemaEditor/types/NumberEditor.tsx @@ -28,6 +28,7 @@ const NumberEditor: React.FC = ({ validationNode, onChange, integer = false, + readOnly = false, }) => { const [enumValue, setEnumValue] = useState(""); const t = useTranslation(); @@ -209,226 +210,266 @@ const NumberEditor: React.FC = ({ [validationNode], ); + const hasConstraint = + !!minimum || + !!maximum || + !!exclusiveMinimum || + !!exclusiveMaximum || + !!multipleOf || + enumValues.length > 0; + return (
-
-
- {!!minMaxError && ( -
{minMaxError}
- )} - {!!redundantMinError && ( -
- {redundantMinError} + {readOnly && !hasConstraint && ( +

+ {t.numberNoConstraint} +

+ )} + + {(!readOnly || hasConstraint) && ( +
+
+ {!!minMaxError && ( +
+ {minMaxError} +
+ )} + {!!redundantMinError && ( +
+ {redundantMinError} +
+ )} + {!!redundantMaxError && ( +
+ {redundantMaxError} +
+ )} + {!!enumError && ( +
{enumError}
+ )} +
+ + {(!readOnly || !!minimum) && ( +
+ + { + const value = e.target.value + ? Number(e.target.value) + : undefined; + handleValidationChange("minimum", value); + }} + placeholder={t.numberMinimumPlaceholder} + className={cn( + "h-8", + minimum !== undefined && + (!!minMaxError || !!redundantMinError) && + "border-destructive", + )} + step={integer ? 1 : "any"} + />
)} - {!!redundantMaxError && ( -
- {redundantMaxError} + + {(!readOnly || !!maximum) && ( +
+ + { + const value = e.target.value + ? Number(e.target.value) + : undefined; + handleValidationChange("maximum", value); + }} + placeholder={t.numberMaximumPlaceholder} + className={cn( + "h-8", + maximum !== undefined && + (!!minMaxError || !!redundantMaxError) && + "border-destructive", + )} + step={integer ? 1 : "any"} + />
)} - {!!enumError && ( -
{enumError}
- )}
+ )} + + {(!readOnly || !!exclusiveMaximum || !!exclusiveMinimum) && ( +
+ {(!readOnly || !!exclusiveMinimum) && ( +
+ + { + const value = e.target.value + ? Number(e.target.value) + : undefined; + handleValidationChange("exclusiveMinimum", value); + }} + placeholder={t.numberExclusiveMinimumPlaceholder} + className={cn( + "h-8", + exclusiveMinimum !== undefined && + (!!minMaxError || !!redundantMinError) && + "border-destructive", + )} + step={integer ? 1 : "any"} + /> +
+ )} -
- - { - const value = e.target.value ? Number(e.target.value) : undefined; - handleValidationChange("minimum", value); - }} - placeholder={t.numberMinimumPlaceholder} - className={cn( - "h-8", - minimum !== undefined && - (!!minMaxError || !!redundantMinError) && - "border-destructive", - )} - step={integer ? 1 : "any"} - /> + {(!readOnly || !!exclusiveMaximum) && ( +
+ + { + const value = e.target.value + ? Number(e.target.value) + : undefined; + handleValidationChange("exclusiveMaximum", value); + }} + placeholder={t.numberExclusiveMaximumPlaceholder} + className={cn( + "h-8", + exclusiveMaximum !== undefined && + (!!minMaxError || !!redundantMaxError) && + "border-destructive", + )} + step={integer ? 1 : "any"} + /> +
+ )}
+ )} + {(!readOnly || !!multipleOf) && (
{ const value = e.target.value ? Number(e.target.value) : undefined; - handleValidationChange("maximum", value); + handleValidationChange("multipleOf", value); }} - placeholder={t.numberMaximumPlaceholder} - className={cn( - "h-8", - maximum !== undefined && - (!!minMaxError || !!redundantMaxError) && - "border-destructive", - )} + placeholder={t.numberMultipleOfPlaceholder} + className={cn("h-8", !!multipleOfError && "border-destructive")} + min={0} step={integer ? 1 : "any"} /> + {!!multipleOfError && ( +
+ {multipleOfError} +
+ )}
-
+ )} -
-
-
); }; diff --git a/src/components/SchemaEditor/types/ObjectEditor.tsx b/src/components/SchemaEditor/types/ObjectEditor.tsx index 05bd561..64d0cde 100644 --- a/src/components/SchemaEditor/types/ObjectEditor.tsx +++ b/src/components/SchemaEditor/types/ObjectEditor.tsx @@ -16,6 +16,7 @@ const ObjectEditor: React.FC = ({ validationNode, onChange, depth = 0, + readOnly = false, }) => { const t = useTranslation(); @@ -114,6 +115,7 @@ const ObjectEditor: React.FC = ({
{properties.map((property) => ( = ({
)} -
- -
+ {!readOnly && ( +
+ +
+ )}
); }; diff --git a/src/components/SchemaEditor/types/StringEditor.tsx b/src/components/SchemaEditor/types/StringEditor.tsx index e269117..fbc508a 100644 --- a/src/components/SchemaEditor/types/StringEditor.tsx +++ b/src/components/SchemaEditor/types/StringEditor.tsx @@ -24,6 +24,7 @@ const StringEditor: React.FC = ({ schema, validationNode, onChange, + readOnly = false, }) => { const t = useTranslation(); const [enumValue, setEnumValue] = useState(""); @@ -137,170 +138,206 @@ const StringEditor: React.FC = ({ [validationNode], ); + const minLengthValue = minLength ?? ""; + const maxLengthValue = maxLength ?? ""; + const patternValue = pattern ?? ""; + const formatValue = format || "none"; + const needsDetail = + !readOnly || + minLengthValue !== "" || + maxLengthValue !== "" || + patternValue !== "" || + formatValue !== "none" || + enumValues.length > 0; + return (
+ {readOnly && !needsDetail && ( +

+ {t.stringNoConstraint} +

+ )} + + {(!readOnly || minLengthValue !== "") && ( +
+ + { + const value = e.target.value + ? Number(e.target.value) + : undefined; + handleValidationChange("minLength", value); + }} + placeholder={t.stringMinimumLengthPlaceholder} + className={cn( + "h-8", + (!!minMaxError || !!minLengthError) && "border-destructive", + )} + /> +
+ )} + + {(!readOnly || maxLengthValue !== "") && ( +
+ + { + const value = e.target.value + ? Number(e.target.value) + : undefined; + handleValidationChange("maxLength", value); + }} + placeholder={t.stringMaximumLengthPlaceholder} + className={cn( + "h-8", + (!!minMaxError || !!maxLengthError) && "border-destructive", + )} + /> +
+ )} + {(!!minMaxError || !!minLengthError || !!maxLengthError) && ( +
+ {[minMaxError, minLengthError ?? maxLengthError] + .filter(Boolean) + .join("\n")} +
+ )} +
+ + {(!readOnly || patternValue !== "") && (
{ - const value = e.target.value ? Number(e.target.value) : undefined; - handleValidationChange("minLength", value); + const value = e.target.value || undefined; + handleValidationChange("pattern", value); }} - placeholder={t.stringMinimumLengthPlaceholder} - className={cn( - "h-8", - (!!minMaxError || !!minLengthError) && "border-destructive", - )} + placeholder={t.stringPatternPlaceholder} + className="h-8" />
+ )} + {(!readOnly || formatValue !== "none") && (
- { - const value = e.target.value ? Number(e.target.value) : undefined; - handleValidationChange("maxLength", value); +
- {(!!minMaxError || !!minLengthError || !!maxLengthError) && ( -
- {[minMaxError, minLengthError ?? maxLengthError] - .filter(Boolean) - .join("\n")} -
- )} -
- -
- - { - const value = e.target.value || undefined; - handleValidationChange("pattern", value); - }} - placeholder={t.stringPatternPlaceholder} - className="h-8" - /> -
+ )} -
- - -
- -
- + {(!readOnly || enumValues.length > 0) && ( +
+ -
- {enumValues.length > 0 ? ( - enumValues.map((value) => ( -
- {value} - -
- )) - ) : ( -

- {t.stringAllowedValuesEnumNone} -

- )} -
+ {value} + +
+ )) + ) : ( +

+ {t.stringAllowedValuesEnumNone} +

+ )} +
-
- setEnumValue(e.target.value)} - placeholder={t.stringAllowedValuesEnumAddPlaceholder} - className="h-8 text-xs flex-1" - onKeyDown={(e) => e.key === "Enter" && handleAddEnumValue()} - /> - +
+ setEnumValue(e.target.value)} + placeholder={t.stringAllowedValuesEnumAddPlaceholder} + className="h-8 text-xs flex-1" + onKeyDown={(e) => e.key === "Enter" && handleAddEnumValue()} + /> + +
-
+ )}
); }; diff --git a/src/components/features/JsonValidator.tsx b/src/components/features/JsonValidator.tsx index d75b495..6492a8a 100644 --- a/src/components/features/JsonValidator.tsx +++ b/src/components/features/JsonValidator.tsx @@ -10,15 +10,15 @@ import { DialogTitle, } from "../../components/ui/dialog.tsx"; import { useMonacoTheme } from "../../hooks/use-monaco-theme.ts"; +import { + formatTranslation, + useTranslation, +} from "../../hooks/use-translation.ts"; import type { JSONSchema } from "../../types/jsonSchema.ts"; import { type ValidationResult, validateJson, } from "../../utils/jsonValidator.ts"; -import { - formatTranslation, - useTranslation, -} from "../../hooks/use-translation.ts"; /** @public */ export interface JsonValidatorProps { diff --git a/src/components/features/SchemaInferencer.tsx b/src/components/features/SchemaInferencer.tsx index 9ef8baa..dc11bc8 100644 --- a/src/components/features/SchemaInferencer.tsx +++ b/src/components/features/SchemaInferencer.tsx @@ -11,9 +11,9 @@ import { DialogTitle, } from "../../components/ui/dialog.tsx"; import { useMonacoTheme } from "../../hooks/use-monaco-theme.ts"; +import { useTranslation } from "../../hooks/use-translation.ts"; import { createSchemaFromJson } from "../../lib/schema-inference.ts"; import type { JSONSchema } from "../../types/jsonSchema.ts"; -import { useTranslation } from "../../hooks/use-translation.ts"; /** @public */ export interface SchemaInferencerProps { @@ -106,7 +106,9 @@ export function SchemaInferencer({ - + diff --git a/src/components/ui/badge.tsx b/src/components/ui/badge.tsx index 61fa432..4d6ce99 100644 --- a/src/components/ui/badge.tsx +++ b/src/components/ui/badge.tsx @@ -1,4 +1,4 @@ -import { type VariantProps, cva } from "class-variance-authority"; +import { cva, type VariantProps } from "class-variance-authority"; import type { HTMLAttributes } from "react"; import { cn } from "../../lib/utils.ts"; diff --git a/src/components/ui/button.tsx b/src/components/ui/button.tsx index 0687ad1..eb9d077 100644 --- a/src/components/ui/button.tsx +++ b/src/components/ui/button.tsx @@ -1,5 +1,5 @@ import { Slot } from "@radix-ui/react-slot"; -import { type VariantProps, cva } from "class-variance-authority"; +import { cva, type VariantProps } from "class-variance-authority"; import { type ButtonHTMLAttributes, forwardRef } from "react"; import { cn } from "../../lib/utils.ts"; diff --git a/src/components/ui/dialog.tsx b/src/components/ui/dialog.tsx index ff21e62..9e133ea 100644 --- a/src/components/ui/dialog.tsx +++ b/src/components/ui/dialog.tsx @@ -4,8 +4,8 @@ import { X } from "lucide-react"; import { type ComponentPropsWithoutRef, type ComponentRef, - type HTMLAttributes, forwardRef, + type HTMLAttributes, useId, } from "react"; import { cn } from "../../lib/utils.ts"; diff --git a/src/components/ui/label.tsx b/src/components/ui/label.tsx index 62e71f5..2d2238a 100644 --- a/src/components/ui/label.tsx +++ b/src/components/ui/label.tsx @@ -1,5 +1,5 @@ import * as LabelPrimitive from "@radix-ui/react-label"; -import { type VariantProps, cva } from "class-variance-authority"; +import { cva, type VariantProps } from "class-variance-authority"; import { type ComponentPropsWithoutRef, diff --git a/src/components/ui/select.tsx b/src/components/ui/select.tsx index f0a85de..dc19089 100644 --- a/src/components/ui/select.tsx +++ b/src/components/ui/select.tsx @@ -81,7 +81,7 @@ const SelectContent = forwardRef< position === "popper" && "data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1", className, - "jsonjoy" + "jsonjoy", )} position={position} {...props} diff --git a/src/i18n/locales/de.ts b/src/i18n/locales/de.ts index 743b623..de385f7 100644 --- a/src/i18n/locales/de.ts +++ b/src/i18n/locales/de.ts @@ -49,6 +49,7 @@ export const de: Translation = { schemaEditorEditModeVisual: "Visuell", schemaEditorEditModeJson: "JSON", + arrayNoConstraint: "Keine Einschränkung", arrayMinimumLabel: "Mindestanzahl an Elementen", arrayMinimumPlaceholder: "Kein Minimum", arrayMaximumLabel: "Maximale Anzahl an Elemente", @@ -60,11 +61,13 @@ export const de: Translation = { arrayValidationErrorContainsMinMax: "'minContains' darf nicht größer als 'maxContains' sein.", + booleanNoConstraint: "Keine Einschränkung", booleanAllowFalseLabel: "Falsch-Werte erlauben", booleanAllowTrueLabel: "Wahr-Werte erlauben", booleanNeitherWarning: "Achtung: Mindestens einer von beiden Werten muss erlaubt sein.", + numberNoConstraint: "Keine Einschränkung", numberMinimumLabel: "Minimalwert", numberMinimumPlaceholder: "Kein Minimum", numberMaximumLabel: "Maximalwert", @@ -91,6 +94,7 @@ export const de: Translation = { objectValidationErrorMinMax: "'minProperties' darf nicht größer als 'maxProperties' sein.", + stringNoConstraint: "Keine Einschränkung", stringMinimumLengthLabel: "Minimale Länge", stringMinimumLengthPlaceholder: "Kein Minimum", stringMaximumLengthLabel: "Maximale Länge", diff --git a/src/i18n/locales/en.ts b/src/i18n/locales/en.ts index 87d1fa8..ffa2f02 100644 --- a/src/i18n/locales/en.ts +++ b/src/i18n/locales/en.ts @@ -48,6 +48,7 @@ export const en: Translation = { schemaEditorEditModeVisual: "Visual", schemaEditorEditModeJson: "JSON", + arrayNoConstraint: "No constraint", arrayMinimumLabel: "Minimum Items", arrayMinimumPlaceholder: "No minimum", arrayMaximumLabel: "Maximum Items", @@ -58,10 +59,12 @@ export const en: Translation = { arrayValidationErrorContainsMinMax: "'minContains' cannot be greater than 'maxContains'.", + booleanNoConstraint: "No constraint", booleanAllowFalseLabel: "Allow false value", booleanAllowTrueLabel: "Allow true value", booleanNeitherWarning: "Warning: You must allow at least one value.", + numberNoConstraint: "No constraint", numberMinimumLabel: "Minimum Value", numberMinimumPlaceholder: "No minimum", numberMaximumLabel: "Maximum Value", @@ -88,6 +91,7 @@ export const en: Translation = { objectValidationErrorMinMax: "'minProperties' cannot be greater than 'maxProperties'.", + stringNoConstraint: "No constraint", stringMinimumLengthLabel: "Minimum Length", stringMinimumLengthPlaceholder: "No minimum", stringMaximumLengthLabel: "Maximum Length", diff --git a/src/i18n/locales/fr.ts b/src/i18n/locales/fr.ts index d34e534..c13b259 100644 --- a/src/i18n/locales/fr.ts +++ b/src/i18n/locales/fr.ts @@ -50,6 +50,7 @@ export const fr: Translation = { schemaEditorEditModeVisual: "Visuel", schemaEditorEditModeJson: "JSON", + arrayNoConstraint: "Pas de constrainte", arrayMinimumLabel: "Éléments minimum", arrayMinimumPlaceholder: "Pas de minimum", arrayMaximumLabel: "Éléments maximum", @@ -61,11 +62,13 @@ export const fr: Translation = { arrayValidationErrorContainsMinMax: "'minContains' ne peut pas être supérieur à 'maxContains'.", + booleanNoConstraint: "Pas de constrainte", booleanAllowFalseLabel: "Autoriser la valeur faux", booleanAllowTrueLabel: "Autoriser la valeur vrai", booleanNeitherWarning: "Avertissement: Vous devez autoriser au moins une valeur.", + numberNoConstraint: "Pas de constrainte", numberMinimumLabel: "Valeur minimale", numberMinimumPlaceholder: "Pas de minimum", numberMaximumLabel: "Valeur maximale", @@ -92,6 +95,7 @@ export const fr: Translation = { objectValidationErrorMinMax: "'minProperties' ne peut pas être supérieur à 'maxProperties'.", + stringNoConstraint: "Pas de constrainte", stringMinimumLengthLabel: "Longueur minimale", stringMinimumLengthPlaceholder: "Pas de minimum", stringMaximumLengthLabel: "Longueur maximale", diff --git a/src/i18n/locales/ru.ts b/src/i18n/locales/ru.ts index a04cf42..11a29c7 100644 --- a/src/i18n/locales/ru.ts +++ b/src/i18n/locales/ru.ts @@ -50,6 +50,7 @@ export const ru: Translation = { schemaEditorEditModeVisual: "Визуальный", schemaEditorEditModeJson: "JSON", + arrayNoConstraint: "Без ограничений", arrayMinimumLabel: "Минимум элементов", arrayMinimumPlaceholder: "Нет минимума", arrayMaximumLabel: "Максимум элементов", @@ -60,10 +61,12 @@ export const ru: Translation = { arrayValidationErrorContainsMinMax: "'minContains' не может быть больше 'maxContains'.", + booleanNoConstraint: "Без ограничений", booleanAllowFalseLabel: "Разрешить значение ложь", booleanAllowTrueLabel: "Разрешить значение истина", booleanNeitherWarning: "Внимание: Вы должны разрешить хотя бы одно значение.", + numberNoConstraint: "Без ограничений", numberMinimumLabel: "Минимальное значение", numberMinimumPlaceholder: "Нет минимума", numberMaximumLabel: "Максимальное значение", @@ -91,6 +94,7 @@ export const ru: Translation = { objectValidationErrorMinMax: "'minProperties' не может быть больше 'maxProperties'.", + stringNoConstraint: "Без ограничений", stringMinimumLengthLabel: "Минимальная длина", stringMinimumLengthPlaceholder: "Нет минимума", stringMaximumLengthLabel: "Максимальная длина", diff --git a/src/i18n/translation-keys.ts b/src/i18n/translation-keys.ts index f4b86e0..c8719fd 100644 --- a/src/i18n/translation-keys.ts +++ b/src/i18n/translation-keys.ts @@ -232,6 +232,12 @@ export interface Translation { */ readonly propertyDelete: string; + /** + * The translation for the key `arrayNoConstraint`. English default is: + * + * > No constraint + */ + readonly arrayNoConstraint: string; /** * The translation for the key `arrayMinimumLabel`. English default is: * @@ -281,6 +287,12 @@ export interface Translation { */ readonly arrayValidationErrorContainsMinMax: string; + /** + * The translation for the key `booleanNoConstraint`. English default is: + * + * > No constraint + */ + readonly booleanNoConstraint: string; /** * The translation for the key `booleanAllowTrueLabel`. English default is: * @@ -300,6 +312,12 @@ export interface Translation { */ readonly booleanNeitherWarning: string; + /** + * The translation for the key `numberNoConstraint`. English default is: + * + * > No constraint + */ + readonly numberNoConstraint: string; /** * The translation for the key `numberMinimumLabel`. English default is: * @@ -428,6 +446,12 @@ export interface Translation { * > Minimum Length */ readonly stringMinimumLengthLabel: string; + /** + * The translation for the key `stringNoConstraint`. English default is: + * + * > No constraint + */ + readonly stringNoConstraint: string; /** * The translation for the key `stringMinimumLengthPlaceholder`. English default is: * diff --git a/src/index.css b/src/index.css index f7b432e..b61608b 100644 --- a/src/index.css +++ b/src/index.css @@ -51,8 +51,10 @@ --animate-scale-out: jsonjoy-scale-out 0.2s ease-out; --animate-float: jsonjoy-float 3s ease-in-out infinite; --animate-pulse-subtle: pulse-subtle 3s ease-in-out infinite; - --animate-enter: jsonjoy-fade-in 0.4s ease-out, jsonjoy-scale-in 0.3s ease-out; - --animate-exit: jsonjoy-fade-out 0.3s ease-out, jsonjoy-scale-out 0.2s ease-out; + --animate-enter: + jsonjoy-fade-in 0.4s ease-out, jsonjoy-scale-in 0.3s ease-out; + --animate-exit: + jsonjoy-fade-out 0.3s ease-out, jsonjoy-scale-out 0.2s ease-out; --font-sans: var(--font-sans), system-ui, sans-serif; diff --git a/src/index.ts b/src/index.ts index 9200383..9e5c58e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -10,13 +10,12 @@ import SchemaVisualEditor, { type SchemaVisualEditorProps, } from "./components/SchemaEditor/SchemaVisualEditor.tsx"; -export * from "./i18n/locales/en.ts"; -export * from "./i18n/locales/de.ts"; -export * from "./i18n/translation-keys.ts"; -export * from "./i18n/translation-context.ts"; - export * from "./components/features/JsonValidator.tsx"; export * from "./components/features/SchemaInferencer.tsx"; +export * from "./i18n/locales/de.ts"; +export * from "./i18n/locales/en.ts"; +export * from "./i18n/translation-context.ts"; +export * from "./i18n/translation-keys.ts"; export { JsonSchemaEditor, diff --git a/src/lib/schema-inference.ts b/src/lib/schema-inference.ts index 022869b..ed2a694 100644 --- a/src/lib/schema-inference.ts +++ b/src/lib/schema-inference.ts @@ -1,4 +1,4 @@ -import { type JSONSchema, asObjectSchema } from "../types/jsonSchema.ts"; +import { asObjectSchema, type JSONSchema } from "../types/jsonSchema.ts"; /** * Merges two JSON schemas. diff --git a/src/lib/utils.ts b/src/lib/utils.ts index a9fe930..201a265 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -1,7 +1,7 @@ import { type ClassValue, clsx } from "clsx"; import { twMerge } from "tailwind-merge"; -import type { SchemaType } from "../types/jsonSchema.ts"; import type { Translation } from "../i18n/translation-keys.ts"; +import type { SchemaType } from "../types/jsonSchema.ts"; export function cn(...inputs: ClassValue[]) { return twMerge(clsx(inputs)); diff --git a/test/components/SchemaEditor/SchemaVisualEditor.test.tsx b/test/components/SchemaEditor/SchemaVisualEditor.test.tsx new file mode 100644 index 0000000..28065dd --- /dev/null +++ b/test/components/SchemaEditor/SchemaVisualEditor.test.tsx @@ -0,0 +1,38 @@ +import { render } from "@testing-library/react"; +import "global-jsdom/register"; +import { describe, test } from "node:test"; +import React from "react"; +import SchemaVisualEditor from "../../../src/components/SchemaEditor/SchemaVisualEditor.tsx"; + +describe("SchemaVisualEditor", () => { + test("write mode does show constraints", (t) => { + const element = React.createElement(SchemaVisualEditor, { + readOnly: false, + onChange: () => {}, + schema: { + type: "object", + properties: { + name: { + type: "string", + }, + }, + }, + }); + t.assert.snapshot(render(element).container.innerHTML); + }); + test("read-only mode doesn't show constraints", (t) => { + const element = React.createElement(SchemaVisualEditor, { + readOnly: true, + onChange: () => {}, + schema: { + type: "object", + properties: { + name: { + type: "string", + }, + }, + }, + }); + t.assert.snapshot(render(element).container.innerHTML); + }); +}); diff --git a/test/components/SchemaEditor/SchemaVisualEditor.test.tsx.snapshot b/test/components/SchemaEditor/SchemaVisualEditor.test.tsx.snapshot new file mode 100644 index 0000000..27a474a --- /dev/null +++ b/test/components/SchemaEditor/SchemaVisualEditor.test.tsx.snapshot @@ -0,0 +1,7 @@ +exports[`SchemaVisualEditor > read-only mode doesn't show constraints 1`] = ` +"
" +`; + +exports[`SchemaVisualEditor > write mode does show constraints 1`] = ` +"
" +`; diff --git a/test/components/SchemaEditor/types/ArrayEditor.test.tsx b/test/components/SchemaEditor/types/ArrayEditor.test.tsx new file mode 100644 index 0000000..fe0d32f --- /dev/null +++ b/test/components/SchemaEditor/types/ArrayEditor.test.tsx @@ -0,0 +1,38 @@ +import { render } from "@testing-library/react"; +import "global-jsdom/register"; +import { describe, test } from "node:test"; +import React from "react"; +import ArrayEditor from "../../../../src/components/SchemaEditor/types/ArrayEditor.tsx"; + +describe("ArrayEditor", () => { + test("write mode does show constraints", (t) => { + const element = React.createElement(ArrayEditor, { + readOnly: false, + onChange: () => {}, + depth: 0, + validationNode: undefined, + schema: { + type: "array", + items: { + type: "string", + }, + }, + }); + t.assert.snapshot(render(element).container.innerHTML); + }); + test("read-only mode doesn't show constraints", (t) => { + const element = React.createElement(ArrayEditor, { + readOnly: true, + onChange: () => {}, + depth: 0, + validationNode: undefined, + schema: { + type: "array", + items: { + type: "string", + }, + }, + }); + t.assert.snapshot(render(element).container.innerHTML); + }); +}); diff --git a/test/components/SchemaEditor/types/ArrayEditor.test.tsx.snapshot b/test/components/SchemaEditor/types/ArrayEditor.test.tsx.snapshot new file mode 100644 index 0000000..bbed26b --- /dev/null +++ b/test/components/SchemaEditor/types/ArrayEditor.test.tsx.snapshot @@ -0,0 +1,7 @@ +exports[`ArrayEditor > read-only mode doesn't show constraints 1`] = ` +"
Loading editor...
" +`; + +exports[`ArrayEditor > write mode does show constraints 1`] = ` +"
Loading editor...
" +`; diff --git a/test/components/SchemaEditor/types/BooleanEditor.test.tsx b/test/components/SchemaEditor/types/BooleanEditor.test.tsx new file mode 100644 index 0000000..83be54a --- /dev/null +++ b/test/components/SchemaEditor/types/BooleanEditor.test.tsx @@ -0,0 +1,28 @@ +import { render } from "@testing-library/react"; +import "global-jsdom/register"; +import { describe, test } from "node:test"; +import React from "react"; +import BooleanEditor from "../../../../src/components/SchemaEditor/types/BooleanEditor.tsx"; + +describe("BooleanEditor", () => { + test("write mode does show constraints", (t) => { + const element = React.createElement(BooleanEditor, { + readOnly: false, + onChange: () => {}, + schema: { + type: "boolean", + }, + }); + t.assert.snapshot(render(element).container.innerHTML); + }); + test("read-only mode doesn't show constraints", (t) => { + const element = React.createElement(BooleanEditor, { + readOnly: true, + onChange: () => {}, + schema: { + type: "boolean", + }, + }); + t.assert.snapshot(render(element).container.innerHTML); + }); +}); diff --git a/test/components/SchemaEditor/types/BooleanEditor.test.tsx.snapshot b/test/components/SchemaEditor/types/BooleanEditor.test.tsx.snapshot new file mode 100644 index 0000000..92dab74 --- /dev/null +++ b/test/components/SchemaEditor/types/BooleanEditor.test.tsx.snapshot @@ -0,0 +1,7 @@ +exports[`BooleanEditor > read-only mode doesn't show constraints 1`] = ` +"

No constraint

" +`; + +exports[`BooleanEditor > write mode does show constraints 1`] = ` +"
" +`; diff --git a/test/components/SchemaEditor/types/NumberEditor.test.tsx b/test/components/SchemaEditor/types/NumberEditor.test.tsx new file mode 100644 index 0000000..ffe5bd8 --- /dev/null +++ b/test/components/SchemaEditor/types/NumberEditor.test.tsx @@ -0,0 +1,28 @@ +import { render } from "@testing-library/react"; +import "global-jsdom/register"; +import { describe, test } from "node:test"; +import React from "react"; +import NumberEditor from "../../../../src/components/SchemaEditor/types/NumberEditor.tsx"; + +describe("NumberEditor", () => { + test("write mode does show constraints", (t) => { + const element = React.createElement(NumberEditor, { + readOnly: false, + onChange: () => {}, + schema: { + type: "number", + }, + }); + t.assert.snapshot(render(element).container.innerHTML); + }); + test("read-only mode doesn't show constraints", (t) => { + const element = React.createElement(NumberEditor, { + readOnly: true, + onChange: () => {}, + schema: { + type: "number", + }, + }); + t.assert.snapshot(render(element).container.innerHTML); + }); +}); diff --git a/test/components/SchemaEditor/types/NumberEditor.test.tsx.snapshot b/test/components/SchemaEditor/types/NumberEditor.test.tsx.snapshot new file mode 100644 index 0000000..7832be3 --- /dev/null +++ b/test/components/SchemaEditor/types/NumberEditor.test.tsx.snapshot @@ -0,0 +1,7 @@ +exports[`NumberEditor > read-only mode doesn't show constraints 1`] = ` +"

No constraint

" +`; + +exports[`NumberEditor > write mode does show constraints 1`] = ` +"

No restricted values set

" +`; diff --git a/test/components/SchemaEditor/types/ObjectEditor.test.tsx b/test/components/SchemaEditor/types/ObjectEditor.test.tsx new file mode 100644 index 0000000..9743a2e --- /dev/null +++ b/test/components/SchemaEditor/types/ObjectEditor.test.tsx @@ -0,0 +1,42 @@ +import { render } from "@testing-library/react"; +import "global-jsdom/register"; +import { describe, test } from "node:test"; +import React from "react"; +import ObjectEditor from "../../../../src/components/SchemaEditor/types/ObjectEditor.tsx"; + +describe("ObjectEditor", () => { + test("write mode does show constraints", (t) => { + const element = React.createElement(ObjectEditor, { + readOnly: false, + onChange: () => {}, + depth: 0, + validationNode: undefined, + schema: { + type: "object", + properties: { + name: { + type: "string", + }, + }, + }, + }); + t.assert.snapshot(render(element).container.innerHTML); + }); + test("read-only mode doesn't show constraints", (t) => { + const element = React.createElement(ObjectEditor, { + readOnly: true, + onChange: () => {}, + depth: 0, + validationNode: undefined, + schema: { + type: "object", + properties: { + name: { + type: "string", + }, + }, + }, + }); + t.assert.snapshot(render(element).container.innerHTML); + }); +}); diff --git a/test/components/SchemaEditor/types/ObjectEditor.test.tsx.snapshot b/test/components/SchemaEditor/types/ObjectEditor.test.tsx.snapshot new file mode 100644 index 0000000..881eef1 --- /dev/null +++ b/test/components/SchemaEditor/types/ObjectEditor.test.tsx.snapshot @@ -0,0 +1,7 @@ +exports[`ObjectEditor > read-only mode doesn't show constraints 1`] = ` +"
" +`; + +exports[`ObjectEditor > write mode does show constraints 1`] = ` +"
" +`; diff --git a/test/components/SchemaEditor/types/StringEditor.test.tsx b/test/components/SchemaEditor/types/StringEditor.test.tsx new file mode 100644 index 0000000..f1c45b0 --- /dev/null +++ b/test/components/SchemaEditor/types/StringEditor.test.tsx @@ -0,0 +1,30 @@ +import { render } from "@testing-library/react"; +import "global-jsdom/register"; +import { describe, test } from "node:test"; +import React from "react"; +import StringEditor from "../../../../src/components/SchemaEditor/types/StringEditor.tsx"; + +describe("StringEditor", () => { + test("write mode does show constraints", (t) => { + const element = React.createElement(StringEditor, { + readOnly: false, + onChange: () => {}, + validationNode: undefined, + schema: { + type: "number", + }, + }); + t.assert.snapshot(render(element).container.innerHTML); + }); + test("read-only mode doesn't show constraints", (t) => { + const element = React.createElement(StringEditor, { + readOnly: true, + onChange: () => {}, + validationNode: undefined, + schema: { + type: "number", + }, + }); + t.assert.snapshot(render(element).container.innerHTML); + }); +}); diff --git a/test/components/SchemaEditor/types/StringEditor.test.tsx.snapshot b/test/components/SchemaEditor/types/StringEditor.test.tsx.snapshot new file mode 100644 index 0000000..0ff6671 --- /dev/null +++ b/test/components/SchemaEditor/types/StringEditor.test.tsx.snapshot @@ -0,0 +1,7 @@ +exports[`StringEditor > read-only mode doesn't show constraints 1`] = ` +"

No constraint

" +`; + +exports[`StringEditor > write mode does show constraints 1`] = ` +"

No restricted values set

" +`; diff --git a/tsconfig.test.json b/tsconfig.test.json new file mode 100644 index 0000000..d4c1d07 --- /dev/null +++ b/tsconfig.test.json @@ -0,0 +1,9 @@ +{ + "extends": "./tsconfig-base.json", + "compilerOptions": { + "composite": true, + "types": [], + "allowJs": true + }, + "include": ["**/*.ts", "**/*.tsx"] +}