diff --git a/examples/dev-node-state-style/README.md b/examples/dev-node-state-style/README.md new file mode 100644 index 00000000000..5e639ed3aca --- /dev/null +++ b/examples/dev-node-state-style/README.md @@ -0,0 +1,13 @@ +# DEV Node State Style example + +Here we have an example that demonstrates how NodeState can be used with a +DOMRenderExtension to override create and export behavior of any node. + +This example currently depends on unreleased features (v0.37+) and will not +work outside of the monorepo. + +**Run it locally:** `npm i && npm run dev` + +[![Open in StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/facebook/lexical/tree/main?file=examples/dev-node-state-style/src/main.tsx&startCommand=npm%20run%20start:example%20dev-node-state-style) + +[![Open in StackBlitz-PR](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/etrepum/lexical/tree/extensible-dom-export?file=examples/dev-node-state-style/src/main.tsx&startCommand=npm%20run%20start:example%20dev-node-state-style) diff --git a/examples/dev-node-state-style/index.html b/examples/dev-node-state-style/index.html new file mode 100644 index 00000000000..27517b253ae --- /dev/null +++ b/examples/dev-node-state-style/index.html @@ -0,0 +1,12 @@ + + + + + + Lexical Node State Example + + +
+ + + diff --git a/examples/dev-node-state-style/package-lock.json b/examples/dev-node-state-style/package-lock.json new file mode 100644 index 00000000000..98f1aaf5879 --- /dev/null +++ b/examples/dev-node-state-style/package-lock.json @@ -0,0 +1,2633 @@ +{ + "name": "@lexical/node-state-style-example", + "version": "0.36.1", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "@lexical/node-state-style-example", + "version": "0.36.1", + "dependencies": { + "@ark-ui/react": "^5.6.0", + "@lexical/clipboard": "0.36.1", + "@lexical/html": "0.36.1", + "@lexical/react": "0.36.1", + "@lexical/selection": "0.36.1", + "@lexical/utils": "0.36.1", + "@shikijs/langs": "^3.3.0", + "@shikijs/themes": "^3.3.0", + "inline-style-parser": "^0.2.4", + "lexical": "0.36.1", + "lucide-react": "^0.503.0", + "prettier": "^3.5.3", + "react": "^19.1.0", + "react-dom": "^19.1.0", + "shiki": "^3.3.0" + }, + "devDependencies": { + "@types/react": "^19.1.2", + "@types/react-dom": "^19.1.2", + "@vitejs/plugin-react": "^5.0.2", + "cross-env": "^7.0.3", + "csstype": "^3.1.3", + "typescript": "^5.9.2", + "vite": "^7.1.4" + } + }, + "node_modules/@ark-ui/react": { + "version": "5.25.0", + "license": "MIT", + "dependencies": { + "@internationalized/date": "3.9.0", + "@zag-js/accordion": "1.24.1", + "@zag-js/anatomy": "1.24.1", + "@zag-js/angle-slider": "1.24.1", + "@zag-js/async-list": "1.24.1", + "@zag-js/auto-resize": "1.24.1", + "@zag-js/avatar": "1.24.1", + "@zag-js/carousel": "1.24.1", + "@zag-js/checkbox": "1.24.1", + "@zag-js/clipboard": "1.24.1", + "@zag-js/collapsible": "1.24.1", + "@zag-js/collection": "1.24.1", + "@zag-js/color-picker": "1.24.1", + "@zag-js/color-utils": "1.24.1", + "@zag-js/combobox": "1.24.1", + "@zag-js/core": "1.24.1", + "@zag-js/date-picker": "1.24.1", + "@zag-js/date-utils": "1.24.1", + "@zag-js/dialog": "1.24.1", + "@zag-js/dom-query": "1.24.1", + "@zag-js/editable": "1.24.1", + "@zag-js/file-upload": "1.24.1", + "@zag-js/file-utils": "1.24.1", + "@zag-js/floating-panel": "1.24.1", + "@zag-js/focus-trap": "1.24.1", + "@zag-js/highlight-word": "1.24.1", + "@zag-js/hover-card": "1.24.1", + "@zag-js/i18n-utils": "1.24.1", + "@zag-js/json-tree-utils": "1.24.1", + "@zag-js/listbox": "1.24.1", + "@zag-js/menu": "1.24.1", + "@zag-js/number-input": "1.24.1", + "@zag-js/pagination": "1.24.1", + "@zag-js/password-input": "1.24.1", + "@zag-js/pin-input": "1.24.1", + "@zag-js/popover": "1.24.1", + "@zag-js/presence": "1.24.1", + "@zag-js/progress": "1.24.1", + "@zag-js/qr-code": "1.24.1", + "@zag-js/radio-group": "1.24.1", + "@zag-js/rating-group": "1.24.1", + "@zag-js/react": "1.24.1", + "@zag-js/scroll-area": "1.24.1", + "@zag-js/select": "1.24.1", + "@zag-js/signature-pad": "1.24.1", + "@zag-js/slider": "1.24.1", + "@zag-js/splitter": "1.24.1", + "@zag-js/steps": "1.24.1", + "@zag-js/switch": "1.24.1", + "@zag-js/tabs": "1.24.1", + "@zag-js/tags-input": "1.24.1", + "@zag-js/timer": "1.24.1", + "@zag-js/toast": "1.24.1", + "@zag-js/toggle": "1.24.1", + "@zag-js/toggle-group": "1.24.1", + "@zag-js/tooltip": "1.24.1", + "@zag-js/tour": "1.24.1", + "@zag-js/tree-view": "1.24.1", + "@zag-js/types": "1.24.1", + "@zag-js/utils": "1.24.1" + }, + "peerDependencies": { + "react": ">=18.0.0", + "react-dom": ">=18.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.27.1", + "dev": true, + "license": "MIT", + "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/compat-data": { + "version": "7.28.4", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.28.4", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.3", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-module-transforms": "^7.28.3", + "@babel/helpers": "^7.28.4", + "@babel/parser": "^7.28.4", + "@babel/template": "^7.27.2", + "@babel/traverse": "^7.28.4", + "@babel/types": "^7.28.4", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.28.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.28.3", + "@babel/types": "^7.28.2", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.27.2", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.27.2", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.27.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.28.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1", + "@babel/traverse": "^7.28.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.27.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.27.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.28.4", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.4" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.28.4", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.4" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-self": { + "version": "7.27.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-source": { + "version": "7.27.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.28.4", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template": { + "version": "7.27.2", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/parser": "^7.27.2", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.28.4", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.3", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.28.4", + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.4", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.28.4", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.10", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@floating-ui/core": { + "version": "1.7.3", + "license": "MIT", + "dependencies": { + "@floating-ui/utils": "^0.2.10" + } + }, + "node_modules/@floating-ui/dom": { + "version": "1.7.4", + "license": "MIT", + "dependencies": { + "@floating-ui/core": "^1.7.3", + "@floating-ui/utils": "^0.2.10" + } + }, + "node_modules/@floating-ui/react": { + "version": "0.27.16", + "license": "MIT", + "dependencies": { + "@floating-ui/react-dom": "^2.1.6", + "@floating-ui/utils": "^0.2.10", + "tabbable": "^6.0.0" + }, + "peerDependencies": { + "react": ">=17.0.0", + "react-dom": ">=17.0.0" + } + }, + "node_modules/@floating-ui/react-dom": { + "version": "2.1.6", + "license": "MIT", + "dependencies": { + "@floating-ui/dom": "^1.7.4" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@floating-ui/utils": { + "version": "0.2.10", + "license": "MIT" + }, + "node_modules/@internationalized/date": { + "version": "3.9.0", + "license": "Apache-2.0", + "dependencies": { + "@swc/helpers": "^0.5.0" + } + }, + "node_modules/@internationalized/number": { + "version": "3.6.5", + "license": "Apache-2.0", + "dependencies": { + "@swc/helpers": "^0.5.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@lexical/clipboard": { + "version": "0.36.1", + "resolved": "https://registry.npmjs.org/@lexical/clipboard/-/clipboard-0.36.1.tgz", + "integrity": "sha512-qwOereYfHm1MgLu4Kq+nTsHwCoHtZE/PEMrs5k3lbsNk4SEZ8MLHI/Bx9TYijh24MnJhIoWdMLdAmhIaVs/IHg==", + "license": "MIT", + "dependencies": { + "@lexical/html": "0.36.1", + "@lexical/list": "0.36.1", + "@lexical/selection": "0.36.1", + "@lexical/utils": "0.36.1", + "lexical": "0.36.1" + } + }, + "node_modules/@lexical/code": { + "version": "0.36.1", + "resolved": "https://registry.npmjs.org/@lexical/code/-/code-0.36.1.tgz", + "integrity": "sha512-lybXf03xLGtzjYK5Wvpp8ZEdddbYzdnIE74+qXVVuN96XbpSHKkYqVkyIz1fAatEdrpdNXfqO8dZOyUS216wiA==", + "license": "MIT", + "dependencies": { + "@lexical/utils": "0.36.1", + "lexical": "0.36.1", + "prismjs": "^1.30.0" + } + }, + "node_modules/@lexical/devtools-core": { + "version": "0.36.1", + "resolved": "https://registry.npmjs.org/@lexical/devtools-core/-/devtools-core-0.36.1.tgz", + "integrity": "sha512-SRbYta2DZKZH+gCc0MaeqR/XLfbydbfMjwn7+XIUUTUE2BZEtYhKqN58kA9N50tJZF4jcHp7MzBIddCfQJqlOg==", + "license": "MIT", + "dependencies": { + "@lexical/html": "0.36.1", + "@lexical/link": "0.36.1", + "@lexical/mark": "0.36.1", + "@lexical/table": "0.36.1", + "@lexical/utils": "0.36.1", + "lexical": "0.36.1" + }, + "peerDependencies": { + "react": ">=17.x", + "react-dom": ">=17.x" + } + }, + "node_modules/@lexical/dragon": { + "version": "0.36.1", + "resolved": "https://registry.npmjs.org/@lexical/dragon/-/dragon-0.36.1.tgz", + "integrity": "sha512-XIG/GnAMPQJXB+HFfHE2H+3pCeXumbL+7WcNWG18U8v4K9Zosd815ub4Cy9QR6VqWqILP9prCi+RhCrdY3RY2Q==", + "license": "MIT", + "dependencies": { + "@lexical/extension": "0.36.1", + "lexical": "0.36.1" + } + }, + "node_modules/@lexical/extension": { + "version": "0.36.1", + "resolved": "https://registry.npmjs.org/@lexical/extension/-/extension-0.36.1.tgz", + "integrity": "sha512-8QBYPda+tFpSkXZ6DFHk01gpqbri0Q8UvuivJfHsSBso6OVzub9q/7xg+0b7bezY9mjajAK8b5rqIQvhAsSlTg==", + "license": "MIT", + "dependencies": { + "@lexical/utils": "0.36.1", + "@preact/signals-core": "^1.11.0", + "lexical": "0.36.1" + } + }, + "node_modules/@lexical/hashtag": { + "version": "0.36.1", + "resolved": "https://registry.npmjs.org/@lexical/hashtag/-/hashtag-0.36.1.tgz", + "integrity": "sha512-Qr4WOVGSK/N5X2HQNTZsEI1p2O9S4RfFzeYJpMQJmvbmQlRg5qE5GZrtqH8VgaiD8Hp4ztYfCOUDCmu9gdw+AQ==", + "license": "MIT", + "dependencies": { + "@lexical/text": "0.36.1", + "@lexical/utils": "0.36.1", + "lexical": "0.36.1" + } + }, + "node_modules/@lexical/history": { + "version": "0.36.1", + "resolved": "https://registry.npmjs.org/@lexical/history/-/history-0.36.1.tgz", + "integrity": "sha512-3gUCS4tvhmRPkR5CLosIrLNmKIycOBFnHdek7e1Hpwh8dKPqMF2/NIBKS202ikSwnA/5mD2m6wbELmHBEdtXkg==", + "license": "MIT", + "dependencies": { + "@lexical/extension": "0.36.1", + "@lexical/utils": "0.36.1", + "lexical": "0.36.1" + } + }, + "node_modules/@lexical/html": { + "version": "0.36.1", + "resolved": "https://registry.npmjs.org/@lexical/html/-/html-0.36.1.tgz", + "integrity": "sha512-Uk83ot7033YRJmrnERgqg7Z0wT2T2VtCTcBYTdagLq3PGrka6B8eCBpj2ALmwAANU40Cy3JyL+p83m4bldObYA==", + "license": "MIT", + "dependencies": { + "@lexical/selection": "0.36.1", + "@lexical/utils": "0.36.1", + "lexical": "0.36.1" + } + }, + "node_modules/@lexical/link": { + "version": "0.36.1", + "resolved": "https://registry.npmjs.org/@lexical/link/-/link-0.36.1.tgz", + "integrity": "sha512-l9C4Vcm05eYeBk31oCmJBX4HX7mUSYrXW0v/5FNNabkoyA7m0HLYX7pR9truTl3E2IuWjHmeszkLMd0wKjVxyw==", + "license": "MIT", + "dependencies": { + "@lexical/extension": "0.36.1", + "@lexical/utils": "0.36.1", + "lexical": "0.36.1" + } + }, + "node_modules/@lexical/list": { + "version": "0.36.1", + "resolved": "https://registry.npmjs.org/@lexical/list/-/list-0.36.1.tgz", + "integrity": "sha512-7/9dxrAZyCU7CpEUhOxRIDjaYIjKVt4KJrZrd2BsgQKhruSH3pdB62kyObRVXMeq1ZJiOz+u43/1DUVHTqZJrw==", + "license": "MIT", + "dependencies": { + "@lexical/extension": "0.36.1", + "@lexical/selection": "0.36.1", + "@lexical/utils": "0.36.1", + "lexical": "0.36.1" + } + }, + "node_modules/@lexical/mark": { + "version": "0.36.1", + "resolved": "https://registry.npmjs.org/@lexical/mark/-/mark-0.36.1.tgz", + "integrity": "sha512-PwwuNU1KvXtAmgFECUOdt9GYj6ncUs4TbmsjHHjVnBEGFJBITm7vJq7uGf89vDRgA5Bhb5iaVV2uK4pnk9c7VA==", + "license": "MIT", + "dependencies": { + "@lexical/utils": "0.36.1", + "lexical": "0.36.1" + } + }, + "node_modules/@lexical/markdown": { + "version": "0.36.1", + "resolved": "https://registry.npmjs.org/@lexical/markdown/-/markdown-0.36.1.tgz", + "integrity": "sha512-Ue63kqdiUaWskICh50k3Da/4eANP7pJkKvnkw+qdyhIEMHR45w41ktnqfQViQQttOymBNEk6opTxbSgWFJu9hQ==", + "license": "MIT", + "dependencies": { + "@lexical/code": "0.36.1", + "@lexical/link": "0.36.1", + "@lexical/list": "0.36.1", + "@lexical/rich-text": "0.36.1", + "@lexical/text": "0.36.1", + "@lexical/utils": "0.36.1", + "lexical": "0.36.1" + } + }, + "node_modules/@lexical/offset": { + "version": "0.36.1", + "resolved": "https://registry.npmjs.org/@lexical/offset/-/offset-0.36.1.tgz", + "integrity": "sha512-4WZVcrME5+GGV7KenJCD1AavJZjKHBAiMUe6yuxOUr5z59V1MEp9Uthol+qeY/WV2/LWZPugrRaONrlFB+FfCg==", + "license": "MIT", + "dependencies": { + "lexical": "0.36.1" + } + }, + "node_modules/@lexical/overflow": { + "version": "0.36.1", + "resolved": "https://registry.npmjs.org/@lexical/overflow/-/overflow-0.36.1.tgz", + "integrity": "sha512-Brc3sHPMbH742HzojpJ8DXoEaZEBA3f66Opp4sUhb+B3jIPMgcqxkmP8fDjNrTMBXuWQgjrrgWXZudTHQdjwug==", + "license": "MIT", + "dependencies": { + "lexical": "0.36.1" + } + }, + "node_modules/@lexical/plain-text": { + "version": "0.36.1", + "resolved": "https://registry.npmjs.org/@lexical/plain-text/-/plain-text-0.36.1.tgz", + "integrity": "sha512-lCrVQOQ5+ZMff7+9f7B2TIYbkbkZAKbcA/J9HAoVAXh0eN5XKh1SIDWJvoWjAJK9v/xs2pbfS2YCczOEQOFuaA==", + "license": "MIT", + "dependencies": { + "@lexical/clipboard": "0.36.1", + "@lexical/dragon": "0.36.1", + "@lexical/selection": "0.36.1", + "@lexical/utils": "0.36.1", + "lexical": "0.36.1" + } + }, + "node_modules/@lexical/react": { + "version": "0.36.1", + "resolved": "https://registry.npmjs.org/@lexical/react/-/react-0.36.1.tgz", + "integrity": "sha512-+vgkGybbfFlgcYJMWp7tRjbWlsdbiOILJfj+VUpCgoqOEX7ouheIflaXwiNF16LPJAwSKiTfkXrRWhWHrF8fVA==", + "license": "MIT", + "dependencies": { + "@floating-ui/react": "^0.27.16", + "@lexical/devtools-core": "0.36.1", + "@lexical/dragon": "0.36.1", + "@lexical/extension": "0.36.1", + "@lexical/hashtag": "0.36.1", + "@lexical/history": "0.36.1", + "@lexical/link": "0.36.1", + "@lexical/list": "0.36.1", + "@lexical/mark": "0.36.1", + "@lexical/markdown": "0.36.1", + "@lexical/overflow": "0.36.1", + "@lexical/plain-text": "0.36.1", + "@lexical/rich-text": "0.36.1", + "@lexical/table": "0.36.1", + "@lexical/text": "0.36.1", + "@lexical/utils": "0.36.1", + "@lexical/yjs": "0.36.1", + "lexical": "0.36.1", + "react-error-boundary": "^6.0.0" + }, + "peerDependencies": { + "react": ">=17.x", + "react-dom": ">=17.x" + } + }, + "node_modules/@lexical/rich-text": { + "version": "0.36.1", + "resolved": "https://registry.npmjs.org/@lexical/rich-text/-/rich-text-0.36.1.tgz", + "integrity": "sha512-XBTGecp60S0RdoHRYpEpwAoJFlXbd1Y4DSHdCCx2hzOIgMdlq/PE4RXXTPvqNWE7uIXFLNMkVFdlpdhcQSg8Eg==", + "license": "MIT", + "dependencies": { + "@lexical/clipboard": "0.36.1", + "@lexical/dragon": "0.36.1", + "@lexical/selection": "0.36.1", + "@lexical/utils": "0.36.1", + "lexical": "0.36.1" + } + }, + "node_modules/@lexical/selection": { + "version": "0.36.1", + "resolved": "https://registry.npmjs.org/@lexical/selection/-/selection-0.36.1.tgz", + "integrity": "sha512-wIZJsTneNhmB61BiOb0y6scgrS4GOiGUVoBQCpP++xfesh0Vife8Zq0oc7NaD0l0E6tvFW3CjMd8ITZ66Zg78A==", + "license": "MIT", + "dependencies": { + "lexical": "0.36.1" + } + }, + "node_modules/@lexical/table": { + "version": "0.36.1", + "resolved": "https://registry.npmjs.org/@lexical/table/-/table-0.36.1.tgz", + "integrity": "sha512-7+wP0249pk+TnbzhX459Txu0JA66PXQK7zGJJLBS1f6JXycYba0hfaBhGCsTKQMOXkgCSMkVyhA2pkbRrUP+Hg==", + "license": "MIT", + "dependencies": { + "@lexical/clipboard": "0.36.1", + "@lexical/extension": "0.36.1", + "@lexical/utils": "0.36.1", + "lexical": "0.36.1" + } + }, + "node_modules/@lexical/text": { + "version": "0.36.1", + "resolved": "https://registry.npmjs.org/@lexical/text/-/text-0.36.1.tgz", + "integrity": "sha512-XboXh15srB1eHJ30x1D7sRH5NlW2GKWER8BpoLGJK9Q5j1QoS6gpL4EAEw6ID8NXgJhplKLtTYX/92JRDOSASg==", + "license": "MIT", + "dependencies": { + "lexical": "0.36.1" + } + }, + "node_modules/@lexical/utils": { + "version": "0.36.1", + "resolved": "https://registry.npmjs.org/@lexical/utils/-/utils-0.36.1.tgz", + "integrity": "sha512-Uv3Mr4cFktJKEcMb8NLMo1mhYzcpOkwu2oGEhsFeYgSnWSb1BrVYsDp2yfkWfI8gZzqXH1v9s82NW2Skg42RPQ==", + "license": "MIT", + "dependencies": { + "@lexical/list": "0.36.1", + "@lexical/selection": "0.36.1", + "@lexical/table": "0.36.1", + "lexical": "0.36.1" + } + }, + "node_modules/@lexical/yjs": { + "version": "0.36.1", + "resolved": "https://registry.npmjs.org/@lexical/yjs/-/yjs-0.36.1.tgz", + "integrity": "sha512-qBqIN/WJHmU739zjH/IczBMpzJ8u2uXvrPpG1bbH2zqnY3kd6nL96NHL96P9cTlE+u4XWgHugVxnki3yjnUNMg==", + "license": "MIT", + "dependencies": { + "@lexical/offset": "0.36.1", + "@lexical/selection": "0.36.1", + "lexical": "0.36.1" + }, + "peerDependencies": { + "yjs": ">=13.5.22" + } + }, + "node_modules/@preact/signals-core": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@preact/signals-core/-/signals-core-1.12.1.tgz", + "integrity": "sha512-BwbTXpj+9QutoZLQvbttRg5x3l5468qaV2kufh+51yha1c53ep5dY4kTuZR35+3pAZxpfQerGJiQqg34ZNZ6uA==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/preact" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-beta.35", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.52.0", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@shikijs/core": { + "version": "3.13.0", + "license": "MIT", + "dependencies": { + "@shikijs/types": "3.13.0", + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.4", + "hast-util-to-html": "^9.0.5" + } + }, + "node_modules/@shikijs/engine-javascript": { + "version": "3.13.0", + "license": "MIT", + "dependencies": { + "@shikijs/types": "3.13.0", + "@shikijs/vscode-textmate": "^10.0.2", + "oniguruma-to-es": "^4.3.3" + } + }, + "node_modules/@shikijs/engine-oniguruma": { + "version": "3.13.0", + "license": "MIT", + "dependencies": { + "@shikijs/types": "3.13.0", + "@shikijs/vscode-textmate": "^10.0.2" + } + }, + "node_modules/@shikijs/langs": { + "version": "3.13.0", + "license": "MIT", + "dependencies": { + "@shikijs/types": "3.13.0" + } + }, + "node_modules/@shikijs/themes": { + "version": "3.13.0", + "license": "MIT", + "dependencies": { + "@shikijs/types": "3.13.0" + } + }, + "node_modules/@shikijs/types": { + "version": "3.13.0", + "license": "MIT", + "dependencies": { + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.4" + } + }, + "node_modules/@shikijs/vscode-textmate": { + "version": "10.0.2", + "license": "MIT" + }, + "node_modules/@swc/helpers": { + "version": "0.5.17", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.8.0" + } + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.28.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.2" + } + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/hast": { + "version": "3.0.4", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/mdast": { + "version": "4.0.4", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/react": { + "version": "19.1.13", + "dev": true, + "license": "MIT", + "dependencies": { + "csstype": "^3.0.2" + } + }, + "node_modules/@types/react-dom": { + "version": "19.1.9", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^19.0.0" + } + }, + "node_modules/@types/unist": { + "version": "3.0.3", + "license": "MIT" + }, + "node_modules/@ungap/structured-clone": { + "version": "1.3.0", + "license": "ISC" + }, + "node_modules/@vitejs/plugin-react": { + "version": "5.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.28.4", + "@babel/plugin-transform-react-jsx-self": "^7.27.1", + "@babel/plugin-transform-react-jsx-source": "^7.27.1", + "@rolldown/pluginutils": "1.0.0-beta.35", + "@types/babel__core": "^7.20.5", + "react-refresh": "^0.17.0" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" + } + }, + "node_modules/@zag-js/accordion": { + "version": "1.24.1", + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "1.24.1", + "@zag-js/core": "1.24.1", + "@zag-js/dom-query": "1.24.1", + "@zag-js/types": "1.24.1", + "@zag-js/utils": "1.24.1" + } + }, + "node_modules/@zag-js/anatomy": { + "version": "1.24.1", + "license": "MIT" + }, + "node_modules/@zag-js/angle-slider": { + "version": "1.24.1", + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "1.24.1", + "@zag-js/core": "1.24.1", + "@zag-js/dom-query": "1.24.1", + "@zag-js/rect-utils": "1.24.1", + "@zag-js/types": "1.24.1", + "@zag-js/utils": "1.24.1" + } + }, + "node_modules/@zag-js/aria-hidden": { + "version": "1.24.1", + "license": "MIT" + }, + "node_modules/@zag-js/async-list": { + "version": "1.24.1", + "license": "MIT", + "dependencies": { + "@zag-js/core": "1.24.1", + "@zag-js/utils": "1.24.1" + } + }, + "node_modules/@zag-js/auto-resize": { + "version": "1.24.1", + "license": "MIT", + "dependencies": { + "@zag-js/dom-query": "1.24.1" + } + }, + "node_modules/@zag-js/avatar": { + "version": "1.24.1", + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "1.24.1", + "@zag-js/core": "1.24.1", + "@zag-js/dom-query": "1.24.1", + "@zag-js/types": "1.24.1", + "@zag-js/utils": "1.24.1" + } + }, + "node_modules/@zag-js/carousel": { + "version": "1.24.1", + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "1.24.1", + "@zag-js/core": "1.24.1", + "@zag-js/dom-query": "1.24.1", + "@zag-js/scroll-snap": "1.24.1", + "@zag-js/types": "1.24.1", + "@zag-js/utils": "1.24.1" + } + }, + "node_modules/@zag-js/checkbox": { + "version": "1.24.1", + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "1.24.1", + "@zag-js/core": "1.24.1", + "@zag-js/dom-query": "1.24.1", + "@zag-js/focus-visible": "1.24.1", + "@zag-js/types": "1.24.1", + "@zag-js/utils": "1.24.1" + } + }, + "node_modules/@zag-js/clipboard": { + "version": "1.24.1", + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "1.24.1", + "@zag-js/core": "1.24.1", + "@zag-js/dom-query": "1.24.1", + "@zag-js/types": "1.24.1", + "@zag-js/utils": "1.24.1" + } + }, + "node_modules/@zag-js/collapsible": { + "version": "1.24.1", + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "1.24.1", + "@zag-js/core": "1.24.1", + "@zag-js/dom-query": "1.24.1", + "@zag-js/types": "1.24.1", + "@zag-js/utils": "1.24.1" + } + }, + "node_modules/@zag-js/collection": { + "version": "1.24.1", + "license": "MIT", + "dependencies": { + "@zag-js/utils": "1.24.1" + } + }, + "node_modules/@zag-js/color-picker": { + "version": "1.24.1", + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "1.24.1", + "@zag-js/color-utils": "1.24.1", + "@zag-js/core": "1.24.1", + "@zag-js/dismissable": "1.24.1", + "@zag-js/dom-query": "1.24.1", + "@zag-js/popper": "1.24.1", + "@zag-js/types": "1.24.1", + "@zag-js/utils": "1.24.1" + } + }, + "node_modules/@zag-js/color-utils": { + "version": "1.24.1", + "license": "MIT", + "dependencies": { + "@zag-js/utils": "1.24.1" + } + }, + "node_modules/@zag-js/combobox": { + "version": "1.24.1", + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "1.24.1", + "@zag-js/aria-hidden": "1.24.1", + "@zag-js/collection": "1.24.1", + "@zag-js/core": "1.24.1", + "@zag-js/dismissable": "1.24.1", + "@zag-js/dom-query": "1.24.1", + "@zag-js/popper": "1.24.1", + "@zag-js/types": "1.24.1", + "@zag-js/utils": "1.24.1" + } + }, + "node_modules/@zag-js/core": { + "version": "1.24.1", + "license": "MIT", + "dependencies": { + "@zag-js/dom-query": "1.24.1", + "@zag-js/utils": "1.24.1" + } + }, + "node_modules/@zag-js/date-picker": { + "version": "1.24.1", + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "1.24.1", + "@zag-js/core": "1.24.1", + "@zag-js/date-utils": "1.24.1", + "@zag-js/dismissable": "1.24.1", + "@zag-js/dom-query": "1.24.1", + "@zag-js/live-region": "1.24.1", + "@zag-js/popper": "1.24.1", + "@zag-js/types": "1.24.1", + "@zag-js/utils": "1.24.1" + }, + "peerDependencies": { + "@internationalized/date": ">=3.0.0" + } + }, + "node_modules/@zag-js/date-utils": { + "version": "1.24.1", + "license": "MIT", + "peerDependencies": { + "@internationalized/date": ">=3.0.0" + } + }, + "node_modules/@zag-js/dialog": { + "version": "1.24.1", + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "1.24.1", + "@zag-js/aria-hidden": "1.24.1", + "@zag-js/core": "1.24.1", + "@zag-js/dismissable": "1.24.1", + "@zag-js/dom-query": "1.24.1", + "@zag-js/focus-trap": "1.24.1", + "@zag-js/remove-scroll": "1.24.1", + "@zag-js/types": "1.24.1", + "@zag-js/utils": "1.24.1" + } + }, + "node_modules/@zag-js/dismissable": { + "version": "1.24.1", + "license": "MIT", + "dependencies": { + "@zag-js/dom-query": "1.24.1", + "@zag-js/interact-outside": "1.24.1", + "@zag-js/utils": "1.24.1" + } + }, + "node_modules/@zag-js/dom-query": { + "version": "1.24.1", + "license": "MIT", + "dependencies": { + "@zag-js/types": "1.24.1" + } + }, + "node_modules/@zag-js/editable": { + "version": "1.24.1", + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "1.24.1", + "@zag-js/core": "1.24.1", + "@zag-js/dom-query": "1.24.1", + "@zag-js/interact-outside": "1.24.1", + "@zag-js/types": "1.24.1", + "@zag-js/utils": "1.24.1" + } + }, + "node_modules/@zag-js/file-upload": { + "version": "1.24.1", + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "1.24.1", + "@zag-js/core": "1.24.1", + "@zag-js/dom-query": "1.24.1", + "@zag-js/file-utils": "1.24.1", + "@zag-js/i18n-utils": "1.24.1", + "@zag-js/types": "1.24.1", + "@zag-js/utils": "1.24.1" + } + }, + "node_modules/@zag-js/file-utils": { + "version": "1.24.1", + "license": "MIT", + "dependencies": { + "@zag-js/i18n-utils": "1.24.1" + } + }, + "node_modules/@zag-js/floating-panel": { + "version": "1.24.1", + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "1.24.1", + "@zag-js/core": "1.24.1", + "@zag-js/dom-query": "1.24.1", + "@zag-js/popper": "1.24.1", + "@zag-js/rect-utils": "1.24.1", + "@zag-js/store": "1.24.1", + "@zag-js/types": "1.24.1", + "@zag-js/utils": "1.24.1" + } + }, + "node_modules/@zag-js/focus-trap": { + "version": "1.24.1", + "license": "MIT", + "dependencies": { + "@zag-js/dom-query": "1.24.1" + } + }, + "node_modules/@zag-js/focus-visible": { + "version": "1.24.1", + "license": "MIT", + "dependencies": { + "@zag-js/dom-query": "1.24.1" + } + }, + "node_modules/@zag-js/highlight-word": { + "version": "1.24.1", + "license": "MIT" + }, + "node_modules/@zag-js/hover-card": { + "version": "1.24.1", + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "1.24.1", + "@zag-js/core": "1.24.1", + "@zag-js/dismissable": "1.24.1", + "@zag-js/dom-query": "1.24.1", + "@zag-js/popper": "1.24.1", + "@zag-js/types": "1.24.1", + "@zag-js/utils": "1.24.1" + } + }, + "node_modules/@zag-js/i18n-utils": { + "version": "1.24.1", + "license": "MIT", + "dependencies": { + "@zag-js/dom-query": "1.24.1" + } + }, + "node_modules/@zag-js/interact-outside": { + "version": "1.24.1", + "license": "MIT", + "dependencies": { + "@zag-js/dom-query": "1.24.1", + "@zag-js/utils": "1.24.1" + } + }, + "node_modules/@zag-js/json-tree-utils": { + "version": "1.24.1", + "license": "MIT" + }, + "node_modules/@zag-js/listbox": { + "version": "1.24.1", + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "1.24.1", + "@zag-js/collection": "1.24.1", + "@zag-js/core": "1.24.1", + "@zag-js/dom-query": "1.24.1", + "@zag-js/focus-visible": "1.24.1", + "@zag-js/types": "1.24.1", + "@zag-js/utils": "1.24.1" + } + }, + "node_modules/@zag-js/live-region": { + "version": "1.24.1", + "license": "MIT" + }, + "node_modules/@zag-js/menu": { + "version": "1.24.1", + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "1.24.1", + "@zag-js/core": "1.24.1", + "@zag-js/dismissable": "1.24.1", + "@zag-js/dom-query": "1.24.1", + "@zag-js/popper": "1.24.1", + "@zag-js/rect-utils": "1.24.1", + "@zag-js/types": "1.24.1", + "@zag-js/utils": "1.24.1" + } + }, + "node_modules/@zag-js/number-input": { + "version": "1.24.1", + "license": "MIT", + "dependencies": { + "@internationalized/number": "3.6.5", + "@zag-js/anatomy": "1.24.1", + "@zag-js/core": "1.24.1", + "@zag-js/dom-query": "1.24.1", + "@zag-js/types": "1.24.1", + "@zag-js/utils": "1.24.1" + } + }, + "node_modules/@zag-js/pagination": { + "version": "1.24.1", + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "1.24.1", + "@zag-js/core": "1.24.1", + "@zag-js/dom-query": "1.24.1", + "@zag-js/types": "1.24.1", + "@zag-js/utils": "1.24.1" + } + }, + "node_modules/@zag-js/password-input": { + "version": "1.24.1", + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "1.24.1", + "@zag-js/core": "1.24.1", + "@zag-js/dom-query": "1.24.1", + "@zag-js/types": "1.24.1", + "@zag-js/utils": "1.24.1" + } + }, + "node_modules/@zag-js/pin-input": { + "version": "1.24.1", + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "1.24.1", + "@zag-js/core": "1.24.1", + "@zag-js/dom-query": "1.24.1", + "@zag-js/types": "1.24.1", + "@zag-js/utils": "1.24.1" + } + }, + "node_modules/@zag-js/popover": { + "version": "1.24.1", + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "1.24.1", + "@zag-js/aria-hidden": "1.24.1", + "@zag-js/core": "1.24.1", + "@zag-js/dismissable": "1.24.1", + "@zag-js/dom-query": "1.24.1", + "@zag-js/focus-trap": "1.24.1", + "@zag-js/popper": "1.24.1", + "@zag-js/remove-scroll": "1.24.1", + "@zag-js/types": "1.24.1", + "@zag-js/utils": "1.24.1" + } + }, + "node_modules/@zag-js/popper": { + "version": "1.24.1", + "license": "MIT", + "dependencies": { + "@floating-ui/dom": "1.7.4", + "@zag-js/dom-query": "1.24.1", + "@zag-js/utils": "1.24.1" + } + }, + "node_modules/@zag-js/presence": { + "version": "1.24.1", + "license": "MIT", + "dependencies": { + "@zag-js/core": "1.24.1", + "@zag-js/dom-query": "1.24.1", + "@zag-js/types": "1.24.1" + } + }, + "node_modules/@zag-js/progress": { + "version": "1.24.1", + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "1.24.1", + "@zag-js/core": "1.24.1", + "@zag-js/dom-query": "1.24.1", + "@zag-js/types": "1.24.1", + "@zag-js/utils": "1.24.1" + } + }, + "node_modules/@zag-js/qr-code": { + "version": "1.24.1", + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "1.24.1", + "@zag-js/core": "1.24.1", + "@zag-js/dom-query": "1.24.1", + "@zag-js/types": "1.24.1", + "@zag-js/utils": "1.24.1", + "proxy-memoize": "3.0.1", + "uqr": "0.1.2" + } + }, + "node_modules/@zag-js/radio-group": { + "version": "1.24.1", + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "1.24.1", + "@zag-js/core": "1.24.1", + "@zag-js/dom-query": "1.24.1", + "@zag-js/focus-visible": "1.24.1", + "@zag-js/types": "1.24.1", + "@zag-js/utils": "1.24.1" + } + }, + "node_modules/@zag-js/rating-group": { + "version": "1.24.1", + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "1.24.1", + "@zag-js/core": "1.24.1", + "@zag-js/dom-query": "1.24.1", + "@zag-js/types": "1.24.1", + "@zag-js/utils": "1.24.1" + } + }, + "node_modules/@zag-js/react": { + "version": "1.24.1", + "license": "MIT", + "dependencies": { + "@zag-js/core": "1.24.1", + "@zag-js/store": "1.24.1", + "@zag-js/types": "1.24.1", + "@zag-js/utils": "1.24.1" + }, + "peerDependencies": { + "react": ">=18.0.0", + "react-dom": ">=18.0.0" + } + }, + "node_modules/@zag-js/rect-utils": { + "version": "1.24.1", + "license": "MIT" + }, + "node_modules/@zag-js/remove-scroll": { + "version": "1.24.1", + "license": "MIT", + "dependencies": { + "@zag-js/dom-query": "1.24.1" + } + }, + "node_modules/@zag-js/scroll-area": { + "version": "1.24.1", + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "1.24.1", + "@zag-js/core": "1.24.1", + "@zag-js/dom-query": "1.24.1", + "@zag-js/types": "1.24.1", + "@zag-js/utils": "1.24.1" + } + }, + "node_modules/@zag-js/scroll-snap": { + "version": "1.24.1", + "license": "MIT", + "dependencies": { + "@zag-js/dom-query": "1.24.1" + } + }, + "node_modules/@zag-js/select": { + "version": "1.24.1", + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "1.24.1", + "@zag-js/collection": "1.24.1", + "@zag-js/core": "1.24.1", + "@zag-js/dismissable": "1.24.1", + "@zag-js/dom-query": "1.24.1", + "@zag-js/popper": "1.24.1", + "@zag-js/types": "1.24.1", + "@zag-js/utils": "1.24.1" + } + }, + "node_modules/@zag-js/signature-pad": { + "version": "1.24.1", + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "1.24.1", + "@zag-js/core": "1.24.1", + "@zag-js/dom-query": "1.24.1", + "@zag-js/types": "1.24.1", + "@zag-js/utils": "1.24.1", + "perfect-freehand": "^1.2.2" + } + }, + "node_modules/@zag-js/slider": { + "version": "1.24.1", + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "1.24.1", + "@zag-js/core": "1.24.1", + "@zag-js/dom-query": "1.24.1", + "@zag-js/types": "1.24.1", + "@zag-js/utils": "1.24.1" + } + }, + "node_modules/@zag-js/splitter": { + "version": "1.24.1", + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "1.24.1", + "@zag-js/core": "1.24.1", + "@zag-js/dom-query": "1.24.1", + "@zag-js/types": "1.24.1", + "@zag-js/utils": "1.24.1" + } + }, + "node_modules/@zag-js/steps": { + "version": "1.24.1", + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "1.24.1", + "@zag-js/core": "1.24.1", + "@zag-js/dom-query": "1.24.1", + "@zag-js/types": "1.24.1", + "@zag-js/utils": "1.24.1" + } + }, + "node_modules/@zag-js/store": { + "version": "1.24.1", + "license": "MIT", + "dependencies": { + "proxy-compare": "3.0.1" + } + }, + "node_modules/@zag-js/switch": { + "version": "1.24.1", + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "1.24.1", + "@zag-js/core": "1.24.1", + "@zag-js/dom-query": "1.24.1", + "@zag-js/focus-visible": "1.24.1", + "@zag-js/types": "1.24.1", + "@zag-js/utils": "1.24.1" + } + }, + "node_modules/@zag-js/tabs": { + "version": "1.24.1", + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "1.24.1", + "@zag-js/core": "1.24.1", + "@zag-js/dom-query": "1.24.1", + "@zag-js/types": "1.24.1", + "@zag-js/utils": "1.24.1" + } + }, + "node_modules/@zag-js/tags-input": { + "version": "1.24.1", + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "1.24.1", + "@zag-js/auto-resize": "1.24.1", + "@zag-js/core": "1.24.1", + "@zag-js/dom-query": "1.24.1", + "@zag-js/interact-outside": "1.24.1", + "@zag-js/live-region": "1.24.1", + "@zag-js/types": "1.24.1", + "@zag-js/utils": "1.24.1" + } + }, + "node_modules/@zag-js/timer": { + "version": "1.24.1", + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "1.24.1", + "@zag-js/core": "1.24.1", + "@zag-js/dom-query": "1.24.1", + "@zag-js/types": "1.24.1", + "@zag-js/utils": "1.24.1" + } + }, + "node_modules/@zag-js/toast": { + "version": "1.24.1", + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "1.24.1", + "@zag-js/core": "1.24.1", + "@zag-js/dismissable": "1.24.1", + "@zag-js/dom-query": "1.24.1", + "@zag-js/types": "1.24.1", + "@zag-js/utils": "1.24.1" + } + }, + "node_modules/@zag-js/toggle": { + "version": "1.24.1", + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "1.24.1", + "@zag-js/core": "1.24.1", + "@zag-js/dom-query": "1.24.1", + "@zag-js/types": "1.24.1", + "@zag-js/utils": "1.24.1" + } + }, + "node_modules/@zag-js/toggle-group": { + "version": "1.24.1", + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "1.24.1", + "@zag-js/core": "1.24.1", + "@zag-js/dom-query": "1.24.1", + "@zag-js/types": "1.24.1", + "@zag-js/utils": "1.24.1" + } + }, + "node_modules/@zag-js/tooltip": { + "version": "1.24.1", + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "1.24.1", + "@zag-js/core": "1.24.1", + "@zag-js/dom-query": "1.24.1", + "@zag-js/focus-visible": "1.24.1", + "@zag-js/popper": "1.24.1", + "@zag-js/types": "1.24.1", + "@zag-js/utils": "1.24.1" + } + }, + "node_modules/@zag-js/tour": { + "version": "1.24.1", + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "1.24.1", + "@zag-js/core": "1.24.1", + "@zag-js/dismissable": "1.24.1", + "@zag-js/dom-query": "1.24.1", + "@zag-js/focus-trap": "1.24.1", + "@zag-js/interact-outside": "1.24.1", + "@zag-js/popper": "1.24.1", + "@zag-js/types": "1.24.1", + "@zag-js/utils": "1.24.1" + } + }, + "node_modules/@zag-js/tree-view": { + "version": "1.24.1", + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "1.24.1", + "@zag-js/collection": "1.24.1", + "@zag-js/core": "1.24.1", + "@zag-js/dom-query": "1.24.1", + "@zag-js/types": "1.24.1", + "@zag-js/utils": "1.24.1" + } + }, + "node_modules/@zag-js/types": { + "version": "1.24.1", + "license": "MIT", + "dependencies": { + "csstype": "3.1.3" + } + }, + "node_modules/@zag-js/utils": { + "version": "1.24.1", + "license": "MIT" + }, + "node_modules/baseline-browser-mapping": { + "version": "2.8.6", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.js" + } + }, + "node_modules/browserslist": { + "version": "4.26.2", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.8.3", + "caniuse-lite": "^1.0.30001741", + "electron-to-chromium": "^1.5.218", + "node-releases": "^2.0.21", + "update-browserslist-db": "^1.1.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001743", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/ccount": { + "version": "2.0.1", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-html4": { + "version": "2.1.0", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-legacy": { + "version": "3.0.0", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/comma-separated-tokens": { + "version": "2.0.3", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/cross-env": { + "version": "7.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.1" + }, + "bin": { + "cross-env": "src/bin/cross-env.js", + "cross-env-shell": "src/bin/cross-env-shell.js" + }, + "engines": { + "node": ">=10.14", + "npm": ">=6", + "yarn": ">=1" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/csstype": { + "version": "3.1.3", + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.3", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/dequal": { + "version": "2.0.3", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/devlop": { + "version": "1.1.0", + "license": "MIT", + "dependencies": { + "dequal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.222", + "dev": true, + "license": "ISC" + }, + "node_modules/esbuild": { + "version": "0.25.10", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.10", + "@esbuild/android-arm": "0.25.10", + "@esbuild/android-arm64": "0.25.10", + "@esbuild/android-x64": "0.25.10", + "@esbuild/darwin-arm64": "0.25.10", + "@esbuild/darwin-x64": "0.25.10", + "@esbuild/freebsd-arm64": "0.25.10", + "@esbuild/freebsd-x64": "0.25.10", + "@esbuild/linux-arm": "0.25.10", + "@esbuild/linux-arm64": "0.25.10", + "@esbuild/linux-ia32": "0.25.10", + "@esbuild/linux-loong64": "0.25.10", + "@esbuild/linux-mips64el": "0.25.10", + "@esbuild/linux-ppc64": "0.25.10", + "@esbuild/linux-riscv64": "0.25.10", + "@esbuild/linux-s390x": "0.25.10", + "@esbuild/linux-x64": "0.25.10", + "@esbuild/netbsd-arm64": "0.25.10", + "@esbuild/netbsd-x64": "0.25.10", + "@esbuild/openbsd-arm64": "0.25.10", + "@esbuild/openbsd-x64": "0.25.10", + "@esbuild/openharmony-arm64": "0.25.10", + "@esbuild/sunos-x64": "0.25.10", + "@esbuild/win32-arm64": "0.25.10", + "@esbuild/win32-ia32": "0.25.10", + "@esbuild/win32-x64": "0.25.10" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/fdir": { + "version": "6.5.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/hast-util-to-html": { + "version": "9.0.5", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "ccount": "^2.0.0", + "comma-separated-tokens": "^2.0.0", + "hast-util-whitespace": "^3.0.0", + "html-void-elements": "^3.0.0", + "mdast-util-to-hast": "^13.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0", + "stringify-entities": "^4.0.0", + "zwitch": "^2.0.4" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-whitespace": { + "version": "3.0.0", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/html-void-elements": { + "version": "3.0.0", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/inline-style-parser": { + "version": "0.2.4", + "license": "MIT" + }, + "node_modules/isexe": { + "version": "2.0.0", + "dev": true, + "license": "ISC" + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/jsesc": { + "version": "3.1.0", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json5": { + "version": "2.2.3", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/lexical": { + "version": "0.36.1", + "resolved": "https://registry.npmjs.org/lexical/-/lexical-0.36.1.tgz", + "integrity": "sha512-VD/rxRp40IvaKGgD+AfWAEPzovm7RTEp++j0P96iaBgiUAq8tDdW3GvPwA5pLk83aQZ5IV10jzEIvzAJkLw+pA==", + "license": "MIT" + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/lucide-react": { + "version": "0.503.0", + "license": "ISC", + "peerDependencies": { + "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/mdast-util-to-hast": { + "version": "13.2.0", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@ungap/structured-clone": "^1.0.0", + "devlop": "^1.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "trim-lines": "^3.0.0", + "unist-util-position": "^5.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-util-character": { + "version": "2.1.1", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-encode": { + "version": "2.0.1", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-sanitize-uri": { + "version": "2.0.1", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-symbol": { + "version": "2.0.1", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-types": { + "version": "2.0.2", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/ms": { + "version": "2.1.3", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.11", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/node-releases": { + "version": "2.0.21", + "dev": true, + "license": "MIT" + }, + "node_modules/oniguruma-parser": { + "version": "0.12.1", + "license": "MIT" + }, + "node_modules/oniguruma-to-es": { + "version": "4.3.3", + "license": "MIT", + "dependencies": { + "oniguruma-parser": "^0.12.1", + "regex": "^6.0.1", + "regex-recursion": "^6.0.2" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/perfect-freehand": { + "version": "1.2.2", + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.3", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.6", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/prettier": { + "version": "3.6.2", + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/prismjs": { + "version": "1.30.0", + "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.30.0.tgz", + "integrity": "sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/property-information": { + "version": "7.1.0", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/proxy-compare": { + "version": "3.0.1", + "license": "MIT" + }, + "node_modules/proxy-memoize": { + "version": "3.0.1", + "license": "MIT", + "dependencies": { + "proxy-compare": "^3.0.0" + } + }, + "node_modules/react": { + "version": "19.1.1", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "19.1.1", + "license": "MIT", + "dependencies": { + "scheduler": "^0.26.0" + }, + "peerDependencies": { + "react": "^19.1.1" + } + }, + "node_modules/react-error-boundary": { + "version": "6.0.0", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.12.5" + }, + "peerDependencies": { + "react": ">=16.13.1" + } + }, + "node_modules/react-refresh": { + "version": "0.17.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/regex": { + "version": "6.0.1", + "license": "MIT", + "dependencies": { + "regex-utilities": "^2.3.0" + } + }, + "node_modules/regex-recursion": { + "version": "6.0.2", + "license": "MIT", + "dependencies": { + "regex-utilities": "^2.3.0" + } + }, + "node_modules/regex-utilities": { + "version": "2.3.0", + "license": "MIT" + }, + "node_modules/rollup": { + "version": "4.52.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.52.0", + "@rollup/rollup-android-arm64": "4.52.0", + "@rollup/rollup-darwin-arm64": "4.52.0", + "@rollup/rollup-darwin-x64": "4.52.0", + "@rollup/rollup-freebsd-arm64": "4.52.0", + "@rollup/rollup-freebsd-x64": "4.52.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.52.0", + "@rollup/rollup-linux-arm-musleabihf": "4.52.0", + "@rollup/rollup-linux-arm64-gnu": "4.52.0", + "@rollup/rollup-linux-arm64-musl": "4.52.0", + "@rollup/rollup-linux-loong64-gnu": "4.52.0", + "@rollup/rollup-linux-ppc64-gnu": "4.52.0", + "@rollup/rollup-linux-riscv64-gnu": "4.52.0", + "@rollup/rollup-linux-riscv64-musl": "4.52.0", + "@rollup/rollup-linux-s390x-gnu": "4.52.0", + "@rollup/rollup-linux-x64-gnu": "4.52.0", + "@rollup/rollup-linux-x64-musl": "4.52.0", + "@rollup/rollup-openharmony-arm64": "4.52.0", + "@rollup/rollup-win32-arm64-msvc": "4.52.0", + "@rollup/rollup-win32-ia32-msvc": "4.52.0", + "@rollup/rollup-win32-x64-gnu": "4.52.0", + "@rollup/rollup-win32-x64-msvc": "4.52.0", + "fsevents": "~2.3.2" + } + }, + "node_modules/scheduler": { + "version": "0.26.0", + "license": "MIT" + }, + "node_modules/semver": { + "version": "6.3.1", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/shiki": { + "version": "3.13.0", + "license": "MIT", + "dependencies": { + "@shikijs/core": "3.13.0", + "@shikijs/engine-javascript": "3.13.0", + "@shikijs/engine-oniguruma": "3.13.0", + "@shikijs/langs": "3.13.0", + "@shikijs/themes": "3.13.0", + "@shikijs/types": "3.13.0", + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.4" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/space-separated-tokens": { + "version": "2.0.2", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/stringify-entities": { + "version": "4.0.4", + "license": "MIT", + "dependencies": { + "character-entities-html4": "^2.0.0", + "character-entities-legacy": "^3.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/tabbable": { + "version": "6.2.0", + "license": "MIT" + }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/trim-lines": { + "version": "3.0.1", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "license": "0BSD" + }, + "node_modules/typescript": { + "version": "5.9.2", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/unist-util-is": { + "version": "6.0.0", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-position": { + "version": "5.0.0", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-stringify-position": { + "version": "4.0.0", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit": { + "version": "5.0.0", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit-parents": { + "version": "6.0.1", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.1.3", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uqr": { + "version": "0.1.2", + "license": "MIT" + }, + "node_modules/vfile": { + "version": "6.0.3", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-message": { + "version": "4.0.3", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vite": { + "version": "7.1.6", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.25.0", + "fdir": "^6.5.0", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.43.0", + "tinyglobby": "^0.2.15" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "lightningcss": "^1.21.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/which": { + "version": "2.0.2", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "dev": true, + "license": "ISC" + }, + "node_modules/zwitch": { + "version": "2.0.4", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + } + } +} diff --git a/examples/dev-node-state-style/package.json b/examples/dev-node-state-style/package.json new file mode 100644 index 00000000000..702774c0b29 --- /dev/null +++ b/examples/dev-node-state-style/package.json @@ -0,0 +1,41 @@ +{ + "name": "@lexical/dev-node-state-style-example", + "private": true, + "version": "0.37.0", + "type": "module", + "scripts": { + "dev": "vite -c vite.config.monorepo.ts", + "monorepo:dev": "vite -c vite.config.monorepo.ts", + "build": "tsc && vite build -c vite.config.monorepo.ts", + "preview": "vite preview" + }, + "dependencies": { + "@ark-ui/react": "^5.6.0", + "@lexical/clipboard": "0.37.0", + "@lexical/extension": "0.37.0", + "@lexical/history": "0.37.0", + "@lexical/html": "0.37.0", + "@lexical/react": "0.37.0", + "@lexical/rich-text": "0.37.0", + "@lexical/selection": "0.37.0", + "@lexical/utils": "0.37.0", + "@shikijs/langs": "^3.3.0", + "@shikijs/themes": "^3.3.0", + "inline-style-parser": "^0.2.4", + "lexical": "0.37.0", + "lucide-react": "^0.503.0", + "prettier": "^3.5.3", + "react": "^19.1.0", + "react-dom": "^19.1.0", + "shiki": "^3.3.0" + }, + "devDependencies": { + "@types/react": "^19.1.2", + "@types/react-dom": "^19.1.2", + "@vitejs/plugin-react": "^5.0.2", + "cross-env": "^7.0.3", + "csstype": "^3.1.3", + "typescript": "^5.9.2", + "vite": "^7.1.4" + } +} diff --git a/examples/dev-node-state-style/src/App.tsx b/examples/dev-node-state-style/src/App.tsx new file mode 100644 index 00000000000..16fb6777edf --- /dev/null +++ b/examples/dev-node-state-style/src/App.tsx @@ -0,0 +1,82 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + */ + +import {Tabs} from '@ark-ui/react/tabs'; +import {AutoFocusExtension} from '@lexical/extension'; +import {HistoryExtension} from '@lexical/history'; +import {ContentEditable} from '@lexical/react/LexicalContentEditable'; +import {LexicalExtensionComposer} from '@lexical/react/LexicalExtensionComposer'; +import {RichTextExtension} from '@lexical/rich-text'; +import {defineExtension, ParagraphNode, TextNode} from 'lexical'; + +import ExampleTheme from './ExampleTheme'; +import {ShikiViewPlugin} from './plugins/ShikiViewPlugin'; +import {StyleViewPlugin} from './plugins/StyleViewPlugin'; +import {ToolbarPlugin} from './plugins/ToolbarPlugin'; +import {StyleStateExtension} from './styleState'; + +const placeholder = 'Enter some rich text...'; + +const editorExtension = defineExtension({ + dependencies: [ + RichTextExtension, + HistoryExtension, + AutoFocusExtension, + StyleStateExtension, + ], + name: '@lexical/examples/node-state-style', + namespace: 'NodeState Demo', + nodes: [ParagraphNode, TextNode], + onError(error: Error) { + throw error; + }, + theme: ExampleTheme, +}); + +export default function App() { + return ( + +
+ +
+ {placeholder}
+ } + /> +
+ + + + Style Tree + HTML + JSON + + + + + + + + + + + + +
+ ); +} diff --git a/examples/dev-node-state-style/src/ExampleTheme.ts b/examples/dev-node-state-style/src/ExampleTheme.ts new file mode 100644 index 00000000000..234afe01563 --- /dev/null +++ b/examples/dev-node-state-style/src/ExampleTheme.ts @@ -0,0 +1,41 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + */ + +export default { + code: 'editor-code', + heading: { + h1: 'editor-heading-h1', + h2: 'editor-heading-h2', + h3: 'editor-heading-h3', + h4: 'editor-heading-h4', + h5: 'editor-heading-h5', + }, + image: 'editor-image', + link: 'editor-link', + list: { + listitem: 'editor-listitem', + nested: { + listitem: 'editor-nested-listitem', + }, + ol: 'editor-list-ol', + ul: 'editor-list-ul', + }, + paragraph: 'editor-paragraph', + placeholder: 'editor-placeholder', + quote: 'editor-quote', + text: { + bold: 'editor-text-bold', + code: 'editor-text-code', + hashtag: 'editor-text-hashtag', + italic: 'editor-text-italic', + overflowed: 'editor-text-overflowed', + strikethrough: 'editor-text-strikethrough', + underline: 'editor-text-underline', + underlineStrikethrough: 'editor-text-underlineStrikethrough', + }, +}; diff --git a/examples/dev-node-state-style/src/icons/LICENSE.md b/examples/dev-node-state-style/src/icons/LICENSE.md new file mode 100644 index 00000000000..ce74f6abeed --- /dev/null +++ b/examples/dev-node-state-style/src/icons/LICENSE.md @@ -0,0 +1,5 @@ +Bootstrap Icons +https://icons.getbootstrap.com + +Licensed under MIT license +https://github.com/twbs/icons/blob/main/LICENSE.md diff --git a/examples/dev-node-state-style/src/icons/arrow-clockwise.svg b/examples/dev-node-state-style/src/icons/arrow-clockwise.svg new file mode 100644 index 00000000000..b072eb097ab --- /dev/null +++ b/examples/dev-node-state-style/src/icons/arrow-clockwise.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/examples/dev-node-state-style/src/icons/arrow-counterclockwise.svg b/examples/dev-node-state-style/src/icons/arrow-counterclockwise.svg new file mode 100644 index 00000000000..b0b23b9bbc4 --- /dev/null +++ b/examples/dev-node-state-style/src/icons/arrow-counterclockwise.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/examples/dev-node-state-style/src/icons/text-paragraph.svg b/examples/dev-node-state-style/src/icons/text-paragraph.svg new file mode 100644 index 00000000000..9779beabf1c --- /dev/null +++ b/examples/dev-node-state-style/src/icons/text-paragraph.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/examples/dev-node-state-style/src/icons/type-bold.svg b/examples/dev-node-state-style/src/icons/type-bold.svg new file mode 100644 index 00000000000..276d133c25c --- /dev/null +++ b/examples/dev-node-state-style/src/icons/type-bold.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/examples/dev-node-state-style/src/icons/type-italic.svg b/examples/dev-node-state-style/src/icons/type-italic.svg new file mode 100644 index 00000000000..3ac6b09f02a --- /dev/null +++ b/examples/dev-node-state-style/src/icons/type-italic.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/examples/dev-node-state-style/src/icons/type-strikethrough.svg b/examples/dev-node-state-style/src/icons/type-strikethrough.svg new file mode 100644 index 00000000000..1c940e42a87 --- /dev/null +++ b/examples/dev-node-state-style/src/icons/type-strikethrough.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/examples/dev-node-state-style/src/icons/type-underline.svg b/examples/dev-node-state-style/src/icons/type-underline.svg new file mode 100644 index 00000000000..c299b8bf2f0 --- /dev/null +++ b/examples/dev-node-state-style/src/icons/type-underline.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/examples/dev-node-state-style/src/main.tsx b/examples/dev-node-state-style/src/main.tsx new file mode 100644 index 00000000000..efedcf1fced --- /dev/null +++ b/examples/dev-node-state-style/src/main.tsx @@ -0,0 +1,22 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + */ +import './styles.css'; + +import React from 'react'; +import ReactDOM from 'react-dom/client'; + +import App from './App.tsx'; + +ReactDOM.createRoot(document.getElementById('root')!).render( + +
+

NodeState Style Example

+ +
+
, +); diff --git a/examples/dev-node-state-style/src/plugins/ShikiViewPlugin.css b/examples/dev-node-state-style/src/plugins/ShikiViewPlugin.css new file mode 100644 index 00000000000..c2f75f2d89a --- /dev/null +++ b/examples/dev-node-state-style/src/plugins/ShikiViewPlugin.css @@ -0,0 +1,5 @@ +.shiki-view-plugin > pre.shiki { + margin: 0; + padding: 10px; + white-space: pre-wrap; +} diff --git a/examples/dev-node-state-style/src/plugins/ShikiViewPlugin.tsx b/examples/dev-node-state-style/src/plugins/ShikiViewPlugin.tsx new file mode 100644 index 00000000000..d7b30435e4f --- /dev/null +++ b/examples/dev-node-state-style/src/plugins/ShikiViewPlugin.tsx @@ -0,0 +1,92 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + */ +import './ShikiViewPlugin.css'; + +import {$generateHtmlFromNodes} from '@lexical/html'; +import {useLexicalComposerContext} from '@lexical/react/LexicalComposerContext'; +import {EditorState, LexicalEditor} from 'lexical'; +import * as prettier from 'prettier'; +import {useEffect, useMemo, useState} from 'react'; +import {createHighlighterCore} from 'shiki/core'; +import {createJavaScriptRegexEngine} from 'shiki/engine/javascript'; + +const jsEngine = createJavaScriptRegexEngine({target: 'ES2024'}); + +const shikiPromise = createHighlighterCore({ + engine: jsEngine, + langs: [import('@shikijs/langs/html'), import('@shikijs/langs/json')], + themes: [import('@shikijs/themes/nord')], +}); +const prettierPlugins = [ + import('prettier/plugins/babel'), + import('prettier/plugins/estree'), + import('prettier/plugins/html'), +]; + +function editorHTML(editor: LexicalEditor, editorState: EditorState): string { + return editorState.read(() => $generateHtmlFromNodes(editor, null), {editor}); +} + +function editorJSON(_editor: LexicalEditor, editorState: EditorState): string { + return JSON.stringify(editorState.toJSON(), null, 2); +} + +const langs = { + html: editorHTML, + json: editorJSON, +} as const; + +export interface ShikiViewPluginProps { + lang: keyof typeof langs; +} + +export function ShikiViewPlugin({lang}: ShikiViewPluginProps) { + const [editor] = useLexicalComposerContext(); + const [editorState, setEditorState] = useState(() => editor.getEditorState()); + useEffect( + () => + editor.registerUpdateListener((payload) => + setEditorState(payload.editorState), + ), + [editor], + ); + const rawCode = useMemo( + () => langs[lang](editor, editorState), + [lang, editor, editorState], + ); + const htmlPromise = useMemo( + () => + (async () => { + const prettified = await prettier.format(rawCode, { + parser: lang, + plugins: (await Promise.all(prettierPlugins)).map( + (mod) => mod.default, + ), + }); + return (await shikiPromise).codeToHtml(prettified, { + lang, + theme: 'nord', + }); + })(), + [lang, rawCode], + ); + const [html, setHtml] = useState(''); + useEffect(() => { + let canceled = false; + htmlPromise.then((formatted) => canceled || setHtml(formatted)); + return () => { + canceled = true; + }; + }, [htmlPromise]); + return ( +
+ ); +} diff --git a/examples/dev-node-state-style/src/plugins/StyleViewPlugin.css b/examples/dev-node-state-style/src/plugins/StyleViewPlugin.css new file mode 100644 index 00000000000..8db74fd35f8 --- /dev/null +++ b/examples/dev-node-state-style/src/plugins/StyleViewPlugin.css @@ -0,0 +1,205 @@ +/* tree-view */ +[data-scope='tree-view'][data-part='tree'] { + width: 240px; +} + +[data-scope='tree-view'][data-part='item'], +[data-scope='tree-view'][data-part='branch-control'] { + user-select: none; + --padding-inline: 16px; + padding-inline-start: calc(var(--depth) * var(--padding-inline)); + padding-inline-end: var(--padding-inline); + display: flex; + align-items: center; + gap: 8px; + border-radius: 2px; + + & svg { + width: 16px; + height: 16px; + opacity: 0.5; + position: relative; + top: 3px; + } + + &:hover { + background: rgb(243, 243, 243); + } + + &[data-selected] { + background: rgb(226, 226, 226); + } + + &:focus { + outline: 1px solid rgb(148, 148, 148); + outline-offset: -1px; + } +} + +[data-scope='tree-view'][data-part='item-text'], +[data-scope='tree-view'][data-part='branch-text'] { + flex: 1; +} + +[data-scope='tree-view'][data-part='branch-content'] { + position: relative; + isolation: isolate; +} + +[data-scope='tree-view'][data-part='branch-indent-guide'] { + position: absolute; + content: ''; + border-left: 1px solid rgb(226, 226, 226); + height: 100%; + translate: calc(var(--depth) * 1.25rem); + z-index: 0; +} + +[data-scope='tree-view'][data-part='branch-indicator'] { + display: flex; + /* align-items: center; */ + &[data-state='open'] svg { + transform: rotate(90deg); + } +} + +@keyframes slideDown { + from { + opacity: 0.01; + height: 0; + } + to { + opacity: 1; + height: var(--height); + } +} + +@keyframes slideUp { + from { + opacity: 1; + height: var(--height); + } + to { + opacity: 0.01; + height: 0; + } +} + +[data-scope='tree-view'][data-part='branch-content'] { + overflow: hidden; + max-width: 400px; +} + +[data-scope='tree-view'][data-part='branch-content'][data-state='open'] { + animation: slideDown 250ms cubic-bezier(0, 0, 0.38, 0.9); +} + +[data-scope='tree-view'][data-part='branch-content'][data-state='closed'] { + animation: slideUp 200ms cubic-bezier(0, 0, 0.38, 0.9); +} + +/* splitter */ +[data-scope='splitter'][data-part='root'] { + gap: 4px; +} + +[data-scope='splitter'][data-part='root'][data-orientation='horizontal'] { + min-height: 300px; +} + +[data-scope='splitter'][data-part='root'][data-orientation='vertical'] { + min-height: 300px; +} + +[data-scope='splitter'][data-part='panel'] { + display: flex; + /* align-items: center; */ + /* justify-content: center; */ + border: 1px solid lightgray; + overflow: auto; + padding: 10px; +} + +[data-scope='splitter'][data-part='panel']:has( + [data-scope='splitter'][data-part='panel'] + ) { + border: none; +} + +[data-scope='splitter'][data-part='resize-trigger'][data-orientation='vertical'] { + min-height: 12px; +} + +.style-view-node-button-delete { + padding: 0; + display: inline-flex; + position: absolute; + top: 0.125em; + left: 0; + border: none; +} +.style-view-node-button-delete svg { + height: 1em; + width: 1em; +} + +.style-view-node-button { + cursor: pointer; +} + +.style-view-node-text-contents::before, +.style-view-node-text-contents::after { + content: '"'; +} + +.style-view-node-text-contents { + max-width: 75ch; + text-overflow: ellipsis; + white-space: nowrap; +} + +.style-view-text-pane { + font-family: monospace; +} + +.style-view-entry { + position: relative; + padding-left: 4ch; + text-indent: -2ch; +} + +.style-view-key { + color: #0288d1; +} + +.style-view-style-heading { + color: #616161; +} + +.style-view-value, +.style-view-value > p { + display: inline; +} +.style-view-value:focus-visible { + outline: 1px auto rgba(20, 20, 20, 0.2); + outline-offset: 4px; +} + +.style-view-actions { + padding-left: 2ch; +} + +[data-scope='combobox'][data-part='item-group'] { + padding: 2px; +} +[data-scope='combobox'][data-part='content'] { + border: 1px solid #000; + background-color: #fff; +} +[data-scope='combobox'][data-part='item'] { + cursor: pointer; + padding: 0 0.5rem; +} +[data-scope='combobox'][data-part='item'][data-highlighted] { + background-color: rgba(0, 0, 0, 0.2); +} diff --git a/examples/dev-node-state-style/src/plugins/StyleViewPlugin.tsx b/examples/dev-node-state-style/src/plugins/StyleViewPlugin.tsx new file mode 100644 index 00000000000..933b5f411f5 --- /dev/null +++ b/examples/dev-node-state-style/src/plugins/StyleViewPlugin.tsx @@ -0,0 +1,838 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + */ + +import './StyleViewPlugin.css'; + +import { + Combobox, + createListCollection, + useCombobox, +} from '@ark-ui/react/combobox'; +import {Portal} from '@ark-ui/react/portal'; +import {Splitter, useSplitter} from '@ark-ui/react/splitter'; +import { + createTreeCollection, + TreeCollection, + TreeView, + useTreeView, + UseTreeViewReturn, +} from '@ark-ui/react/tree-view'; +import {LexicalComposer} from '@lexical/react/LexicalComposer'; +import {useLexicalComposerContext} from '@lexical/react/LexicalComposerContext'; +import {ContentEditable} from '@lexical/react/LexicalContentEditable'; +import {LexicalErrorBoundary} from '@lexical/react/LexicalErrorBoundary'; +import {PlainTextPlugin} from '@lexical/react/LexicalPlainTextPlugin'; +import {$getAdjacentCaret, mergeRegister} from '@lexical/utils'; +import {type SelectionDetails} from '@zag-js/combobox'; +import { + $addUpdateTag, + $createLineBreakNode, + $createParagraphNode, + $createTabNode, + $createTextNode, + $getCaretRange, + $getChildCaret, + $getNodeByKey, + $getPreviousSelection, + $getRoot, + $getSelection, + $getSiblingCaret, + $isDecoratorNode, + $isElementNode, + $isLineBreakNode, + $isParagraphNode, + $isRangeSelection, + $isRootNode, + $isTabNode, + $isTextNode, + $normalizeCaret, + $setSelection, + $setSelectionFromCaretRange, + BLUR_COMMAND, + COMMAND_PRIORITY_LOW, + type EditorState, + ElementNode, + KEY_DOWN_COMMAND, + LexicalEditor, + LexicalNode, + NodeCaret, + NodeKey, +} from 'lexical'; +import { + ChevronRightIcon, + CircleXIcon, + CodeXmlIcon, + CornerDownLeftIcon, + FolderIcon, + FolderRoot, + TextIcon, +} from 'lucide-react'; +import React, { + createContext, + Fragment, + type JSX, + KeyboardEventHandler, + use, + useCallback, + useEffect, + useMemo, + useReducer, + useRef, + useState, +} from 'react'; + +import { + $removeStyleProperty, + $setStyleProperty, + getStyleObjectDirect, + StyleObject, + styleObjectToArray, +} from '../styleState'; + +const SKIP_DOM_SELECTION_TAG = 'skip-dom-selection'; +const SKIP_SCROLL_INTO_VIEW_TAG = 'skip-scroll-into-view'; + +function $preserveSelection(): void { + const selection = $getSelection(); + if (!selection) { + const prevSelection = $getPreviousSelection(); + if (prevSelection) { + $setSelection(prevSelection.clone()); + } + } +} + +const EditorStateContext = createContext(undefined); +function useEditorState() { + const editorState = use(EditorStateContext); + if (editorState === undefined) { + throw new Error('Missing EditorStateContext'); + } + return editorState; +} + +const NodeTreeViewContext = createContext< + undefined | UseTreeViewReturn +>(undefined); +function useNodeTreeViewContext() { + const ctx = use(NodeTreeViewContext); + if (ctx === undefined) { + throw new Error('Missing NodeTreeViewContext'); + } + return ctx; +} + +export function StyleViewPlugin(): JSX.Element { + const [editor] = useLexicalComposerContext(); + const [editorState, setEditorState] = useState(() => editor.getEditorState()); + useEffect( + () => + editor.registerUpdateListener(() => { + setEditorState(editor.getEditorState()); + }), + [editor], + ); + return ( + + + + ); +} + +function NodeLabel({node}: {node: LexicalNode}) { + const [editor] = useLexicalComposerContext(); + const key = node.getKey(); + const type = node.getType(); + const reactLabel = ( + <> + {' '} + {type} + + ); + if ($isTextNode(node)) { + const text = node.__text; + return ( + <> + {reactLabel}{' '} + + {text} + + + ); + } + return reactLabel; +} + +function describeNode(node: LexicalNode): [string, React.ReactNode] { + return [`(${node.getKey()}) ${node.getType()}`, ]; +} + +function LexicalNodeTreeViewItem(props: TreeView.NodeProviderProps) { + const id = props.node; + const editorState = useEditorState(); + const node = + typeof id === 'string' + ? editorState.read(() => $getNodeByKey(id, editorState)) + : null; + const indexPathString = JSON.stringify(props.indexPath); + return useMemo(() => { + if (!node) { + return null; + } + const indexPath = JSON.parse(indexPathString); + const [_ariaLabel, label] = describeNode(node); + const nextNode = node.__next && ( + + ); + const icon = $isRootNode(node) ? ( + + ) : $isElementNode(node) ? ( + + ) : $isTextNode(node) ? ( + + ) : $isDecoratorNode(node) ? ( + + ) : $isLineBreakNode(node) ? ( + + ) : null; + let content: React.ReactNode; + if ($isElementNode(node)) { + content = ( + + + + {icon} {label} + + + {node.__first ? : null} + + + + + {node.__first ? ( + + ) : null} + + + ); + } else { + content = ( + + + {icon} {label} + + + ); + } + return ( + + + {content} + + {nextNode} + + ); + }, [id, node, indexPathString]); +} + +function getSelectedNodeKey( + api: UseTreeViewReturn, +): undefined | NodeKey { + return api.selectedValue.at(0); +} + +interface SelectedNodeStateAction { + panelNodeKey: undefined | NodeKey; + editorState: EditorState; +} +interface SelectedNodeState extends SelectedNodeStateAction { + panelNodeKey: NodeKey; + selectionNodeKey: NodeKey | null; + panelNode: LexicalNode | null; + cached: React.ReactNode; +} +interface InitialSelectedNodeState extends SelectedNodeStateAction { + selectionNodeKey?: undefined; + panelNode?: undefined; + cached?: undefined; +} + +interface StyleValueEditorProps { + ref?: React.Ref; + prop: keyof StyleObject; + value: string; + onChange: (prop: keyof StyleObject, value: string) => void; +} + +type ParsedChunk = '\n' | '\r\n' | '\t' | string; + +function parseRawText(text: string): ParsedChunk[] { + return text.split(/(\r?\n|\t)/); +} + +function $patchNodes( + parent: T, + nodes: LexicalNode[], +): T { + const childrenSize = parent.getChildrenSize(); + if ( + childrenSize === nodes.length && + parent.getChildren().every((node, i) => node === nodes[i]) + ) { + // no-op, do not mark as dirty + return parent; + } + return $getChildCaret(parent, 'next').splice(childrenSize, nodes).origin; +} + +function $patchParsedText( + parent: T, + chunks: readonly ParsedChunk[], +): T { + let caret: null | NodeCaret<'next'> = $getChildCaret(parent, 'next'); + const nodes: LexicalNode[] = []; + for (const chunk of chunks) { + caret = $getAdjacentCaret(caret); + const node = caret ? caret.origin : null; + if (chunk === '\r\n' || chunk === '\n') { + nodes.push($isLineBreakNode(node) ? node : $createLineBreakNode()); + } else if (chunk === '\t') { + nodes.push($isTabNode(node) ? node : $createTabNode()); + } else if (chunk) { + nodes.push( + $isTextNode(node) + ? node.getTextContent() === chunk + ? node + : node.setTextContent(chunk) + : $createTextNode(chunk), + ); + } + } + return $patchNodes(parent, nodes); +} + +function $patchParsedTextAtRoot(chunks: readonly ParsedChunk[]): void { + const root = $getRoot(); + const firstNode = root.getFirstChild(); + const p = $isParagraphNode(firstNode) ? firstNode : $createParagraphNode(); + $getChildCaret(root, 'next').splice(root.getChildrenSize(), [p]); + $patchParsedText(p, chunks); +} + +function StyleValuePlugin(props: StyleValueEditorProps) { + const [editor] = useLexicalComposerContext(); + const {prop, onChange, ref} = props; + const valueRef = useRef(props.value); + useEffect(() => { + const setRef = + typeof ref === 'function' + ? ref + : ref + ? (value: LexicalEditor | null) => { + ref.current = value; + } + : () => {}; + setRef(editor); + return () => { + setRef(null); + }; + }, [editor, ref]); + useEffect(() => { + valueRef.current = props.value; + }, [props.value]); + useEffect(() => { + let timer: undefined | ReturnType; + function clearTimer() { + if (timer) { + clearTimeout(timer); + timer = undefined; + } + } + function handleInput() { + clearTimer(); + setTimeout(handleFlush, 300); + } + function handleFlush() { + clearTimer(); + const value = editor + .getEditorState() + .read(() => $getRoot().getTextContent()); + if (valueRef.current !== value) { + onChange(prop, value); + } + } + return mergeRegister( + editor.registerUpdateListener((payload) => { + if (payload.editorState !== payload.prevEditorState) { + handleInput(); + } + }), + editor.registerCommand( + BLUR_COMMAND, + () => { + handleFlush(); + return true; + }, + COMMAND_PRIORITY_LOW, + ), + editor.registerCommand( + KEY_DOWN_COMMAND, + (e) => { + if (e.key === 'Enter') { + e.preventDefault(); + editor.blur(); + return true; + } + return false; + }, + COMMAND_PRIORITY_LOW, + ), + ); + }, [editor, prop, onChange]); + return ( + + } + ErrorBoundary={LexicalErrorBoundary} + /> + ); +} + +function StyleValueEditor(props: StyleValueEditorProps) { + return ( + { + $patchParsedTextAtRoot(parseRawText(props.value)); + }, + namespace: 'style-view-value', + onError: (err) => { + throw err; + }, + }}> + + + ); +} + +function LexicalTextSelectionPaneContents({node}: {node: LexicalNode}) { + const [editor] = useLexicalComposerContext(); + const [registeredNodes] = useState( + () => new Map(), + ); + const styles = getStyleObjectDirect(node); + const focusPropertyRef = useRef(''); + + const nodeRef = useRef(node); + useEffect(() => { + nodeRef.current = node; + }, [node]); + const {handleChange, handleAddProperty} = useMemo(() => { + // eslint-disable-next-line no-shadow + const handleAddProperty = (prop: keyof StyleObject) => { + const reg = registeredNodes.get(prop); + if (reg) { + reg.focus(); + } else { + focusPropertyRef.current = prop; + editor.update( + () => { + $setStyleProperty(nodeRef.current, prop, ''); + }, + {tag: [SKIP_DOM_SELECTION_TAG, SKIP_SCROLL_INTO_VIEW_TAG]}, + ); + } + }; + // eslint-disable-next-line no-shadow + const handleChange = ( + prop: keyof StyleObject, + textContent: string | null, + ) => { + editor.update( + () => { + $preserveSelection(); + $addUpdateTag(SKIP_DOM_SELECTION_TAG); + $addUpdateTag(SKIP_SCROLL_INTO_VIEW_TAG); + $setStyleProperty(nodeRef.current, prop, textContent || undefined); + }, + {tag: [SKIP_DOM_SELECTION_TAG, SKIP_SCROLL_INTO_VIEW_TAG]}, + ); + }; + return {handleAddProperty, handleChange}; + }, [editor, registeredNodes]); + + const rows = useMemo( + () => + styleObjectToArray(styles).map(([k, v]) => ( +
+ + {k}: + { + if (el === null) { + registeredNodes.delete(k); + return; + } + if (focusPropertyRef.current === k) { + el.focus(); + focusPropertyRef.current = ''; + } + registeredNodes.set(k, el); + }} + /> +
+ )), + [editor, registeredNodes, styles, handleChange], + ); + return ( +
+ {describeNode(node)[1]} +
+
+ style {'{'} +
+ {rows} +
+ +
+
{'}'}
+
+
+ ); +} + +function initTextSelectionPaneReducer(action: SelectedNodeStateAction) { + return textSelectionPaneReducer(action, action); +} + +function textSelectionPaneReducer( + state: InitialSelectedNodeState | SelectedNodeState, + action: SelectedNodeStateAction, +): SelectedNodeState { + return action.editorState.read(() => { + const selection = $getSelection(); + const selectionNodeKey = $isRangeSelection(selection) + ? selection.focus.key + : null; + const { + // selectionNodeKey: prevSelectionNodeKey = null, + panelNode: prevPanelNode = null, + cached: prevCached = null, + } = state; + let panelNodeKey = + selectionNodeKey || action.panelNodeKey || state.panelNodeKey; + if (selectionNodeKey) { + panelNodeKey = selectionNodeKey; + } else if (!panelNodeKey) { + panelNodeKey = 'root'; + } + const panelNode = $getNodeByKey(panelNodeKey); + if (panelNode === prevPanelNode && state.cached) { + return state; + } + const cached = + panelNode === prevPanelNode && prevCached ? ( + prevCached + ) : panelNode === null ? ( + Node {panelNodeKey} no longer in the document + ) : ( + + ); + return {...action, cached, panelNode, panelNodeKey, selectionNodeKey}; + }); +} + +function getSuggestedStyleKeys(): readonly (keyof StyleObject)[] { + const keys = new Set(); + if (typeof document !== 'undefined') { + const {style} = document.body; + for (const k in style) { + if (typeof style[k] === 'string') { + const kebab = k + .replace(/[A-Z]/g, (s) => '-' + s.toLowerCase()) + .replace(/^(webkit|moz|ms|o)-/, '-$1-') + .replace(/^css-/, ''); + keys.add(kebab as keyof StyleObject); + } + } + } + return [...keys].sort(); +} + +function isNotVendorProperty(item: string): boolean { + return !item.startsWith('-'); +} + +function useSuggestedStylesCombobox(props: CSSPropertyComboBoxProps) { + const initialItems = useMemo(getSuggestedStyleKeys, []); + const [items, setItems] = useState(() => + initialItems.filter(isNotVendorProperty).join('\n'), + ); + const collection = useMemo( + () => createListCollection({items: items.split(/\n/g)}), + [items], + ); + const handleInputValueChange = ( + details: Combobox.InputValueChangeDetails, + ) => { + const search = details.inputValue.toLowerCase(); + setItems( + initialItems + .filter( + search + ? (item) => item.toLowerCase().startsWith(search) + : isNotVendorProperty, + ) + .join('\n'), + ); + }; + const handleSelect = (details: SelectionDetails) => { + props.onAddProperty(details.itemValue as keyof StyleObject); + combobox.setInputValue(''); + }; + + const combobox = useCombobox({ + allowCustomValue: true, + collection: collection, + inputBehavior: 'autocomplete', + onInputValueChange: handleInputValueChange, + onSelect: handleSelect, + placeholder: 'Add CSS Property', + positioning: { + placement: 'bottom-start', + sameWidth: false, + }, + }); + return combobox; +} + +interface CSSPropertyComboBoxProps { + onAddProperty: (property: keyof StyleObject) => void; +} + +const CSSPropertyComboBox = (props: CSSPropertyComboBoxProps) => { + const {onAddProperty} = props; + const combobox = useSuggestedStylesCombobox(props); + const handleKeydown = useCallback>( + (event) => { + const {inputValue} = combobox; + if (event.key === 'Enter') { + event.preventDefault(); + if (inputValue) { + onAddProperty(inputValue as keyof StyleObject); + combobox.setInputValue(''); + } + } else if (event.key === 'Tab') { + event.preventDefault(); + if (inputValue) { + const [autocomplete] = combobox.collection.items; + if (autocomplete) { + onAddProperty(autocomplete as keyof StyleObject); + combobox.setInputValue(''); + } + } + } + }, + [combobox, onAddProperty], + ); + + return ( + + + + + + + + + {combobox.collection.items.map((item) => ( + + {item} + ✓ + + ))} + + + + + + ); +}; + +function LexicalTextSelectionPane() { + const editorState = useEditorState(); + const api = useNodeTreeViewContext(); + const panelNodeKey = getSelectedNodeKey(api); + const [state, dispatch] = useReducer( + textSelectionPaneReducer, + {editorState, panelNodeKey}, + initTextSelectionPaneReducer, + ); + useEffect(() => { + dispatch({editorState, panelNodeKey}); + }, [panelNodeKey, editorState]); + return state.cached || null; +} + +function LexicalTreeView() { + const collectionState = useEditorCollectionState(); + const {collection, focusNodeKey} = collectionState; + const [editor] = useLexicalComposerContext(); + const editorRef = useRef(editor); + useEffect(() => { + editorRef.current = editor; + }, [editor]); + const treeView = useTreeView({ + collection, + defaultExpandedValue: ['root'], + }); + useEffect(() => { + if ( + focusNodeKey !== null && + !treeView.expandedValue.includes(focusNodeKey) + ) { + treeView.expand([focusNodeKey]); + } + }, [treeView, focusNodeKey]); + const splitter = useSplitter({ + defaultSize: [50, 50], + panels: [{id: 'tree'}, {id: 'node'}], + }); + + return ( + + ); +} + +interface EditorCollectionState { + editor: LexicalEditor; + editorState: EditorState; + collection: TreeCollection; + focusNodeKey: null | NodeKey; +} + +function nextFocusNodeKey(state: EditorCollectionState): null | NodeKey { + return state.editorState.read(() => { + const selection = $getSelection(); + return selection && $isRangeSelection(selection) + ? selection.focus.getNode().getKey() + : null; + }); +} + +function initEditorCollection( + state: Omit & + Partial>, +): EditorCollectionState { + return Object.assign(state, { + collection: createTreeCollection({ + isNodeDisabled: () => false, + nodeToChildren: (nodeKey) => + state.editorState.read(() => { + const node = $getNodeByKey(nodeKey); + return $isElementNode(node) ? node.getChildrenKeys() : []; + }), + nodeToString: (nodeKey) => nodeKey, + nodeToValue: (nodeKey) => nodeKey, + rootNode: 'root', + }), + focusNodeKey: null, + }); +} + +function editorCollectionReducer( + state: EditorCollectionState, + action: Partial, +) { + let nextState = {...state, ...action}; + if (action.editor && action.editor !== state.editor) { + nextState = initEditorCollection(nextState); + } + nextState.focusNodeKey = nextFocusNodeKey(nextState); + return nextState; +} + +function useEditorCollectionState() { + const [editor] = useLexicalComposerContext(); + const editorState = useEditorState(); + const [state, dispatch] = useReducer( + editorCollectionReducer, + {editor, editorState}, + initEditorCollection, + ); + useEffect(() => { + dispatch({editor, editorState}); + }, [editor, editorState]); + return state; +} diff --git a/examples/dev-node-state-style/src/plugins/ToolbarPlugin.tsx b/examples/dev-node-state-style/src/plugins/ToolbarPlugin.tsx new file mode 100644 index 00000000000..bc3af4583cb --- /dev/null +++ b/examples/dev-node-state-style/src/plugins/ToolbarPlugin.tsx @@ -0,0 +1,162 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + */ +import {useLexicalComposerContext} from '@lexical/react/LexicalComposerContext'; +import {mergeRegister} from '@lexical/utils'; +import { + $getSelection, + $isRangeSelection, + CAN_REDO_COMMAND, + CAN_UNDO_COMMAND, + COMMAND_PRIORITY_LOW, + FORMAT_TEXT_COMMAND, + REDO_COMMAND, + SELECTION_CHANGE_COMMAND, + UNDO_COMMAND, +} from 'lexical'; +import {useCallback, useEffect, useRef, useState} from 'react'; + +import { + $selectionHasStyle, + NO_STYLE, + PATCH_TEXT_STYLE_COMMAND, +} from '../styleState'; + +function Divider() { + return
; +} + +export function ToolbarPlugin() { + const [editor] = useLexicalComposerContext(); + const toolbarRef = useRef(null); + const [canUndo, setCanUndo] = useState(false); + const [canRedo, setCanRedo] = useState(false); + const [isBold, setIsBold] = useState(false); + const [isItalic, setIsItalic] = useState(false); + const [isUnderline, setIsUnderline] = useState(false); + const [isStrikethrough, setIsStrikethrough] = useState(false); + const [isStyled, setIsStyled] = useState(false); + + const $updateToolbar = useCallback(() => { + const selection = $getSelection(); + setIsStyled($selectionHasStyle()); + if ($isRangeSelection(selection)) { + // Update text format + setIsBold(selection.hasFormat('bold')); + setIsItalic(selection.hasFormat('italic')); + setIsUnderline(selection.hasFormat('underline')); + setIsStrikethrough(selection.hasFormat('strikethrough')); + } + }, []); + + useEffect(() => { + return mergeRegister( + editor.registerUpdateListener(({editorState}) => { + editorState.read(() => { + $updateToolbar(); + }); + }), + editor.registerCommand( + SELECTION_CHANGE_COMMAND, + (_payload, _newEditor) => { + $updateToolbar(); + return false; + }, + COMMAND_PRIORITY_LOW, + ), + editor.registerCommand( + CAN_UNDO_COMMAND, + (payload) => { + setCanUndo(payload); + return false; + }, + COMMAND_PRIORITY_LOW, + ), + editor.registerCommand( + CAN_REDO_COMMAND, + (payload) => { + setCanRedo(payload); + return false; + }, + COMMAND_PRIORITY_LOW, + ), + ); + }, [editor, $updateToolbar]); + + return ( +
+ + + + + + + + + +
+ ); +} diff --git a/examples/dev-node-state-style/src/styleState.ts b/examples/dev-node-state-style/src/styleState.ts new file mode 100644 index 00000000000..69d5b66dda8 --- /dev/null +++ b/examples/dev-node-state-style/src/styleState.ts @@ -0,0 +1,449 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + */ + +import type {PropertiesHyphenFallback} from 'csstype'; + +import {domOverride, DOMRenderExtension} from '@lexical/html'; +import {$forEachSelectedTextNode} from '@lexical/selection'; +import InlineStyleParser from 'inline-style-parser'; +import { + $caretRangeFromSelection, + $getPreviousSelection, + $getSelection, + $getState, + $isRangeSelection, + $isTextNode, + $setSelection, + $setState, + COMMAND_PRIORITY_EDITOR, + configExtension, + createCommand, + createState, + defineExtension, + DOMConversionMap, + isHTMLElement, + LexicalNode, + TextNode, + ValueOrUpdater, +} from 'lexical'; + +/** + * Creates an object containing all the styles and their values provided in the CSS string. + * @param css - The CSS string of styles and their values. + * @returns The styleObject containing all the styles and their values. + */ +export function getStyleObjectFromRawCSS(css: string): StyleObject { + let styleObject: undefined | Record; + for (const token of InlineStyleParser(css, {silent: true})) { + if (token.type === 'declaration' && token.value) { + styleObject = styleObject || {}; + styleObject[token.property] = token.value; + } + } + return styleObject || NO_STYLE; +} + +export type Prettify = {[K in keyof T]: T[K]} & {}; + +export type StyleObject = Prettify<{ + [K in keyof PropertiesHyphenFallback]?: + | undefined + // This is simplified to not deal with arrays or numbers. + // This is an example after all! + | Extract; +}>; + +export type StyleTuple = Exclude< + { + [K in keyof StyleObject]: [K, null | Exclude]; + }[keyof StyleObject], + undefined +>; + +export const NO_STYLE: StyleObject = Object.freeze({}); + +function parse(v: unknown): StyleObject { + return typeof v === 'string' ? getStyleObjectFromRawCSS(v) : NO_STYLE; +} + +function unparse(style: StyleObject): string { + const styles: string[] = []; + for (const [k, v] of Object.entries(style)) { + if (k && v) { + styles.push(`${k}: ${v};`); + } + } + return styles.sort().join(' '); +} + +function isEqualValue( + a: StyleObject[keyof StyleObject], + b: StyleObject[keyof StyleObject], +): boolean { + return a === b || (!a && !b); +} + +function isEqual(a: StyleObject, b: StyleObject): boolean { + if (a === b) { + return true; + } + for (const k in a) { + if ( + !( + k in b && + isEqualValue(a[k as keyof StyleObject], b[k as keyof StyleObject]) + ) + ) { + return false; + } + } + for (const k in b) { + if (!(k in a)) { + return false; + } + } + return true; +} + +export const styleState = createState('style', { + isEqual, + parse, + unparse, +}); + +export function $getStyleProperty( + node: LexicalNode, + prop: Prop, +): undefined | StyleObject[Prop] { + return $getStyleObject(node)[prop]; +} + +// eslint-disable-next-line @lexical/rules-of-lexical +export function getStyleObjectDirect(node: LexicalNode): StyleObject { + return $getState(node, styleState, 'direct'); +} + +export function $getStyleObject(node: LexicalNode): StyleObject { + return $getState(node, styleState); +} + +export function $setStyleObject( + node: T, + valueOrUpdater: ValueOrUpdater, +): T { + return $setState(node, styleState, valueOrUpdater); +} + +export function $removeStyleProperty< + T extends LexicalNode, + Prop extends keyof StyleObject, +>(node: T, prop: Prop): T { + return $setStyleObject(node, (prevStyle) => { + if (prop in prevStyle) { + const {[prop]: _ignore, ...nextStyle} = prevStyle; + return nextStyle; + } + return prevStyle; + }); +} + +export function $setStyleProperty< + T extends LexicalNode, + Prop extends keyof StyleObject, +>(node: T, prop: Prop, value: ValueOrUpdater): T { + return $setStyleObject(node, (prevStyle) => { + const prevValue = prevStyle[prop]; + const nextValue = typeof value === 'function' ? value(prevValue) : value; + return prevValue === nextValue + ? prevStyle + : {...prevStyle, [prop]: nextValue}; + }); +} + +export function applyStyle( + element: HTMLElement, + styleObject: StyleObject, +): void { + for (const k_ in styleObject) { + const k = k_ as keyof StyleObject; + element.style.setProperty(k, styleObject[k] ?? null); + } +} + +export function diffStyleObjects( + prevStyles: StyleObject, + nextStyles: StyleObject, +): StyleObject { + let styleDiff: undefined | Record; + if (prevStyles !== nextStyles) { + for (const k_ in nextStyles) { + const k = k_ as keyof StyleObject; + const nextV = nextStyles[k]; + const prevV = prevStyles[k]; + if (!isEqualValue(nextV, prevV)) { + styleDiff = styleDiff || {}; + styleDiff[k] = nextV; + } + } + for (const k in prevStyles) { + if (!(k in nextStyles)) { + styleDiff = styleDiff || {}; + styleDiff[k] = undefined; + } + } + } + return styleDiff || NO_STYLE; +} + +export function mergeStyleObjects( + prevStyles: StyleObject, + nextStyles: StyleObject, +): StyleObject { + return prevStyles === NO_STYLE || prevStyles === nextStyles + ? nextStyles + : {...prevStyles, ...nextStyles}; +} + +export function styleObjectToArray(styleObject: StyleObject): StyleTuple[] { + const entries: StyleTuple[] = []; + for (const k_ in styleObject) { + const k = k_ as keyof StyleObject; + entries.push([k, styleObject[k] ?? null] as StyleTuple); + } + entries.sort(([a], [b]) => a.localeCompare(b)); + return entries; +} + +export const PATCH_TEXT_STYLE_COMMAND = createCommand< + StyleObject | ((prevStyles: StyleObject) => StyleObject) +>('PATCH_TEXT_STYLE_COMMAND'); + +function $nodeHasStyle(node: LexicalNode): boolean { + return !isEqual(NO_STYLE, $getStyleObject(node)); +} + +export function $selectionHasStyle(): boolean { + const selection = $getSelection(); + if ($isRangeSelection(selection)) { + const caretRange = $caretRangeFromSelection(selection); + for (const slice of caretRange.getTextSlices()) { + if (slice && $nodeHasStyle(slice.caret.origin)) { + return true; + } + } + for (const caret of caretRange.iterNodeCarets('root')) { + if ($isTextNode(caret.origin) && $nodeHasStyle(caret.origin)) { + return true; + } + } + } + return false; +} + +export function $patchSelectedTextStyle( + styleObjectOrCallback: + | StyleObject + | ((prevStyles: StyleObject) => StyleObject), +): boolean { + let selection = $getSelection(); + if (!selection) { + const prevSelection = $getPreviousSelection(); + if (!prevSelection) { + return false; + } + selection = prevSelection.clone(); + $setSelection(selection); + } + const styleCallback = + typeof styleObjectOrCallback === 'function' + ? styleObjectOrCallback + : (prevStyles: StyleObject) => + mergeStyleObjects(prevStyles, styleObjectOrCallback); + if ($isRangeSelection(selection) && selection.isCollapsed()) { + const node = selection.focus.getNode(); + if ($isTextNode(node)) { + $setStyleObject(node, styleCallback); + } + } else { + $forEachSelectedTextNode((node) => $setStyleObject(node, styleCallback)); + } + return true; +} + +const PREV_STYLE_STATE = Symbol.for('styleState'); +interface HTMLElementWithManagedStyle extends HTMLElement { + // Store the last reconciled style object directly on the DOM + // so we don't have to track the previous DOM + // which can happen even when nodeMutation is 'updated' + [PREV_STYLE_STATE]?: StyleObject; +} + +interface LexicalNodeWithUnknownStyle extends LexicalNode { + // This property exists on all TextNode and ElementNode + // and likely also some DecoratorNode by convention. + // We use it as a heuristic to see if the style has likely + // been overwritten to see if we should apply a diff + // or all styles. + __style?: unknown; +} + +function styleStringChanged( + node: LexicalNodeWithUnknownStyle, + prevNode: LexicalNodeWithUnknownStyle, +): boolean { + return typeof node.__style === 'string' && prevNode.__style !== node.__style; +} + +function getPreviousStyleObject( + node: LexicalNode, + prevNode: null | LexicalNode, + dom: HTMLElementWithManagedStyle, +): StyleObject { + const prevStyleObject = dom[PREV_STYLE_STATE]; + return prevStyleObject && prevNode && !styleStringChanged(node, prevNode) + ? prevStyleObject + : NO_STYLE; +} + +const IGNORE_STYLES: Set = new Set([ + 'font-weight', + 'text-decoration', + 'font-style', + 'vertical-align', +]); + +export type StyleMapping = (input: StyleObject) => StyleObject; + +// TODO there's no reasonable way to hook into importDOM from a plug-in https://github.com/facebook/lexical/issues/7259 +export function constructStyleImportMap( + styleMapping: StyleMapping = (input) => input, +): DOMConversionMap { + const importMap: DOMConversionMap = {}; + + // Wrap all TextNode importers with a function that also imports + // styles that are not otherwise imported + for (const [tag, fn] of Object.entries(TextNode.importDOM() || {})) { + importMap[tag] = (importNode) => { + const importer = fn(importNode); + if (!importer) { + return null; + } + return { + ...importer, + conversion: (element) => { + const output = importer.conversion(element); + if ( + output === null || + output.forChild === undefined || + output.after !== undefined || + output.node !== null || + !element.hasAttribute('style') + ) { + return output; + } + let extraStyles: undefined | Record; + for (const k of element.style) { + if (IGNORE_STYLES.has(k as keyof StyleObject)) { + continue; + } + extraStyles = extraStyles || {}; + extraStyles[k] = element.style.getPropertyValue(k); + } + if (extraStyles) { + const {forChild} = output; + return { + ...output, + forChild: (child, parent) => { + const node = forChild(child, parent); + return $isTextNode(node) + ? $setStyleObject( + node, + styleMapping(extraStyles as StyleObject), + ) + : node; + }, + }; + } + return output; + }, + }; + }; + } + return importMap; +} + +export const StyleStateExtension = defineExtension({ + dependencies: [ + configExtension(DOMRenderExtension, { + overrides: [ + domOverride('*', { + $createDOM(node, $next) { + const dom: HTMLElementWithManagedStyle = $next(); + const nextStyleObject = $getStyleObject(node); + dom[PREV_STYLE_STATE] = nextStyleObject; + applyStyle(dom, nextStyleObject); + return dom; + }, + $exportDOM(node, $next) { + const output = $next(); + const style = $getStyleObject(node); + if (output.element && style !== NO_STYLE) { + if (output.after) { + return { + ...output, + after: (generatedElement) => { + const el = output.after + ? output.after(generatedElement) + : generatedElement; + if (isHTMLElement(el)) { + applyStyle(el, style); + } + return el; + }, + }; + } else if (isHTMLElement(output.element)) { + applyStyle(output.element, style); + } + } + return output; + }, + $updateDOM( + nextNode, + prevNode, + dom: HTMLElementWithManagedStyle, + $next, + ) { + if ($next()) { + return true; + } + const prevStyleObject = getPreviousStyleObject( + nextNode, + prevNode, + dom, + ); + const nextStyleObject = $getStyleObject(nextNode); + dom[PREV_STYLE_STATE] = nextStyleObject; + applyStyle(dom, diffStyleObjects(prevStyleObject, nextStyleObject)); + return false; + }, + }), + ], + }), + ], + html: { + import: constructStyleImportMap(), + }, + name: '@lexical/examples/node-state-style/StyleState', + register(editor) { + return editor.registerCommand( + PATCH_TEXT_STYLE_COMMAND, + $patchSelectedTextStyle, + COMMAND_PRIORITY_EDITOR, + ); + }, +}); diff --git a/examples/dev-node-state-style/src/styles.css b/examples/dev-node-state-style/src/styles.css new file mode 100644 index 00000000000..7921f7ed21e --- /dev/null +++ b/examples/dev-node-state-style/src/styles.css @@ -0,0 +1,442 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + */ + +body { + margin: 0; + background: #eee; + font-family: + system-ui, + -apple-system, + BlinkMacSystemFont, + '.SFNSText-Regular', + sans-serif; + font-weight: 500; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +.other h2 { + font-size: 18px; + color: #444; + margin-bottom: 7px; +} + +.other a { + color: #777; + text-decoration: underline; + font-size: 14px; +} + +.other ul { + padding: 0; + margin: 0; + list-style-type: none; +} + +.App { + font-family: sans-serif; +} +.App > h1 { + text-align: center; +} + +h1 { + font-size: 24px; + color: #333; +} + +.editor-container { + margin: 20px auto 20px auto; + border-radius: 2px; + max-width: 600px; + color: #000; + position: relative; + line-height: 20px; + font-weight: 400; + text-align: left; + border-top-left-radius: 10px; + border-top-right-radius: 10px; +} + +.editor-inner { + background: #fff; + position: relative; +} + +.editor-input { + min-height: 150px; + resize: none; + font-size: 15px; + caret-color: rgb(5, 5, 5); + position: relative; + tab-size: 1; + outline: 0; + padding: 15px 10px; + caret-color: #444; +} + +.editor-placeholder { + color: #999; + overflow: hidden; + position: absolute; + text-overflow: ellipsis; + top: 15px; + left: 10px; + font-size: 15px; + user-select: none; + display: inline-block; + pointer-events: none; +} + +.editor-text-bold { + font-weight: bold; +} + +.editor-text-italic { + font-style: italic; +} + +.editor-text-underline { + text-decoration: underline; +} + +.editor-text-strikethrough { + text-decoration: line-through; +} + +.editor-text-underlineStrikethrough { + text-decoration: underline line-through; +} + +.editor-text-code { + background-color: rgb(240, 242, 245); + padding: 1px 0.25rem; + font-family: Menlo, Consolas, Monaco, monospace; + font-size: 94%; +} + +.editor-link { + color: rgb(33, 111, 219); + text-decoration: none; +} + +.tree-view-output { + display: block; + background: #222; + color: #fff; + padding: 5px; + font-size: 12px; + white-space: pre-wrap; + margin: 1px auto 10px auto; + max-height: 250px; + position: relative; + border-bottom-left-radius: 10px; + border-bottom-right-radius: 10px; + overflow: auto; + line-height: 14px; +} + +.editor-code { + background-color: rgb(240, 242, 245); + font-family: Menlo, Consolas, Monaco, monospace; + display: block; + padding: 8px 8px 8px 52px; + line-height: 1.53; + font-size: 13px; + margin: 0; + margin-top: 8px; + margin-bottom: 8px; + tab-size: 2; + /* white-space: pre; */ + overflow-x: auto; + position: relative; +} + +.editor-code:before { + content: attr(data-gutter); + position: absolute; + background-color: #eee; + left: 0; + top: 0; + border-right: 1px solid #ccc; + padding: 8px; + color: #777; + white-space: pre-wrap; + text-align: right; + min-width: 25px; +} +.editor-code:after { + content: attr(data-highlight-language); + top: 0; + right: 3px; + padding: 3px; + font-size: 10px; + text-transform: uppercase; + position: absolute; + color: rgba(0, 0, 0, 0.5); +} + +.editor-tokenComment { + color: slategray; +} + +.editor-tokenPunctuation { + color: #999; +} + +.editor-tokenProperty { + color: #905; +} + +.editor-tokenSelector { + color: #690; +} + +.editor-tokenOperator { + color: #9a6e3a; +} + +.editor-tokenAttr { + color: #07a; +} + +.editor-tokenVariable { + color: #e90; +} + +.editor-tokenFunction { + color: #dd4a68; +} + +.editor-paragraph { + margin: 0; + margin-bottom: 8px; + position: relative; +} + +.editor-paragraph:last-child { + margin-bottom: 0; +} + +.editor-heading-h1 { + font-size: 24px; + color: rgb(5, 5, 5); + font-weight: 400; + margin: 0; + margin-bottom: 12px; + padding: 0; +} + +.editor-heading-h2 { + font-size: 15px; + color: rgb(101, 103, 107); + font-weight: 700; + margin: 0; + margin-top: 10px; + padding: 0; + text-transform: uppercase; +} + +.editor-quote { + margin: 0; + margin-left: 20px; + font-size: 15px; + color: rgb(101, 103, 107); + border-left-color: rgb(206, 208, 212); + border-left-width: 4px; + border-left-style: solid; + padding-left: 16px; +} + +.editor-list-ol { + padding: 0; + margin: 0; + margin-left: 16px; +} + +.editor-list-ul { + padding: 0; + margin: 0; + margin-left: 16px; +} + +.editor-listitem { + margin: 8px 32px 8px 32px; +} + +.editor-nested-listitem { + list-style-type: none; +} + +pre::-webkit-scrollbar { + background: transparent; + width: 10px; +} + +pre::-webkit-scrollbar-thumb { + background: #999; +} + +.debug-timetravel-panel { + overflow: hidden; + padding: 0 0 10px 0; + margin: auto; + display: flex; +} + +.debug-timetravel-panel-slider { + padding: 0; + flex: 8; +} + +.debug-timetravel-panel-button { + padding: 0; + border: 0; + background: none; + flex: 1; + color: #fff; + font-size: 12px; +} + +.debug-timetravel-panel-button:hover { + text-decoration: underline; +} + +.debug-timetravel-button { + border: 0; + padding: 0; + font-size: 12px; + top: 10px; + right: 15px; + position: absolute; + background: none; + color: #fff; +} + +.debug-timetravel-button:hover { + text-decoration: underline; +} + +.toolbar { + display: flex; + margin-bottom: 1px; + background: #fff; + padding: 4px; + border-top-left-radius: 10px; + border-top-right-radius: 10px; + vertical-align: middle; +} + +.toolbar button.toolbar-item { + border: 0; + display: flex; + background: none; + border-radius: 10px; + padding: 8px; + cursor: pointer; + vertical-align: middle; +} + +.toolbar button.toolbar-item:disabled { + cursor: not-allowed; +} + +.toolbar button.toolbar-item.spaced { + margin-right: 2px; +} + +.toolbar button.toolbar-item i.format { + background-size: contain; + display: inline-block; + height: 18px; + width: 18px; + margin-top: 2px; + vertical-align: -0.25em; + display: flex; + opacity: 0.6; +} + +.toolbar button.toolbar-item:disabled i.format { + opacity: 0.2; +} + +.toolbar button.toolbar-item.active { + background-color: rgba(223, 232, 250, 0.3); +} + +.toolbar button.toolbar-item.active i { + opacity: 1; +} + +.toolbar .toolbar-item:hover:not([disabled]) { + background-color: #eee; +} + +.toolbar .divider { + width: 1px; + background-color: #eee; + margin: 0 4px; +} + +.toolbar .toolbar-item .text { + display: flex; + line-height: 20px; + width: 200px; + vertical-align: middle; + font-size: 14px; + color: #777; + text-overflow: ellipsis; + width: 70px; + overflow: hidden; + height: 20px; + text-align: left; +} + +.toolbar .toolbar-item .icon { + display: flex; + width: 20px; + height: 20px; + user-select: none; + margin-right: 8px; + line-height: 16px; + background-size: contain; +} + +i.undo { + background-image: url(./icons/arrow-counterclockwise.svg); +} + +i.redo { + background-image: url(./icons/arrow-clockwise.svg); +} + +i.bold { + background-image: url(./icons/type-bold.svg); +} + +i.italic { + background-image: url(./icons/type-italic.svg); +} + +i.underline { + background-image: url(./icons/type-underline.svg); +} + +i.strikethrough { + background-image: url(./icons/type-strikethrough.svg); +} + +i.text-shadow::before { + content: '✨'; + filter: contrast(0); +} + +i.text-shadow.active::before { + filter: contrast(1); + text-shadow: 1px solid black; +} diff --git a/examples/dev-node-state-style/src/vite-env.d.ts b/examples/dev-node-state-style/src/vite-env.d.ts new file mode 100644 index 00000000000..11f02fe2a00 --- /dev/null +++ b/examples/dev-node-state-style/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/examples/dev-node-state-style/tsconfig.json b/examples/dev-node-state-style/tsconfig.json new file mode 100644 index 00000000000..ccf1249ccd6 --- /dev/null +++ b/examples/dev-node-state-style/tsconfig.json @@ -0,0 +1,26 @@ +{ + "compilerOptions": { + "target": "ES2020", + "useDefineForClassFields": true, + "lib": ["ES2020", "DOM", "DOM.Iterable", "ESNext.Disposable"], + "module": "ESNext", + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "react-jsx", + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + // "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true + }, + "extends": ["../../tsconfig.json"], + "include": ["src", "../../libdefs/*.d.ts"], + "references": [{"path": "./tsconfig.node.json"}] +} diff --git a/examples/dev-node-state-style/tsconfig.node.json b/examples/dev-node-state-style/tsconfig.node.json new file mode 100644 index 00000000000..97ede7ee6f2 --- /dev/null +++ b/examples/dev-node-state-style/tsconfig.node.json @@ -0,0 +1,11 @@ +{ + "compilerOptions": { + "composite": true, + "skipLibCheck": true, + "module": "ESNext", + "moduleResolution": "bundler", + "allowSyntheticDefaultImports": true, + "strict": true + }, + "include": ["vite.config.ts"] +} diff --git a/examples/dev-node-state-style/vite.config.monorepo.ts b/examples/dev-node-state-style/vite.config.monorepo.ts new file mode 100644 index 00000000000..140511d5b57 --- /dev/null +++ b/examples/dev-node-state-style/vite.config.monorepo.ts @@ -0,0 +1,15 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + */ +import {mergeConfig} from 'vite'; + +import lexicalMonorepoPlugin from '../../packages/shared/lexicalMonorepoPlugin'; +import config from './vite.config'; + +export default mergeConfig(config, { + plugins: [lexicalMonorepoPlugin()], +}); diff --git a/examples/dev-node-state-style/vite.config.ts b/examples/dev-node-state-style/vite.config.ts new file mode 100644 index 00000000000..2294526fc4f --- /dev/null +++ b/examples/dev-node-state-style/vite.config.ts @@ -0,0 +1,14 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + */ +import react from '@vitejs/plugin-react'; +import {defineConfig} from 'vite'; + +// https://vitejs.dev/config/ +export default defineConfig({ + plugins: [react()], +}); diff --git a/examples/extension-vanilla-tailwind/package-lock.json b/examples/extension-vanilla-tailwind/package-lock.json index e77cb559719..cf2452e5c6d 100644 --- a/examples/extension-vanilla-tailwind/package-lock.json +++ b/examples/extension-vanilla-tailwind/package-lock.json @@ -824,6 +824,19 @@ "node": ">=8.6" } }, + "node_modules/micromatch/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/minimatch": { "version": "3.1.2", "dev": true, @@ -928,11 +941,13 @@ "license": "ISC" }, "node_modules/picomatch": { - "version": "2.3.1", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", "engines": { - "node": ">=8.6" + "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/jonschlinkert" @@ -1153,17 +1168,6 @@ "url": "https://github.com/sponsors/SuperchupuDev" } }, - "node_modules/tinyglobby/node_modules/picomatch": { - "version": "4.0.3", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, "node_modules/to-regex-range": { "version": "5.0.1", "dev": true, @@ -1310,17 +1314,6 @@ } } }, - "node_modules/vite/node_modules/picomatch": { - "version": "4.0.3", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, "node_modules/which": { "version": "2.0.2", "dev": true, diff --git a/package-lock.json b/package-lock.json index 2eb81a7b2b5..aa0dd4b15a6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,8 @@ "version": "0.37.0", "license": "MIT", "workspaces": [ - "packages/*" + "packages/*", + "examples/dev-*" ], "dependencies": { "semver": "^7.7.2", @@ -111,6 +112,45 @@ "eslint": "^7.31.0 || ^8.0.0" } }, + "examples/dev-node-state-style": { + "name": "@lexical/dev-node-state-style-example", + "version": "0.37.0", + "dependencies": { + "@ark-ui/react": "^5.6.0", + "@lexical/clipboard": "0.37.0", + "@lexical/extension": "0.37.0", + "@lexical/history": "0.37.0", + "@lexical/html": "0.37.0", + "@lexical/react": "0.37.0", + "@lexical/rich-text": "0.37.0", + "@lexical/selection": "0.37.0", + "@lexical/utils": "0.37.0", + "@shikijs/langs": "^3.3.0", + "@shikijs/themes": "^3.3.0", + "inline-style-parser": "^0.2.4", + "lexical": "0.37.0", + "lucide-react": "^0.503.0", + "prettier": "^3.5.3", + "react": "^19.1.0", + "react-dom": "^19.1.0", + "shiki": "^3.3.0" + }, + "devDependencies": { + "@types/react": "^19.1.2", + "@types/react-dom": "^19.1.2", + "@vitejs/plugin-react": "^5.0.2", + "cross-env": "^7.0.3", + "csstype": "^3.1.3", + "typescript": "^5.9.2", + "vite": "^7.1.4" + } + }, + "examples/dev-node-state-style/node_modules/inline-style-parser": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.6.tgz", + "integrity": "sha512-gtGXVaBdl5mAes3rPcMedEBm12ibjt1kDMFfheul1wUAOVEJW60voNdMVzVkfLN06O7ZaD/rxhfKgtlgtTbMjg==", + "license": "MIT" + }, "node_modules/@1natsu/wait-element": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/@1natsu/wait-element/-/wait-element-4.1.2.tgz", @@ -531,6 +571,88 @@ "url": "https://github.com/sponsors/antfu" } }, + "node_modules/@ark-ui/react": { + "version": "5.26.0", + "resolved": "https://registry.npmjs.org/@ark-ui/react/-/react-5.26.0.tgz", + "integrity": "sha512-SxuGfRNEu+Y9A8ixR8RJevS0RAg0hInMkhFD47X/8pFjYCdajSm6C+Pv23eDLpldV7SNFXEDUfU6wEkont/NGw==", + "license": "MIT", + "dependencies": { + "@internationalized/date": "3.10.0", + "@zag-js/accordion": "1.26.1", + "@zag-js/anatomy": "1.26.1", + "@zag-js/angle-slider": "1.26.1", + "@zag-js/async-list": "1.26.1", + "@zag-js/auto-resize": "1.26.1", + "@zag-js/avatar": "1.26.1", + "@zag-js/bottom-sheet": "1.26.1", + "@zag-js/carousel": "1.26.1", + "@zag-js/checkbox": "1.26.1", + "@zag-js/clipboard": "1.26.1", + "@zag-js/collapsible": "1.26.1", + "@zag-js/collection": "1.26.1", + "@zag-js/color-picker": "1.26.1", + "@zag-js/color-utils": "1.26.1", + "@zag-js/combobox": "1.26.1", + "@zag-js/core": "1.26.1", + "@zag-js/date-picker": "1.26.1", + "@zag-js/date-utils": "1.26.1", + "@zag-js/dialog": "1.26.1", + "@zag-js/dom-query": "1.26.1", + "@zag-js/editable": "1.26.1", + "@zag-js/file-upload": "1.26.1", + "@zag-js/file-utils": "1.26.1", + "@zag-js/floating-panel": "1.26.1", + "@zag-js/focus-trap": "1.26.1", + "@zag-js/highlight-word": "1.26.1", + "@zag-js/hover-card": "1.26.1", + "@zag-js/i18n-utils": "1.26.1", + "@zag-js/json-tree-utils": "1.26.1", + "@zag-js/listbox": "1.26.1", + "@zag-js/menu": "1.26.1", + "@zag-js/number-input": "1.26.1", + "@zag-js/pagination": "1.26.1", + "@zag-js/password-input": "1.26.1", + "@zag-js/pin-input": "1.26.1", + "@zag-js/popover": "1.26.1", + "@zag-js/presence": "1.26.1", + "@zag-js/progress": "1.26.1", + "@zag-js/qr-code": "1.26.1", + "@zag-js/radio-group": "1.26.1", + "@zag-js/rating-group": "1.26.1", + "@zag-js/react": "1.26.1", + "@zag-js/scroll-area": "1.26.1", + "@zag-js/select": "1.26.1", + "@zag-js/signature-pad": "1.26.1", + "@zag-js/slider": "1.26.1", + "@zag-js/splitter": "1.26.1", + "@zag-js/steps": "1.26.1", + "@zag-js/switch": "1.26.1", + "@zag-js/tabs": "1.26.1", + "@zag-js/tags-input": "1.26.1", + "@zag-js/timer": "1.26.1", + "@zag-js/toast": "1.26.1", + "@zag-js/toggle": "1.26.1", + "@zag-js/toggle-group": "1.26.1", + "@zag-js/tooltip": "1.26.1", + "@zag-js/tour": "1.26.1", + "@zag-js/tree-view": "1.26.1", + "@zag-js/types": "1.26.1", + "@zag-js/utils": "1.26.1" + }, + "peerDependencies": { + "react": ">=18.0.0", + "react-dom": ">=18.0.0" + } + }, + "node_modules/@ark-ui/react/node_modules/@zag-js/dom-query": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/dom-query/-/dom-query-1.26.1.tgz", + "integrity": "sha512-AcdNV+Zn+Mrz6jq6IpgBJmiooFCuGBzhbbjjU6Drh3TfNDL7q2YXinrZydoD4EGDaFOQ1h71iChTtCZSHw/yNw==", + "license": "MIT", + "dependencies": { + "@zag-js/types": "1.26.1" + } + }, "node_modules/@babel/code-frame": { "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", @@ -7601,48 +7723,6 @@ "@shikijs/vscode-textmate": "^10.0.2" } }, - "node_modules/@gerrit0/mini-shiki/node_modules/@shikijs/engine-oniguruma": { - "version": "3.12.2", - "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-3.12.2.tgz", - "integrity": "sha512-hozwnFHsLvujK4/CPVHNo3Bcg2EsnG8krI/ZQ2FlBlCRpPZW4XAEQmEwqegJsypsTAN9ehu2tEYe30lYKSZW/w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@shikijs/types": "3.12.2", - "@shikijs/vscode-textmate": "^10.0.2" - } - }, - "node_modules/@gerrit0/mini-shiki/node_modules/@shikijs/langs": { - "version": "3.12.2", - "resolved": "https://registry.npmjs.org/@shikijs/langs/-/langs-3.12.2.tgz", - "integrity": "sha512-bVx5PfuZHDSHoBal+KzJZGheFuyH4qwwcwG/n+MsWno5cTlKmaNtTsGzJpHYQ8YPbB5BdEdKU1rga5/6JGY8ww==", - "dev": true, - "license": "MIT", - "dependencies": { - "@shikijs/types": "3.12.2" - } - }, - "node_modules/@gerrit0/mini-shiki/node_modules/@shikijs/themes": { - "version": "3.12.2", - "resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-3.12.2.tgz", - "integrity": "sha512-fTR3QAgnwYpfGczpIbzPjlRnxyONJOerguQv1iwpyQZ9QXX4qy/XFQqXlf17XTsorxnHoJGbH/LXBvwtqDsF5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@shikijs/types": "3.12.2" - } - }, - "node_modules/@gerrit0/mini-shiki/node_modules/@shikijs/types": { - "version": "3.12.2", - "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-3.12.2.tgz", - "integrity": "sha512-K5UIBzxCyv0YoxN3LMrKB9zuhp1bV+LgewxuVwHdl4Gz5oePoUFrr9EfgJlGlDeXCU1b/yhdnXeuRvAnz8HN8Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@shikijs/vscode-textmate": "^10.0.2", - "@types/hast": "^3.0.4" - } - }, "node_modules/@hapi/hoek": { "version": "9.3.0", "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", @@ -7732,6 +7812,24 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, + "node_modules/@internationalized/date": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/@internationalized/date/-/date-3.10.0.tgz", + "integrity": "sha512-oxDR/NTEJ1k+UFVQElaNIk65E/Z83HK1z1WI3lQyhTtnNg4R5oVXaPzK3jcpKG8UHKDVuDQHzn+wsxSz8RP3aw==", + "license": "Apache-2.0", + "dependencies": { + "@swc/helpers": "^0.5.0" + } + }, + "node_modules/@internationalized/number": { + "version": "3.6.5", + "resolved": "https://registry.npmjs.org/@internationalized/number/-/number-3.6.5.tgz", + "integrity": "sha512-6hY4Kl4HPBvtfS62asS/R22JzNNy8vi/Ssev7x6EobfCp+9QIB2hKvI2EtbdJ0VSQacxVNtqhE/NmF/NZ0gm6g==", + "license": "Apache-2.0", + "dependencies": { + "@swc/helpers": "^0.5.0" + } + }, "node_modules/@isaacs/balanced-match": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz", @@ -8740,6 +8838,10 @@ "resolved": "packages/lexical-code-shiki", "link": true }, + "node_modules/@lexical/dev-node-state-style-example": { + "resolved": "examples/dev-node-state-style", + "link": true + }, "node_modules/@lexical/devtools": { "resolved": "packages/lexical-devtools", "link": true @@ -11700,55 +11802,61 @@ } }, "node_modules/@shikijs/core": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-3.7.0.tgz", - "integrity": "sha512-yilc0S9HvTPyahHpcum8eonYrQtmGTU0lbtwxhA6jHv4Bm1cAdlPFRCJX4AHebkCm75aKTjjRAW+DezqD1b/cg==", + "version": "3.13.0", + "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-3.13.0.tgz", + "integrity": "sha512-3P8rGsg2Eh2qIHekwuQjzWhKI4jV97PhvYjYUzGqjvJfqdQPz+nMlfWahU24GZAyW1FxFI1sYjyhfh5CoLmIUA==", + "license": "MIT", "dependencies": { - "@shikijs/types": "3.7.0", + "@shikijs/types": "3.13.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "node_modules/@shikijs/engine-javascript": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-3.7.0.tgz", - "integrity": "sha512-0t17s03Cbv+ZcUvv+y33GtX75WBLQELgNdVghnsdhTgU3hVcWcMsoP6Lb0nDTl95ZJfbP1mVMO0p3byVh3uuzA==", + "version": "3.13.0", + "resolved": "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-3.13.0.tgz", + "integrity": "sha512-Ty7xv32XCp8u0eQt8rItpMs6rU9Ki6LJ1dQOW3V/56PKDcpvfHPnYFbsx5FFUP2Yim34m/UkazidamMNVR4vKg==", + "license": "MIT", "dependencies": { - "@shikijs/types": "3.7.0", + "@shikijs/types": "3.13.0", "@shikijs/vscode-textmate": "^10.0.2", "oniguruma-to-es": "^4.3.3" } }, "node_modules/@shikijs/engine-oniguruma": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-3.7.0.tgz", - "integrity": "sha512-5BxcD6LjVWsGu4xyaBC5bu8LdNgPCVBnAkWTtOCs/CZxcB22L8rcoWfv7Hh/3WooVjBZmFtyxhgvkQFedPGnFw==", + "version": "3.13.0", + "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-3.13.0.tgz", + "integrity": "sha512-O42rBGr4UDSlhT2ZFMxqM7QzIU+IcpoTMzb3W7AlziI1ZF7R8eS2M0yt5Ry35nnnTX/LTLXFPUjRFCIW+Operg==", + "license": "MIT", "dependencies": { - "@shikijs/types": "3.7.0", + "@shikijs/types": "3.13.0", "@shikijs/vscode-textmate": "^10.0.2" } }, "node_modules/@shikijs/langs": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/@shikijs/langs/-/langs-3.7.0.tgz", - "integrity": "sha512-1zYtdfXLr9xDKLTGy5kb7O0zDQsxXiIsw1iIBcNOO8Yi5/Y1qDbJ+0VsFoqTlzdmneO8Ij35g7QKF8kcLyznCQ==", + "version": "3.13.0", + "resolved": "https://registry.npmjs.org/@shikijs/langs/-/langs-3.13.0.tgz", + "integrity": "sha512-672c3WAETDYHwrRP0yLy3W1QYB89Hbpj+pO4KhxK6FzIrDI2FoEXNiNCut6BQmEApYLfuYfpgOZaqbY+E9b8wQ==", + "license": "MIT", "dependencies": { - "@shikijs/types": "3.7.0" + "@shikijs/types": "3.13.0" } }, "node_modules/@shikijs/themes": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-3.7.0.tgz", - "integrity": "sha512-VJx8497iZPy5zLiiCTSIaOChIcKQwR0FebwE9S3rcN0+J/GTWwQ1v/bqhTbpbY3zybPKeO8wdammqkpXc4NVjQ==", + "version": "3.13.0", + "resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-3.13.0.tgz", + "integrity": "sha512-Vxw1Nm1/Od8jyA7QuAenaV78BG2nSr3/gCGdBkLpfLscddCkzkL36Q5b67SrLLfvAJTOUzW39x4FHVCFriPVgg==", + "license": "MIT", "dependencies": { - "@shikijs/types": "3.7.0" + "@shikijs/types": "3.13.0" } }, "node_modules/@shikijs/types": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-3.7.0.tgz", - "integrity": "sha512-MGaLeaRlSWpnP0XSAum3kP3a8vtcTsITqoEPYdt3lQG3YCdQH4DnEhodkYcNMcU0uW0RffhoD1O3e0vG5eSBBg==", + "version": "3.13.0", + "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-3.13.0.tgz", + "integrity": "sha512-oM9P+NCFri/mmQ8LoFGVfVyemm5Hi27330zuOBp0annwJdKH1kOLndw3zCtAVDehPLg9fKqoEx3Ht/wNZxolfw==", + "license": "MIT", "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" @@ -12393,6 +12501,15 @@ "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==", "license": "Apache-2.0" }, + "node_modules/@swc/helpers": { + "version": "0.5.17", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.17.tgz", + "integrity": "sha512-5IKx/Y13RsYd+sauPb2x+U/xZikHjolzfuDgTAl/Tdf3Q8rslRvC19NKDLgAJQ6wsqADk10ntlv08nPFw/gO/A==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.8.0" + } + }, "node_modules/@swc/html": { "version": "1.13.20", "resolved": "https://registry.npmjs.org/@swc/html/-/html-1.13.20.tgz", @@ -15141,16 +15258,531 @@ "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", "license": "Apache-2.0" }, + "node_modules/@zag-js/accordion": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/accordion/-/accordion-1.26.1.tgz", + "integrity": "sha512-Rqp5zPyWn7w1D2teZAlLytK7okRjfdU4qLuwO7SPdXgeqxr+bn7qP9Bxs4NU78nySA8ZbLZqPbmTA31m9Ya6lw==", + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "1.26.1", + "@zag-js/core": "1.26.1", + "@zag-js/dom-query": "1.26.1", + "@zag-js/types": "1.26.1", + "@zag-js/utils": "1.26.1" + } + }, + "node_modules/@zag-js/accordion/node_modules/@zag-js/dom-query": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/dom-query/-/dom-query-1.26.1.tgz", + "integrity": "sha512-AcdNV+Zn+Mrz6jq6IpgBJmiooFCuGBzhbbjjU6Drh3TfNDL7q2YXinrZydoD4EGDaFOQ1h71iChTtCZSHw/yNw==", + "license": "MIT", + "dependencies": { + "@zag-js/types": "1.26.1" + } + }, + "node_modules/@zag-js/anatomy": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/anatomy/-/anatomy-1.26.1.tgz", + "integrity": "sha512-1GVk5D/gFTvBd06w3MgDkSUGtUyU5n6XL8G6KsmuIh6dq16MgZ0TnDsUOkPVBQL3YEiHqlJfeCS6zkgcsu3q2g==", + "license": "MIT" + }, + "node_modules/@zag-js/angle-slider": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/angle-slider/-/angle-slider-1.26.1.tgz", + "integrity": "sha512-fdT6caDvT8ECyszhV6NN9CfCK5OJNK/0/456nYRclHIzrrRZdQL1ZJhSGKrZhrFM2TbEQIf/qrgoPCK3J4/GCg==", + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "1.26.1", + "@zag-js/core": "1.26.1", + "@zag-js/dom-query": "1.26.1", + "@zag-js/rect-utils": "1.26.1", + "@zag-js/types": "1.26.1", + "@zag-js/utils": "1.26.1" + } + }, + "node_modules/@zag-js/angle-slider/node_modules/@zag-js/dom-query": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/dom-query/-/dom-query-1.26.1.tgz", + "integrity": "sha512-AcdNV+Zn+Mrz6jq6IpgBJmiooFCuGBzhbbjjU6Drh3TfNDL7q2YXinrZydoD4EGDaFOQ1h71iChTtCZSHw/yNw==", + "license": "MIT", + "dependencies": { + "@zag-js/types": "1.26.1" + } + }, + "node_modules/@zag-js/aria-hidden": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/aria-hidden/-/aria-hidden-1.26.1.tgz", + "integrity": "sha512-8zZQDODCfXuJ0zfsx6+WVaYh+ScQoFG2ib2FvKr9sxztoJAn1hiLYFKzzlA1aWOxTAh+dFQC/jJI+C1nWbhBkQ==", + "license": "MIT", + "dependencies": { + "@zag-js/dom-query": "1.26.1" + } + }, + "node_modules/@zag-js/aria-hidden/node_modules/@zag-js/dom-query": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/dom-query/-/dom-query-1.26.1.tgz", + "integrity": "sha512-AcdNV+Zn+Mrz6jq6IpgBJmiooFCuGBzhbbjjU6Drh3TfNDL7q2YXinrZydoD4EGDaFOQ1h71iChTtCZSHw/yNw==", + "license": "MIT", + "dependencies": { + "@zag-js/types": "1.26.1" + } + }, + "node_modules/@zag-js/async-list": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/async-list/-/async-list-1.26.1.tgz", + "integrity": "sha512-jzgypjD/2tx90EIhb8MnjXjd6A//Veiba5NJkAfxAYsr++tG1K/aTNARBPfTunRJq6ZjDoNSZWubjaNX+llD2g==", + "license": "MIT", + "dependencies": { + "@zag-js/core": "1.26.1", + "@zag-js/utils": "1.26.1" + } + }, + "node_modules/@zag-js/auto-resize": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/auto-resize/-/auto-resize-1.26.1.tgz", + "integrity": "sha512-/t53ykgDOttYzroQQUgJqyOb+xlKClI5T1aJe/d7HOvGFqJtSSOYB9ZOJmywpkN9/11nYdH7HaZtSGmRjCQBzw==", + "license": "MIT", + "dependencies": { + "@zag-js/dom-query": "1.26.1" + } + }, + "node_modules/@zag-js/auto-resize/node_modules/@zag-js/dom-query": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/dom-query/-/dom-query-1.26.1.tgz", + "integrity": "sha512-AcdNV+Zn+Mrz6jq6IpgBJmiooFCuGBzhbbjjU6Drh3TfNDL7q2YXinrZydoD4EGDaFOQ1h71iChTtCZSHw/yNw==", + "license": "MIT", + "dependencies": { + "@zag-js/types": "1.26.1" + } + }, + "node_modules/@zag-js/avatar": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/avatar/-/avatar-1.26.1.tgz", + "integrity": "sha512-s34+KSqv/frY3W4Ng9uuuqQbVlgpeWG8b0CtGcELUw9VSldDSOE2ZMIZ1inFLoby9vOOovttHSd/QdGseXkFqQ==", + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "1.26.1", + "@zag-js/core": "1.26.1", + "@zag-js/dom-query": "1.26.1", + "@zag-js/types": "1.26.1", + "@zag-js/utils": "1.26.1" + } + }, + "node_modules/@zag-js/avatar/node_modules/@zag-js/dom-query": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/dom-query/-/dom-query-1.26.1.tgz", + "integrity": "sha512-AcdNV+Zn+Mrz6jq6IpgBJmiooFCuGBzhbbjjU6Drh3TfNDL7q2YXinrZydoD4EGDaFOQ1h71iChTtCZSHw/yNw==", + "license": "MIT", + "dependencies": { + "@zag-js/types": "1.26.1" + } + }, + "node_modules/@zag-js/bottom-sheet": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/bottom-sheet/-/bottom-sheet-1.26.1.tgz", + "integrity": "sha512-Vroa6ivzSikSdYmX/Km88g/z7W7LNEBFnYAUMSCpRb/QFStRdbVPd7POzV27sCZfoKmUONYqGCBNs3g29DAGrQ==", + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "1.26.1", + "@zag-js/aria-hidden": "1.26.1", + "@zag-js/core": "1.26.1", + "@zag-js/dismissable": "1.26.1", + "@zag-js/dom-query": "1.26.1", + "@zag-js/focus-trap": "1.26.1", + "@zag-js/remove-scroll": "1.26.1", + "@zag-js/types": "1.26.1", + "@zag-js/utils": "1.26.1" + } + }, + "node_modules/@zag-js/bottom-sheet/node_modules/@zag-js/dom-query": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/dom-query/-/dom-query-1.26.1.tgz", + "integrity": "sha512-AcdNV+Zn+Mrz6jq6IpgBJmiooFCuGBzhbbjjU6Drh3TfNDL7q2YXinrZydoD4EGDaFOQ1h71iChTtCZSHw/yNw==", + "license": "MIT", + "dependencies": { + "@zag-js/types": "1.26.1" + } + }, + "node_modules/@zag-js/carousel": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/carousel/-/carousel-1.26.1.tgz", + "integrity": "sha512-hwxO1p+fsGpu+b8ufxdQAO2UhfP1jUugmBoYnaT3eQJ7Xl/E3MuJ3OMdxgeM9zbBRZuv7IWXVzfLQTNB1lz78g==", + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "1.26.1", + "@zag-js/core": "1.26.1", + "@zag-js/dom-query": "1.26.1", + "@zag-js/scroll-snap": "1.26.1", + "@zag-js/types": "1.26.1", + "@zag-js/utils": "1.26.1" + } + }, + "node_modules/@zag-js/carousel/node_modules/@zag-js/dom-query": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/dom-query/-/dom-query-1.26.1.tgz", + "integrity": "sha512-AcdNV+Zn+Mrz6jq6IpgBJmiooFCuGBzhbbjjU6Drh3TfNDL7q2YXinrZydoD4EGDaFOQ1h71iChTtCZSHw/yNw==", + "license": "MIT", + "dependencies": { + "@zag-js/types": "1.26.1" + } + }, + "node_modules/@zag-js/checkbox": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/checkbox/-/checkbox-1.26.1.tgz", + "integrity": "sha512-IESEO/WfQ6o45E8beCIOdg0u8tmgSF9gb2F0S1HNBSTmeTq5KSaz47Z8SDkci0g9cw2i+OZIX7BPZI4XpbT3tg==", + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "1.26.1", + "@zag-js/core": "1.26.1", + "@zag-js/dom-query": "1.26.1", + "@zag-js/focus-visible": "1.26.1", + "@zag-js/types": "1.26.1", + "@zag-js/utils": "1.26.1" + } + }, + "node_modules/@zag-js/checkbox/node_modules/@zag-js/dom-query": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/dom-query/-/dom-query-1.26.1.tgz", + "integrity": "sha512-AcdNV+Zn+Mrz6jq6IpgBJmiooFCuGBzhbbjjU6Drh3TfNDL7q2YXinrZydoD4EGDaFOQ1h71iChTtCZSHw/yNw==", + "license": "MIT", + "dependencies": { + "@zag-js/types": "1.26.1" + } + }, + "node_modules/@zag-js/checkbox/node_modules/@zag-js/focus-visible": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/focus-visible/-/focus-visible-1.26.1.tgz", + "integrity": "sha512-Hq57XwQWS0a5rjiZcgWhC8ca1e75VoqVLaSGn8XM29lv13423MPRtJ6b/zkEx5G4yZmddgA+wFh2/U2KPztcYA==", + "license": "MIT", + "dependencies": { + "@zag-js/dom-query": "1.26.1" + } + }, + "node_modules/@zag-js/clipboard": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/clipboard/-/clipboard-1.26.1.tgz", + "integrity": "sha512-1yWTvFELfkRoM9GUjscU0Dl3CsLmXHzRyKttYY478xiL9jOKLaJfg6R0c42HJIXqfgIf7shG1aNOse4qmR266Q==", + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "1.26.1", + "@zag-js/core": "1.26.1", + "@zag-js/dom-query": "1.26.1", + "@zag-js/types": "1.26.1", + "@zag-js/utils": "1.26.1" + } + }, + "node_modules/@zag-js/clipboard/node_modules/@zag-js/dom-query": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/dom-query/-/dom-query-1.26.1.tgz", + "integrity": "sha512-AcdNV+Zn+Mrz6jq6IpgBJmiooFCuGBzhbbjjU6Drh3TfNDL7q2YXinrZydoD4EGDaFOQ1h71iChTtCZSHw/yNw==", + "license": "MIT", + "dependencies": { + "@zag-js/types": "1.26.1" + } + }, + "node_modules/@zag-js/collapsible": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/collapsible/-/collapsible-1.26.1.tgz", + "integrity": "sha512-B8zMtX508R71mpOunB25DXFaeXJEX1n38eOq0kUuGyggmoJdOMwLPCuzo9L9ETROynOcPwJ5YzMnhAkw7kjaBg==", + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "1.26.1", + "@zag-js/core": "1.26.1", + "@zag-js/dom-query": "1.26.1", + "@zag-js/types": "1.26.1", + "@zag-js/utils": "1.26.1" + } + }, + "node_modules/@zag-js/collapsible/node_modules/@zag-js/dom-query": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/dom-query/-/dom-query-1.26.1.tgz", + "integrity": "sha512-AcdNV+Zn+Mrz6jq6IpgBJmiooFCuGBzhbbjjU6Drh3TfNDL7q2YXinrZydoD4EGDaFOQ1h71iChTtCZSHw/yNw==", + "license": "MIT", + "dependencies": { + "@zag-js/types": "1.26.1" + } + }, + "node_modules/@zag-js/collection": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/collection/-/collection-1.26.1.tgz", + "integrity": "sha512-b23L4urNhCCH89TRr5UIWZsCtV9eaBhgTPu91qO2CAgz0fbvmkZJ/E5yr7EI4KeNg0xExbiQrhinZQt2v0LjCA==", + "license": "MIT", + "dependencies": { + "@zag-js/utils": "1.26.1" + } + }, + "node_modules/@zag-js/color-picker": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/color-picker/-/color-picker-1.26.1.tgz", + "integrity": "sha512-c3Y9RCBYqXb4HhzxZIhcNQkG4Vpr/gQa86lbxF7JnrGRCB0CyurRxhTSALZGAOKV7/VovnJEaJYDiYtQz5nGCQ==", + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "1.26.1", + "@zag-js/color-utils": "1.26.1", + "@zag-js/core": "1.26.1", + "@zag-js/dismissable": "1.26.1", + "@zag-js/dom-query": "1.26.1", + "@zag-js/popper": "1.26.1", + "@zag-js/types": "1.26.1", + "@zag-js/utils": "1.26.1" + } + }, + "node_modules/@zag-js/color-picker/node_modules/@zag-js/dom-query": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/dom-query/-/dom-query-1.26.1.tgz", + "integrity": "sha512-AcdNV+Zn+Mrz6jq6IpgBJmiooFCuGBzhbbjjU6Drh3TfNDL7q2YXinrZydoD4EGDaFOQ1h71iChTtCZSHw/yNw==", + "license": "MIT", + "dependencies": { + "@zag-js/types": "1.26.1" + } + }, + "node_modules/@zag-js/color-utils": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/color-utils/-/color-utils-1.26.1.tgz", + "integrity": "sha512-umTm464gTqMRHm3OPNkGDlb65yRlF1Ka4HrMe8eGo3l1AbQipiYE0WJf/aupUuwzm+CXKi244F1lYVnHesXHPQ==", + "license": "MIT", + "dependencies": { + "@zag-js/utils": "1.26.1" + } + }, + "node_modules/@zag-js/combobox": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/combobox/-/combobox-1.26.1.tgz", + "integrity": "sha512-VLTTLvzgurvYrsg7UEb11Ad0ZPxWp3ui9/+J3rzgrTMEuLWj8mPfWT0i0l3AG+ruyFlQv5aZNvA/pupk3UK1Tg==", + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "1.26.1", + "@zag-js/aria-hidden": "1.26.1", + "@zag-js/collection": "1.26.1", + "@zag-js/core": "1.26.1", + "@zag-js/dismissable": "1.26.1", + "@zag-js/dom-query": "1.26.1", + "@zag-js/popper": "1.26.1", + "@zag-js/types": "1.26.1", + "@zag-js/utils": "1.26.1" + } + }, + "node_modules/@zag-js/combobox/node_modules/@zag-js/dom-query": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/dom-query/-/dom-query-1.26.1.tgz", + "integrity": "sha512-AcdNV+Zn+Mrz6jq6IpgBJmiooFCuGBzhbbjjU6Drh3TfNDL7q2YXinrZydoD4EGDaFOQ1h71iChTtCZSHw/yNw==", + "license": "MIT", + "dependencies": { + "@zag-js/types": "1.26.1" + } + }, + "node_modules/@zag-js/core": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/core/-/core-1.26.1.tgz", + "integrity": "sha512-JesW6C1dlrG36Aa+yteL0v5nt4Zqa9n9coqDJUQ9L1AYWzjz6NTHubsA7ysJlTKwl411gSsssmH9Ey5sRxFEWQ==", + "license": "MIT", + "dependencies": { + "@zag-js/dom-query": "1.26.1", + "@zag-js/utils": "1.26.1" + } + }, + "node_modules/@zag-js/core/node_modules/@zag-js/dom-query": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/dom-query/-/dom-query-1.26.1.tgz", + "integrity": "sha512-AcdNV+Zn+Mrz6jq6IpgBJmiooFCuGBzhbbjjU6Drh3TfNDL7q2YXinrZydoD4EGDaFOQ1h71iChTtCZSHw/yNw==", + "license": "MIT", + "dependencies": { + "@zag-js/types": "1.26.1" + } + }, + "node_modules/@zag-js/date-picker": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/date-picker/-/date-picker-1.26.1.tgz", + "integrity": "sha512-dh105RY/SKXa0iPDmIH5qpXZ1W/Ls+AU0d2l5phcEsVItbgE+FBKYTSuC91w+LNv+MpGPH+PXvatt10tW6SYGA==", + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "1.26.1", + "@zag-js/core": "1.26.1", + "@zag-js/date-utils": "1.26.1", + "@zag-js/dismissable": "1.26.1", + "@zag-js/dom-query": "1.26.1", + "@zag-js/live-region": "1.26.1", + "@zag-js/popper": "1.26.1", + "@zag-js/types": "1.26.1", + "@zag-js/utils": "1.26.1" + }, + "peerDependencies": { + "@internationalized/date": ">=3.0.0" + } + }, + "node_modules/@zag-js/date-picker/node_modules/@zag-js/dom-query": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/dom-query/-/dom-query-1.26.1.tgz", + "integrity": "sha512-AcdNV+Zn+Mrz6jq6IpgBJmiooFCuGBzhbbjjU6Drh3TfNDL7q2YXinrZydoD4EGDaFOQ1h71iChTtCZSHw/yNw==", + "license": "MIT", + "dependencies": { + "@zag-js/types": "1.26.1" + } + }, + "node_modules/@zag-js/date-utils": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/date-utils/-/date-utils-1.26.1.tgz", + "integrity": "sha512-nMTSfIk0B7MfOIJ0EpFFrHHOGDEI/F0OCG9LVvkwlKZ3evKmgAT5Hw18h2FbQ8ovQ9gNbWoUGPPnrnZJtkzhNw==", + "license": "MIT", + "peerDependencies": { + "@internationalized/date": ">=3.0.0" + } + }, + "node_modules/@zag-js/dialog": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/dialog/-/dialog-1.26.1.tgz", + "integrity": "sha512-kbNzTIMUkn19RcqDKF+3kH5cGPW3bY2FKiw6qtPi072SshHQtuAeE4g5K3wGFdSTJXHBhXYSK04ZR+ok00YbGA==", + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "1.26.1", + "@zag-js/aria-hidden": "1.26.1", + "@zag-js/core": "1.26.1", + "@zag-js/dismissable": "1.26.1", + "@zag-js/dom-query": "1.26.1", + "@zag-js/focus-trap": "1.26.1", + "@zag-js/remove-scroll": "1.26.1", + "@zag-js/types": "1.26.1", + "@zag-js/utils": "1.26.1" + } + }, + "node_modules/@zag-js/dialog/node_modules/@zag-js/dom-query": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/dom-query/-/dom-query-1.26.1.tgz", + "integrity": "sha512-AcdNV+Zn+Mrz6jq6IpgBJmiooFCuGBzhbbjjU6Drh3TfNDL7q2YXinrZydoD4EGDaFOQ1h71iChTtCZSHw/yNw==", + "license": "MIT", + "dependencies": { + "@zag-js/types": "1.26.1" + } + }, + "node_modules/@zag-js/dismissable": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/dismissable/-/dismissable-1.26.1.tgz", + "integrity": "sha512-n7MdvInqfOh+UJ+VpeIovVU734vf8ekHhfhIugDt41xAzndZW8hdXcBsKnDFK0uY9OVk8cq5FRqpdzhoZuZSgw==", + "license": "MIT", + "dependencies": { + "@zag-js/dom-query": "1.26.1", + "@zag-js/interact-outside": "1.26.1", + "@zag-js/utils": "1.26.1" + } + }, + "node_modules/@zag-js/dismissable/node_modules/@zag-js/dom-query": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/dom-query/-/dom-query-1.26.1.tgz", + "integrity": "sha512-AcdNV+Zn+Mrz6jq6IpgBJmiooFCuGBzhbbjjU6Drh3TfNDL7q2YXinrZydoD4EGDaFOQ1h71iChTtCZSHw/yNw==", + "license": "MIT", + "dependencies": { + "@zag-js/types": "1.26.1" + } + }, "node_modules/@zag-js/dom-query": { "version": "0.16.0", "resolved": "https://registry.npmjs.org/@zag-js/dom-query/-/dom-query-0.16.0.tgz", "integrity": "sha512-Oqhd6+biWyKnhKwFFuZrrf6lxBz2tX2pRQe6grUnYwO6HJ8BcbqZomy2lpOdr+3itlaUqx+Ywj5E5ZZDr/LBfQ==" }, + "node_modules/@zag-js/editable": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/editable/-/editable-1.26.1.tgz", + "integrity": "sha512-/QBKunz9PJvd26b/ZfHSktrXWI2oHp8U9+MDVQasQnpEbG5HEGkXw6Ew3y4v/Rfq4Ih3iUSVdAMmyMU7XpYZ7g==", + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "1.26.1", + "@zag-js/core": "1.26.1", + "@zag-js/dom-query": "1.26.1", + "@zag-js/interact-outside": "1.26.1", + "@zag-js/types": "1.26.1", + "@zag-js/utils": "1.26.1" + } + }, + "node_modules/@zag-js/editable/node_modules/@zag-js/dom-query": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/dom-query/-/dom-query-1.26.1.tgz", + "integrity": "sha512-AcdNV+Zn+Mrz6jq6IpgBJmiooFCuGBzhbbjjU6Drh3TfNDL7q2YXinrZydoD4EGDaFOQ1h71iChTtCZSHw/yNw==", + "license": "MIT", + "dependencies": { + "@zag-js/types": "1.26.1" + } + }, "node_modules/@zag-js/element-size": { "version": "0.10.5", "resolved": "https://registry.npmjs.org/@zag-js/element-size/-/element-size-0.10.5.tgz", "integrity": "sha512-uQre5IidULANvVkNOBQ1tfgwTQcGl4hliPSe69Fct1VfYb2Fd0jdAcGzqQgPhfrXFpR62MxLPB7erxJ/ngtL8w==" }, + "node_modules/@zag-js/file-upload": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/file-upload/-/file-upload-1.26.1.tgz", + "integrity": "sha512-fu6UpK8+qj98g2h3XrFqWzaSD5JHNt71qEg6hlYCwsXdcpYhTObps9YsTKo4sd/XGVh0lriyzSIsjBUeoctR6A==", + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "1.26.1", + "@zag-js/core": "1.26.1", + "@zag-js/dom-query": "1.26.1", + "@zag-js/file-utils": "1.26.1", + "@zag-js/i18n-utils": "1.26.1", + "@zag-js/types": "1.26.1", + "@zag-js/utils": "1.26.1" + } + }, + "node_modules/@zag-js/file-upload/node_modules/@zag-js/dom-query": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/dom-query/-/dom-query-1.26.1.tgz", + "integrity": "sha512-AcdNV+Zn+Mrz6jq6IpgBJmiooFCuGBzhbbjjU6Drh3TfNDL7q2YXinrZydoD4EGDaFOQ1h71iChTtCZSHw/yNw==", + "license": "MIT", + "dependencies": { + "@zag-js/types": "1.26.1" + } + }, + "node_modules/@zag-js/file-utils": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/file-utils/-/file-utils-1.26.1.tgz", + "integrity": "sha512-JW0vr7fzDDi9GEQLmGWSiBbNlSK8FQ/W+tavk0E4LNVb2Hs28/4F1XQXYKNuzfN+CoBYI74EIasb7rublozJMA==", + "license": "MIT", + "dependencies": { + "@zag-js/i18n-utils": "1.26.1" + } + }, + "node_modules/@zag-js/floating-panel": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/floating-panel/-/floating-panel-1.26.1.tgz", + "integrity": "sha512-45fVFASBxM+Lj6nvq4LjlCSl/JuBwzPchv8LYfR4hRHFNUuoNpQ2MDQMFJuMzxCk50U2FAa2R0QXvi3aniEJwA==", + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "1.26.1", + "@zag-js/core": "1.26.1", + "@zag-js/dom-query": "1.26.1", + "@zag-js/popper": "1.26.1", + "@zag-js/rect-utils": "1.26.1", + "@zag-js/store": "1.26.1", + "@zag-js/types": "1.26.1", + "@zag-js/utils": "1.26.1" + } + }, + "node_modules/@zag-js/floating-panel/node_modules/@zag-js/dom-query": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/dom-query/-/dom-query-1.26.1.tgz", + "integrity": "sha512-AcdNV+Zn+Mrz6jq6IpgBJmiooFCuGBzhbbjjU6Drh3TfNDL7q2YXinrZydoD4EGDaFOQ1h71iChTtCZSHw/yNw==", + "license": "MIT", + "dependencies": { + "@zag-js/types": "1.26.1" + } + }, + "node_modules/@zag-js/focus-trap": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/focus-trap/-/focus-trap-1.26.1.tgz", + "integrity": "sha512-88NvG1vxga/Umx54p9K6+nkrC5QLOA/hc5FV68eMKn83u1PCCH8U2+RV/vhMJ7IUTEwI0VTZLOmDfyfbkfomTQ==", + "license": "MIT", + "dependencies": { + "@zag-js/dom-query": "1.26.1" + } + }, + "node_modules/@zag-js/focus-trap/node_modules/@zag-js/dom-query": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/dom-query/-/dom-query-1.26.1.tgz", + "integrity": "sha512-AcdNV+Zn+Mrz6jq6IpgBJmiooFCuGBzhbbjjU6Drh3TfNDL7q2YXinrZydoD4EGDaFOQ1h71iChTtCZSHw/yNw==", + "license": "MIT", + "dependencies": { + "@zag-js/types": "1.26.1" + } + }, "node_modules/@zag-js/focus-visible": { "version": "0.16.0", "resolved": "https://registry.npmjs.org/@zag-js/focus-visible/-/focus-visible-0.16.0.tgz", @@ -15159,6 +15791,873 @@ "@zag-js/dom-query": "0.16.0" } }, + "node_modules/@zag-js/highlight-word": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/highlight-word/-/highlight-word-1.26.1.tgz", + "integrity": "sha512-xHEeUX8Qhie+tYUBdiR2GllrVpR4gqPCOHoI8HmH/8dWgUqFScRk9BFpR2/HetV9CrURsdjT4Za0mbGaJOssag==", + "license": "MIT" + }, + "node_modules/@zag-js/hover-card": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/hover-card/-/hover-card-1.26.1.tgz", + "integrity": "sha512-Qmd6FY51JsUAxLFQEkbeNCsvzLRL2YX75EupFqNdBPL509yEJkFa1ASry6usgUZgpgMlNP9PrjFIwywyDa8gFw==", + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "1.26.1", + "@zag-js/core": "1.26.1", + "@zag-js/dismissable": "1.26.1", + "@zag-js/dom-query": "1.26.1", + "@zag-js/popper": "1.26.1", + "@zag-js/types": "1.26.1", + "@zag-js/utils": "1.26.1" + } + }, + "node_modules/@zag-js/hover-card/node_modules/@zag-js/dom-query": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/dom-query/-/dom-query-1.26.1.tgz", + "integrity": "sha512-AcdNV+Zn+Mrz6jq6IpgBJmiooFCuGBzhbbjjU6Drh3TfNDL7q2YXinrZydoD4EGDaFOQ1h71iChTtCZSHw/yNw==", + "license": "MIT", + "dependencies": { + "@zag-js/types": "1.26.1" + } + }, + "node_modules/@zag-js/i18n-utils": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/i18n-utils/-/i18n-utils-1.26.1.tgz", + "integrity": "sha512-bvzfdsiY4taqZA/QfgqofsynlJGoJGmtmV5zKcVdyTJRVQz04vSEIJ+bzEDpiqK0FtrOI2mOq3Ixmv8fKNRxlQ==", + "license": "MIT", + "dependencies": { + "@zag-js/dom-query": "1.26.1" + } + }, + "node_modules/@zag-js/i18n-utils/node_modules/@zag-js/dom-query": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/dom-query/-/dom-query-1.26.1.tgz", + "integrity": "sha512-AcdNV+Zn+Mrz6jq6IpgBJmiooFCuGBzhbbjjU6Drh3TfNDL7q2YXinrZydoD4EGDaFOQ1h71iChTtCZSHw/yNw==", + "license": "MIT", + "dependencies": { + "@zag-js/types": "1.26.1" + } + }, + "node_modules/@zag-js/interact-outside": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/interact-outside/-/interact-outside-1.26.1.tgz", + "integrity": "sha512-Kxyg8AuJq4WssVbFyCtY14O611DU7UEiKgDwiNBXdQ5V+XXd4BBBwrA5QK9pk/qjgxZawKOalwjcYsyWhFbwLQ==", + "license": "MIT", + "dependencies": { + "@zag-js/dom-query": "1.26.1", + "@zag-js/utils": "1.26.1" + } + }, + "node_modules/@zag-js/interact-outside/node_modules/@zag-js/dom-query": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/dom-query/-/dom-query-1.26.1.tgz", + "integrity": "sha512-AcdNV+Zn+Mrz6jq6IpgBJmiooFCuGBzhbbjjU6Drh3TfNDL7q2YXinrZydoD4EGDaFOQ1h71iChTtCZSHw/yNw==", + "license": "MIT", + "dependencies": { + "@zag-js/types": "1.26.1" + } + }, + "node_modules/@zag-js/json-tree-utils": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/json-tree-utils/-/json-tree-utils-1.26.1.tgz", + "integrity": "sha512-E3IDz6DgSZPJ6rKWoOARqicF9PMK5avUoqcDsQTsAM/z1SqQT1/++dzYpJ8r9rBej5n5rH1mhr+e5PmbkI5b5A==", + "license": "MIT" + }, + "node_modules/@zag-js/listbox": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/listbox/-/listbox-1.26.1.tgz", + "integrity": "sha512-Xqeb/PO4Yrj87RwP/X357ilLmrEgBIqHy/1lYnVwEVQI8l2TvElRe95oAXGORnqWRM1H0dtoNH6EjN+nm2g1WA==", + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "1.26.1", + "@zag-js/collection": "1.26.1", + "@zag-js/core": "1.26.1", + "@zag-js/dom-query": "1.26.1", + "@zag-js/focus-visible": "1.26.1", + "@zag-js/types": "1.26.1", + "@zag-js/utils": "1.26.1" + } + }, + "node_modules/@zag-js/listbox/node_modules/@zag-js/dom-query": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/dom-query/-/dom-query-1.26.1.tgz", + "integrity": "sha512-AcdNV+Zn+Mrz6jq6IpgBJmiooFCuGBzhbbjjU6Drh3TfNDL7q2YXinrZydoD4EGDaFOQ1h71iChTtCZSHw/yNw==", + "license": "MIT", + "dependencies": { + "@zag-js/types": "1.26.1" + } + }, + "node_modules/@zag-js/listbox/node_modules/@zag-js/focus-visible": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/focus-visible/-/focus-visible-1.26.1.tgz", + "integrity": "sha512-Hq57XwQWS0a5rjiZcgWhC8ca1e75VoqVLaSGn8XM29lv13423MPRtJ6b/zkEx5G4yZmddgA+wFh2/U2KPztcYA==", + "license": "MIT", + "dependencies": { + "@zag-js/dom-query": "1.26.1" + } + }, + "node_modules/@zag-js/live-region": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/live-region/-/live-region-1.26.1.tgz", + "integrity": "sha512-Z3c2/ssr5ERIVr0sLchua0e/JrPlbIN3suLZh4oYYPcxpKRjhwZQbN66xa5jm0LfIlW/Z21Obrb8X91GFwXR6w==", + "license": "MIT" + }, + "node_modules/@zag-js/menu": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/menu/-/menu-1.26.1.tgz", + "integrity": "sha512-ElFlY9yqea14XUkRXL1WA3t3SGDh+4jBMXZLIG8GY14GOzoTvruATBg1+Sbd5VVpggAbrUui33F/umWgwh6X4w==", + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "1.26.1", + "@zag-js/core": "1.26.1", + "@zag-js/dismissable": "1.26.1", + "@zag-js/dom-query": "1.26.1", + "@zag-js/popper": "1.26.1", + "@zag-js/rect-utils": "1.26.1", + "@zag-js/types": "1.26.1", + "@zag-js/utils": "1.26.1" + } + }, + "node_modules/@zag-js/menu/node_modules/@zag-js/dom-query": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/dom-query/-/dom-query-1.26.1.tgz", + "integrity": "sha512-AcdNV+Zn+Mrz6jq6IpgBJmiooFCuGBzhbbjjU6Drh3TfNDL7q2YXinrZydoD4EGDaFOQ1h71iChTtCZSHw/yNw==", + "license": "MIT", + "dependencies": { + "@zag-js/types": "1.26.1" + } + }, + "node_modules/@zag-js/number-input": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/number-input/-/number-input-1.26.1.tgz", + "integrity": "sha512-T3QnhDqKjUNn3eIQvwfOk2AEmJo1q6CrvKl71BfqO9PLOo930RfSBg0SWU16dv6kqGrH5bleRigPEwR6gLkDZQ==", + "license": "MIT", + "dependencies": { + "@internationalized/number": "3.6.5", + "@zag-js/anatomy": "1.26.1", + "@zag-js/core": "1.26.1", + "@zag-js/dom-query": "1.26.1", + "@zag-js/types": "1.26.1", + "@zag-js/utils": "1.26.1" + } + }, + "node_modules/@zag-js/number-input/node_modules/@zag-js/dom-query": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/dom-query/-/dom-query-1.26.1.tgz", + "integrity": "sha512-AcdNV+Zn+Mrz6jq6IpgBJmiooFCuGBzhbbjjU6Drh3TfNDL7q2YXinrZydoD4EGDaFOQ1h71iChTtCZSHw/yNw==", + "license": "MIT", + "dependencies": { + "@zag-js/types": "1.26.1" + } + }, + "node_modules/@zag-js/pagination": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/pagination/-/pagination-1.26.1.tgz", + "integrity": "sha512-tInkqiMj1+VoFPSN80Y7lS1ABfxTA+xgyjMyx3Y09dgHNhiOKMOYva3eTGIJaEd6OskUayc3TFKqvfT5R43KlQ==", + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "1.26.1", + "@zag-js/core": "1.26.1", + "@zag-js/dom-query": "1.26.1", + "@zag-js/types": "1.26.1", + "@zag-js/utils": "1.26.1" + } + }, + "node_modules/@zag-js/pagination/node_modules/@zag-js/dom-query": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/dom-query/-/dom-query-1.26.1.tgz", + "integrity": "sha512-AcdNV+Zn+Mrz6jq6IpgBJmiooFCuGBzhbbjjU6Drh3TfNDL7q2YXinrZydoD4EGDaFOQ1h71iChTtCZSHw/yNw==", + "license": "MIT", + "dependencies": { + "@zag-js/types": "1.26.1" + } + }, + "node_modules/@zag-js/password-input": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/password-input/-/password-input-1.26.1.tgz", + "integrity": "sha512-XI5M7uHWEX3K4uKYE5sCwm4gOCup5XHAYRO63ELFOZSKKRhPGl1IN8DeLNweDOtBJuab4ZHT2wLKgSxs/BoJLg==", + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "1.26.1", + "@zag-js/core": "1.26.1", + "@zag-js/dom-query": "1.26.1", + "@zag-js/types": "1.26.1", + "@zag-js/utils": "1.26.1" + } + }, + "node_modules/@zag-js/password-input/node_modules/@zag-js/dom-query": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/dom-query/-/dom-query-1.26.1.tgz", + "integrity": "sha512-AcdNV+Zn+Mrz6jq6IpgBJmiooFCuGBzhbbjjU6Drh3TfNDL7q2YXinrZydoD4EGDaFOQ1h71iChTtCZSHw/yNw==", + "license": "MIT", + "dependencies": { + "@zag-js/types": "1.26.1" + } + }, + "node_modules/@zag-js/pin-input": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/pin-input/-/pin-input-1.26.1.tgz", + "integrity": "sha512-M/vFnCVUQe13Thcz+bYmx3va8yD1kfCaNvMNBb7TzRVrEl/XF+nKdx1IjRi6HmCN/TkWOBCk0Lj/p/dqoALiGg==", + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "1.26.1", + "@zag-js/core": "1.26.1", + "@zag-js/dom-query": "1.26.1", + "@zag-js/types": "1.26.1", + "@zag-js/utils": "1.26.1" + } + }, + "node_modules/@zag-js/pin-input/node_modules/@zag-js/dom-query": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/dom-query/-/dom-query-1.26.1.tgz", + "integrity": "sha512-AcdNV+Zn+Mrz6jq6IpgBJmiooFCuGBzhbbjjU6Drh3TfNDL7q2YXinrZydoD4EGDaFOQ1h71iChTtCZSHw/yNw==", + "license": "MIT", + "dependencies": { + "@zag-js/types": "1.26.1" + } + }, + "node_modules/@zag-js/popover": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/popover/-/popover-1.26.1.tgz", + "integrity": "sha512-nwZfOa7W8NW7npZXqw898nGIBJ7/sk89jglO9Ealqgay5ypOSGBg4v3Afc188Q9c4jFLLqNLQ/LzUhox+h7oLA==", + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "1.26.1", + "@zag-js/aria-hidden": "1.26.1", + "@zag-js/core": "1.26.1", + "@zag-js/dismissable": "1.26.1", + "@zag-js/dom-query": "1.26.1", + "@zag-js/focus-trap": "1.26.1", + "@zag-js/popper": "1.26.1", + "@zag-js/remove-scroll": "1.26.1", + "@zag-js/types": "1.26.1", + "@zag-js/utils": "1.26.1" + } + }, + "node_modules/@zag-js/popover/node_modules/@zag-js/dom-query": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/dom-query/-/dom-query-1.26.1.tgz", + "integrity": "sha512-AcdNV+Zn+Mrz6jq6IpgBJmiooFCuGBzhbbjjU6Drh3TfNDL7q2YXinrZydoD4EGDaFOQ1h71iChTtCZSHw/yNw==", + "license": "MIT", + "dependencies": { + "@zag-js/types": "1.26.1" + } + }, + "node_modules/@zag-js/popper": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/popper/-/popper-1.26.1.tgz", + "integrity": "sha512-wY/YzdXT97gIw8wpPDyU+qf1nlnf6wYwVs3SGDCd6cbgU7iy2jo3Pn6bMJ2OrsFrLZqFVZp6lF2zQtBTNJMZtg==", + "license": "MIT", + "dependencies": { + "@floating-ui/dom": "1.7.4", + "@zag-js/dom-query": "1.26.1", + "@zag-js/utils": "1.26.1" + } + }, + "node_modules/@zag-js/popper/node_modules/@zag-js/dom-query": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/dom-query/-/dom-query-1.26.1.tgz", + "integrity": "sha512-AcdNV+Zn+Mrz6jq6IpgBJmiooFCuGBzhbbjjU6Drh3TfNDL7q2YXinrZydoD4EGDaFOQ1h71iChTtCZSHw/yNw==", + "license": "MIT", + "dependencies": { + "@zag-js/types": "1.26.1" + } + }, + "node_modules/@zag-js/presence": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/presence/-/presence-1.26.1.tgz", + "integrity": "sha512-qcbdmwVQdrc4nbHsGTTRBT9UI6lJTmT2ZVv+QQW1ZeegiIEEg/IQAuMHVaPcGGnS2Kec2OJAA174+Br8rZCKtQ==", + "license": "MIT", + "dependencies": { + "@zag-js/core": "1.26.1", + "@zag-js/dom-query": "1.26.1", + "@zag-js/types": "1.26.1" + } + }, + "node_modules/@zag-js/presence/node_modules/@zag-js/dom-query": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/dom-query/-/dom-query-1.26.1.tgz", + "integrity": "sha512-AcdNV+Zn+Mrz6jq6IpgBJmiooFCuGBzhbbjjU6Drh3TfNDL7q2YXinrZydoD4EGDaFOQ1h71iChTtCZSHw/yNw==", + "license": "MIT", + "dependencies": { + "@zag-js/types": "1.26.1" + } + }, + "node_modules/@zag-js/progress": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/progress/-/progress-1.26.1.tgz", + "integrity": "sha512-NEmLImi7ZvsTQ1B+AAZARsl3IwvdS4+qv9Wpgf+RIjJVCa0CbE3N7XS+sHuI5X4nWAKbP6Kpc3VKPwZ/9dZ7jQ==", + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "1.26.1", + "@zag-js/core": "1.26.1", + "@zag-js/dom-query": "1.26.1", + "@zag-js/types": "1.26.1", + "@zag-js/utils": "1.26.1" + } + }, + "node_modules/@zag-js/progress/node_modules/@zag-js/dom-query": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/dom-query/-/dom-query-1.26.1.tgz", + "integrity": "sha512-AcdNV+Zn+Mrz6jq6IpgBJmiooFCuGBzhbbjjU6Drh3TfNDL7q2YXinrZydoD4EGDaFOQ1h71iChTtCZSHw/yNw==", + "license": "MIT", + "dependencies": { + "@zag-js/types": "1.26.1" + } + }, + "node_modules/@zag-js/qr-code": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/qr-code/-/qr-code-1.26.1.tgz", + "integrity": "sha512-UdFzV65pSVkgzdX6k/zbGwk35GDN3b/WmHVWmOaDgUE0ySyx+Mty97Ho+qomoNVRIoKQ0z5Oshz0qyrEvX131Q==", + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "1.26.1", + "@zag-js/core": "1.26.1", + "@zag-js/dom-query": "1.26.1", + "@zag-js/types": "1.26.1", + "@zag-js/utils": "1.26.1", + "proxy-memoize": "3.0.1", + "uqr": "0.1.2" + } + }, + "node_modules/@zag-js/qr-code/node_modules/@zag-js/dom-query": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/dom-query/-/dom-query-1.26.1.tgz", + "integrity": "sha512-AcdNV+Zn+Mrz6jq6IpgBJmiooFCuGBzhbbjjU6Drh3TfNDL7q2YXinrZydoD4EGDaFOQ1h71iChTtCZSHw/yNw==", + "license": "MIT", + "dependencies": { + "@zag-js/types": "1.26.1" + } + }, + "node_modules/@zag-js/radio-group": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/radio-group/-/radio-group-1.26.1.tgz", + "integrity": "sha512-85BA4b4h6Dq3Qh9VhlRM49gJUk0NGnEqmd86LctNLXMTMeJQ9i40LMeRVlKJqeDDcXMPQDKsINCfATegq2mj2Q==", + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "1.26.1", + "@zag-js/core": "1.26.1", + "@zag-js/dom-query": "1.26.1", + "@zag-js/focus-visible": "1.26.1", + "@zag-js/types": "1.26.1", + "@zag-js/utils": "1.26.1" + } + }, + "node_modules/@zag-js/radio-group/node_modules/@zag-js/dom-query": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/dom-query/-/dom-query-1.26.1.tgz", + "integrity": "sha512-AcdNV+Zn+Mrz6jq6IpgBJmiooFCuGBzhbbjjU6Drh3TfNDL7q2YXinrZydoD4EGDaFOQ1h71iChTtCZSHw/yNw==", + "license": "MIT", + "dependencies": { + "@zag-js/types": "1.26.1" + } + }, + "node_modules/@zag-js/radio-group/node_modules/@zag-js/focus-visible": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/focus-visible/-/focus-visible-1.26.1.tgz", + "integrity": "sha512-Hq57XwQWS0a5rjiZcgWhC8ca1e75VoqVLaSGn8XM29lv13423MPRtJ6b/zkEx5G4yZmddgA+wFh2/U2KPztcYA==", + "license": "MIT", + "dependencies": { + "@zag-js/dom-query": "1.26.1" + } + }, + "node_modules/@zag-js/rating-group": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/rating-group/-/rating-group-1.26.1.tgz", + "integrity": "sha512-bhwqi7c9GtMC+ooO/BiP8NNY1RiMVYhkj6b18CCtkJHrkl+yOB3liqqE5sb7fuVe1A37S7JL+TmAySsRQLkC2g==", + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "1.26.1", + "@zag-js/core": "1.26.1", + "@zag-js/dom-query": "1.26.1", + "@zag-js/types": "1.26.1", + "@zag-js/utils": "1.26.1" + } + }, + "node_modules/@zag-js/rating-group/node_modules/@zag-js/dom-query": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/dom-query/-/dom-query-1.26.1.tgz", + "integrity": "sha512-AcdNV+Zn+Mrz6jq6IpgBJmiooFCuGBzhbbjjU6Drh3TfNDL7q2YXinrZydoD4EGDaFOQ1h71iChTtCZSHw/yNw==", + "license": "MIT", + "dependencies": { + "@zag-js/types": "1.26.1" + } + }, + "node_modules/@zag-js/react": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/react/-/react-1.26.1.tgz", + "integrity": "sha512-O9Egcylyqfw1HNGBfBFtzV5MaEvlWCjbsmdPBKbN0oPOQhIGmjPSFgU+Zvyq3IwW4fHcyUJOVgjZy2EVwMDNfA==", + "license": "MIT", + "dependencies": { + "@zag-js/core": "1.26.1", + "@zag-js/store": "1.26.1", + "@zag-js/types": "1.26.1", + "@zag-js/utils": "1.26.1" + }, + "peerDependencies": { + "react": ">=18.0.0", + "react-dom": ">=18.0.0" + } + }, + "node_modules/@zag-js/rect-utils": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/rect-utils/-/rect-utils-1.26.1.tgz", + "integrity": "sha512-ZvO4kyXXfSPrFmVKgHDTSlTagioU5KNE2VhN6tN/uJYVh2M1us2DBIWtZLGZeGSpdcCuHEpThUp1eES7ZVN8ng==", + "license": "MIT" + }, + "node_modules/@zag-js/remove-scroll": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/remove-scroll/-/remove-scroll-1.26.1.tgz", + "integrity": "sha512-m1wVmykWfpvmp3Q3jLArCvojR8SM4/DTH6I5KIZAwgIp9QiI16K83ZTSS7TiVkhwt3SgH72F9oAaJT/HA9ZHnQ==", + "license": "MIT", + "dependencies": { + "@zag-js/dom-query": "1.26.1" + } + }, + "node_modules/@zag-js/remove-scroll/node_modules/@zag-js/dom-query": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/dom-query/-/dom-query-1.26.1.tgz", + "integrity": "sha512-AcdNV+Zn+Mrz6jq6IpgBJmiooFCuGBzhbbjjU6Drh3TfNDL7q2YXinrZydoD4EGDaFOQ1h71iChTtCZSHw/yNw==", + "license": "MIT", + "dependencies": { + "@zag-js/types": "1.26.1" + } + }, + "node_modules/@zag-js/scroll-area": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/scroll-area/-/scroll-area-1.26.1.tgz", + "integrity": "sha512-+QzuY/zYzl0ZH5Xf7tLg2nTKt8h3B55lu8yvg7WavqT2nLTMcocK6Vr1r7b+9FpGyt3nMYuZ6iUUU1Rdkk2vwg==", + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "1.26.1", + "@zag-js/core": "1.26.1", + "@zag-js/dom-query": "1.26.1", + "@zag-js/types": "1.26.1", + "@zag-js/utils": "1.26.1" + } + }, + "node_modules/@zag-js/scroll-area/node_modules/@zag-js/dom-query": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/dom-query/-/dom-query-1.26.1.tgz", + "integrity": "sha512-AcdNV+Zn+Mrz6jq6IpgBJmiooFCuGBzhbbjjU6Drh3TfNDL7q2YXinrZydoD4EGDaFOQ1h71iChTtCZSHw/yNw==", + "license": "MIT", + "dependencies": { + "@zag-js/types": "1.26.1" + } + }, + "node_modules/@zag-js/scroll-snap": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/scroll-snap/-/scroll-snap-1.26.1.tgz", + "integrity": "sha512-fRRvXO0Yl9fMwrjN7zofVcFSQvHEKYRptq4W4lG2DXIKuUrmJwyTLKMREvLka6kRZ420hDfVlTj7PpSPp4OoGg==", + "license": "MIT", + "dependencies": { + "@zag-js/dom-query": "1.26.1" + } + }, + "node_modules/@zag-js/scroll-snap/node_modules/@zag-js/dom-query": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/dom-query/-/dom-query-1.26.1.tgz", + "integrity": "sha512-AcdNV+Zn+Mrz6jq6IpgBJmiooFCuGBzhbbjjU6Drh3TfNDL7q2YXinrZydoD4EGDaFOQ1h71iChTtCZSHw/yNw==", + "license": "MIT", + "dependencies": { + "@zag-js/types": "1.26.1" + } + }, + "node_modules/@zag-js/select": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/select/-/select-1.26.1.tgz", + "integrity": "sha512-posQbGiEXiLHoUeD2mGsuPPd/cSKat99h78krivAEJqFDijPSmX9l8A0o6sXXxWHFje50gqWxSH4p25IKBGQkA==", + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "1.26.1", + "@zag-js/collection": "1.26.1", + "@zag-js/core": "1.26.1", + "@zag-js/dismissable": "1.26.1", + "@zag-js/dom-query": "1.26.1", + "@zag-js/popper": "1.26.1", + "@zag-js/types": "1.26.1", + "@zag-js/utils": "1.26.1" + } + }, + "node_modules/@zag-js/select/node_modules/@zag-js/dom-query": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/dom-query/-/dom-query-1.26.1.tgz", + "integrity": "sha512-AcdNV+Zn+Mrz6jq6IpgBJmiooFCuGBzhbbjjU6Drh3TfNDL7q2YXinrZydoD4EGDaFOQ1h71iChTtCZSHw/yNw==", + "license": "MIT", + "dependencies": { + "@zag-js/types": "1.26.1" + } + }, + "node_modules/@zag-js/signature-pad": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/signature-pad/-/signature-pad-1.26.1.tgz", + "integrity": "sha512-OCx0EJ9bZ3ePz5AmPxPvSq5nQWSVUQvI4vwRFHwZXULitIycwNm7avfCVSyvCCUj0f6IlgI6SiZDeU68lpNHnA==", + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "1.26.1", + "@zag-js/core": "1.26.1", + "@zag-js/dom-query": "1.26.1", + "@zag-js/types": "1.26.1", + "@zag-js/utils": "1.26.1", + "perfect-freehand": "^1.2.2" + } + }, + "node_modules/@zag-js/signature-pad/node_modules/@zag-js/dom-query": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/dom-query/-/dom-query-1.26.1.tgz", + "integrity": "sha512-AcdNV+Zn+Mrz6jq6IpgBJmiooFCuGBzhbbjjU6Drh3TfNDL7q2YXinrZydoD4EGDaFOQ1h71iChTtCZSHw/yNw==", + "license": "MIT", + "dependencies": { + "@zag-js/types": "1.26.1" + } + }, + "node_modules/@zag-js/signature-pad/node_modules/perfect-freehand": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/perfect-freehand/-/perfect-freehand-1.2.2.tgz", + "integrity": "sha512-eh31l019WICQ03pkF3FSzHxB8n07ItqIQ++G5UV8JX0zVOXzgTGCqnRR0jJ2h9U8/2uW4W4mtGJELt9kEV0CFQ==", + "license": "MIT" + }, + "node_modules/@zag-js/slider": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/slider/-/slider-1.26.1.tgz", + "integrity": "sha512-iHFeugMk9pNZLoijEYeV49h7lLC+twxkpeiTc5KjFgKQB9n8a+H22fIVkl+B+dmIPcfr8tDpri1FfN4hh9kxpA==", + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "1.26.1", + "@zag-js/core": "1.26.1", + "@zag-js/dom-query": "1.26.1", + "@zag-js/types": "1.26.1", + "@zag-js/utils": "1.26.1" + } + }, + "node_modules/@zag-js/slider/node_modules/@zag-js/dom-query": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/dom-query/-/dom-query-1.26.1.tgz", + "integrity": "sha512-AcdNV+Zn+Mrz6jq6IpgBJmiooFCuGBzhbbjjU6Drh3TfNDL7q2YXinrZydoD4EGDaFOQ1h71iChTtCZSHw/yNw==", + "license": "MIT", + "dependencies": { + "@zag-js/types": "1.26.1" + } + }, + "node_modules/@zag-js/splitter": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/splitter/-/splitter-1.26.1.tgz", + "integrity": "sha512-QMwNOrBQ2FQU82aPkHtnfaHqI9wzJKJj2ZU0CHVRBgK9V9D77VJvtufFU0qP+6x/p5Ba26i2y9AGv2jpDGBs6w==", + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "1.26.1", + "@zag-js/core": "1.26.1", + "@zag-js/dom-query": "1.26.1", + "@zag-js/types": "1.26.1", + "@zag-js/utils": "1.26.1" + } + }, + "node_modules/@zag-js/splitter/node_modules/@zag-js/dom-query": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/dom-query/-/dom-query-1.26.1.tgz", + "integrity": "sha512-AcdNV+Zn+Mrz6jq6IpgBJmiooFCuGBzhbbjjU6Drh3TfNDL7q2YXinrZydoD4EGDaFOQ1h71iChTtCZSHw/yNw==", + "license": "MIT", + "dependencies": { + "@zag-js/types": "1.26.1" + } + }, + "node_modules/@zag-js/steps": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/steps/-/steps-1.26.1.tgz", + "integrity": "sha512-EUVpGOdOd/bjG1PiJ0lRZmfdPt7YcqL3y87mS5HZ4ZSyPp8piUOMDy7ZhZvweJB3DScF7FMfmV7ygZTNcq4dLQ==", + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "1.26.1", + "@zag-js/core": "1.26.1", + "@zag-js/dom-query": "1.26.1", + "@zag-js/types": "1.26.1", + "@zag-js/utils": "1.26.1" + } + }, + "node_modules/@zag-js/steps/node_modules/@zag-js/dom-query": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/dom-query/-/dom-query-1.26.1.tgz", + "integrity": "sha512-AcdNV+Zn+Mrz6jq6IpgBJmiooFCuGBzhbbjjU6Drh3TfNDL7q2YXinrZydoD4EGDaFOQ1h71iChTtCZSHw/yNw==", + "license": "MIT", + "dependencies": { + "@zag-js/types": "1.26.1" + } + }, + "node_modules/@zag-js/store": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/store/-/store-1.26.1.tgz", + "integrity": "sha512-0473Z38EVnl6RcjXecevzfxYbzZWEcCqsx+Q3Eti+5LKvSnl05SomiF5dYDd8qPQ167iWAQWhBYSTvasfjKa4w==", + "license": "MIT", + "dependencies": { + "proxy-compare": "3.0.1" + } + }, + "node_modules/@zag-js/switch": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/switch/-/switch-1.26.1.tgz", + "integrity": "sha512-OfOF2BYBtEeoI4RU0eYTFH3QAORNQBuVtDrsERJuWldLk9p1sukETXooTFxqaQuLRx98J3Y7duryjvTvNyA8nA==", + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "1.26.1", + "@zag-js/core": "1.26.1", + "@zag-js/dom-query": "1.26.1", + "@zag-js/focus-visible": "1.26.1", + "@zag-js/types": "1.26.1", + "@zag-js/utils": "1.26.1" + } + }, + "node_modules/@zag-js/switch/node_modules/@zag-js/dom-query": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/dom-query/-/dom-query-1.26.1.tgz", + "integrity": "sha512-AcdNV+Zn+Mrz6jq6IpgBJmiooFCuGBzhbbjjU6Drh3TfNDL7q2YXinrZydoD4EGDaFOQ1h71iChTtCZSHw/yNw==", + "license": "MIT", + "dependencies": { + "@zag-js/types": "1.26.1" + } + }, + "node_modules/@zag-js/switch/node_modules/@zag-js/focus-visible": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/focus-visible/-/focus-visible-1.26.1.tgz", + "integrity": "sha512-Hq57XwQWS0a5rjiZcgWhC8ca1e75VoqVLaSGn8XM29lv13423MPRtJ6b/zkEx5G4yZmddgA+wFh2/U2KPztcYA==", + "license": "MIT", + "dependencies": { + "@zag-js/dom-query": "1.26.1" + } + }, + "node_modules/@zag-js/tabs": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/tabs/-/tabs-1.26.1.tgz", + "integrity": "sha512-RI3Od0H6guYfflmCmpKO0hrh1FOjE/OduEEiD1XDn5foeD0uV/qhS8Wdh8R3ikgcaVjb5XrSOrCZXkuc732g8w==", + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "1.26.1", + "@zag-js/core": "1.26.1", + "@zag-js/dom-query": "1.26.1", + "@zag-js/types": "1.26.1", + "@zag-js/utils": "1.26.1" + } + }, + "node_modules/@zag-js/tabs/node_modules/@zag-js/dom-query": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/dom-query/-/dom-query-1.26.1.tgz", + "integrity": "sha512-AcdNV+Zn+Mrz6jq6IpgBJmiooFCuGBzhbbjjU6Drh3TfNDL7q2YXinrZydoD4EGDaFOQ1h71iChTtCZSHw/yNw==", + "license": "MIT", + "dependencies": { + "@zag-js/types": "1.26.1" + } + }, + "node_modules/@zag-js/tags-input": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/tags-input/-/tags-input-1.26.1.tgz", + "integrity": "sha512-Gjjsq7CQolog1mEjU07hwnTPXQ9jjln35X52EjlsjOuohkzT0TFRDj5Nuf8fqu82k2OcSx8IX09wrdGgxMKklA==", + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "1.26.1", + "@zag-js/auto-resize": "1.26.1", + "@zag-js/core": "1.26.1", + "@zag-js/dom-query": "1.26.1", + "@zag-js/interact-outside": "1.26.1", + "@zag-js/live-region": "1.26.1", + "@zag-js/types": "1.26.1", + "@zag-js/utils": "1.26.1" + } + }, + "node_modules/@zag-js/tags-input/node_modules/@zag-js/dom-query": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/dom-query/-/dom-query-1.26.1.tgz", + "integrity": "sha512-AcdNV+Zn+Mrz6jq6IpgBJmiooFCuGBzhbbjjU6Drh3TfNDL7q2YXinrZydoD4EGDaFOQ1h71iChTtCZSHw/yNw==", + "license": "MIT", + "dependencies": { + "@zag-js/types": "1.26.1" + } + }, + "node_modules/@zag-js/timer": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/timer/-/timer-1.26.1.tgz", + "integrity": "sha512-+Xi7wtxCD9kjdHNeeNpCENMezWh2ZVO6KfRoLx31PTz1Dm6h457pofsAO0u8LZEpbI3IypTiTYFeaJn3qJtVrw==", + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "1.26.1", + "@zag-js/core": "1.26.1", + "@zag-js/dom-query": "1.26.1", + "@zag-js/types": "1.26.1", + "@zag-js/utils": "1.26.1" + } + }, + "node_modules/@zag-js/timer/node_modules/@zag-js/dom-query": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/dom-query/-/dom-query-1.26.1.tgz", + "integrity": "sha512-AcdNV+Zn+Mrz6jq6IpgBJmiooFCuGBzhbbjjU6Drh3TfNDL7q2YXinrZydoD4EGDaFOQ1h71iChTtCZSHw/yNw==", + "license": "MIT", + "dependencies": { + "@zag-js/types": "1.26.1" + } + }, + "node_modules/@zag-js/toast": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/toast/-/toast-1.26.1.tgz", + "integrity": "sha512-TLcN9Bd17fI0069VOV7Ug1kGk5feUK38YKJys8/1cujG+Cu4d5iUif/GUJDrRjcI/P6dPVTzrZ9ag+pCzxkWOA==", + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "1.26.1", + "@zag-js/core": "1.26.1", + "@zag-js/dismissable": "1.26.1", + "@zag-js/dom-query": "1.26.1", + "@zag-js/types": "1.26.1", + "@zag-js/utils": "1.26.1" + } + }, + "node_modules/@zag-js/toast/node_modules/@zag-js/dom-query": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/dom-query/-/dom-query-1.26.1.tgz", + "integrity": "sha512-AcdNV+Zn+Mrz6jq6IpgBJmiooFCuGBzhbbjjU6Drh3TfNDL7q2YXinrZydoD4EGDaFOQ1h71iChTtCZSHw/yNw==", + "license": "MIT", + "dependencies": { + "@zag-js/types": "1.26.1" + } + }, + "node_modules/@zag-js/toggle": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/toggle/-/toggle-1.26.1.tgz", + "integrity": "sha512-MrwXORUWpe5adyK9ieXt7KsxsBwdaFwUn42IoPLYnw61RS92qd4DPttxENXTpdL1RpqpO/G0piIzeAm7tVB/Yw==", + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "1.26.1", + "@zag-js/core": "1.26.1", + "@zag-js/dom-query": "1.26.1", + "@zag-js/types": "1.26.1", + "@zag-js/utils": "1.26.1" + } + }, + "node_modules/@zag-js/toggle-group": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/toggle-group/-/toggle-group-1.26.1.tgz", + "integrity": "sha512-0YXWmK59E+PPxpxj/YYbRY/gP1rVRnJ/hR57XNQykLrl+uYWy16lM3CD9tke3mmLwevdMpC3JwpdPAF9AGVBVw==", + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "1.26.1", + "@zag-js/core": "1.26.1", + "@zag-js/dom-query": "1.26.1", + "@zag-js/types": "1.26.1", + "@zag-js/utils": "1.26.1" + } + }, + "node_modules/@zag-js/toggle-group/node_modules/@zag-js/dom-query": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/dom-query/-/dom-query-1.26.1.tgz", + "integrity": "sha512-AcdNV+Zn+Mrz6jq6IpgBJmiooFCuGBzhbbjjU6Drh3TfNDL7q2YXinrZydoD4EGDaFOQ1h71iChTtCZSHw/yNw==", + "license": "MIT", + "dependencies": { + "@zag-js/types": "1.26.1" + } + }, + "node_modules/@zag-js/toggle/node_modules/@zag-js/dom-query": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/dom-query/-/dom-query-1.26.1.tgz", + "integrity": "sha512-AcdNV+Zn+Mrz6jq6IpgBJmiooFCuGBzhbbjjU6Drh3TfNDL7q2YXinrZydoD4EGDaFOQ1h71iChTtCZSHw/yNw==", + "license": "MIT", + "dependencies": { + "@zag-js/types": "1.26.1" + } + }, + "node_modules/@zag-js/tooltip": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/tooltip/-/tooltip-1.26.1.tgz", + "integrity": "sha512-ezBF4DZYo7E+AjVvpOoXnoPwhSnbS3y6Fp9BMvzQRSn5qElZbdQOEZSI9R6MZBTfEo6syXE68MYyCMzKFpkQFg==", + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "1.26.1", + "@zag-js/core": "1.26.1", + "@zag-js/dom-query": "1.26.1", + "@zag-js/focus-visible": "1.26.1", + "@zag-js/popper": "1.26.1", + "@zag-js/types": "1.26.1", + "@zag-js/utils": "1.26.1" + } + }, + "node_modules/@zag-js/tooltip/node_modules/@zag-js/dom-query": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/dom-query/-/dom-query-1.26.1.tgz", + "integrity": "sha512-AcdNV+Zn+Mrz6jq6IpgBJmiooFCuGBzhbbjjU6Drh3TfNDL7q2YXinrZydoD4EGDaFOQ1h71iChTtCZSHw/yNw==", + "license": "MIT", + "dependencies": { + "@zag-js/types": "1.26.1" + } + }, + "node_modules/@zag-js/tooltip/node_modules/@zag-js/focus-visible": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/focus-visible/-/focus-visible-1.26.1.tgz", + "integrity": "sha512-Hq57XwQWS0a5rjiZcgWhC8ca1e75VoqVLaSGn8XM29lv13423MPRtJ6b/zkEx5G4yZmddgA+wFh2/U2KPztcYA==", + "license": "MIT", + "dependencies": { + "@zag-js/dom-query": "1.26.1" + } + }, + "node_modules/@zag-js/tour": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/tour/-/tour-1.26.1.tgz", + "integrity": "sha512-dNG8+fH6sVUjYsGQ8fO5gHvIZ1temhR3fe4H3lbSjqeb+8FHNXfkM1PbgNkUTmBar4JdEdWU60E/26cChDkxGQ==", + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "1.26.1", + "@zag-js/core": "1.26.1", + "@zag-js/dismissable": "1.26.1", + "@zag-js/dom-query": "1.26.1", + "@zag-js/focus-trap": "1.26.1", + "@zag-js/interact-outside": "1.26.1", + "@zag-js/popper": "1.26.1", + "@zag-js/types": "1.26.1", + "@zag-js/utils": "1.26.1" + } + }, + "node_modules/@zag-js/tour/node_modules/@zag-js/dom-query": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/dom-query/-/dom-query-1.26.1.tgz", + "integrity": "sha512-AcdNV+Zn+Mrz6jq6IpgBJmiooFCuGBzhbbjjU6Drh3TfNDL7q2YXinrZydoD4EGDaFOQ1h71iChTtCZSHw/yNw==", + "license": "MIT", + "dependencies": { + "@zag-js/types": "1.26.1" + } + }, + "node_modules/@zag-js/tree-view": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/tree-view/-/tree-view-1.26.1.tgz", + "integrity": "sha512-t+vzJ8EsDO0hAAwYbwQmQqy2aoWRWdUHJKhYk9pU8+tEwU7shepX2kSP3xbxpsXcHih5TBAzSQhGcqXi8rfnlA==", + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "1.26.1", + "@zag-js/collection": "1.26.1", + "@zag-js/core": "1.26.1", + "@zag-js/dom-query": "1.26.1", + "@zag-js/types": "1.26.1", + "@zag-js/utils": "1.26.1" + } + }, + "node_modules/@zag-js/tree-view/node_modules/@zag-js/dom-query": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/dom-query/-/dom-query-1.26.1.tgz", + "integrity": "sha512-AcdNV+Zn+Mrz6jq6IpgBJmiooFCuGBzhbbjjU6Drh3TfNDL7q2YXinrZydoD4EGDaFOQ1h71iChTtCZSHw/yNw==", + "license": "MIT", + "dependencies": { + "@zag-js/types": "1.26.1" + } + }, + "node_modules/@zag-js/types": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/types/-/types-1.26.1.tgz", + "integrity": "sha512-MXRn8x0af7ZVJ50cxCoJU7hLZ3FmPnD53UQnjkYSmOcwr1eE0Wyt7bjGUP1Zv+6hPMTlyZ18XR2rnUkKAtr7/w==", + "license": "MIT", + "dependencies": { + "csstype": "3.1.3" + } + }, + "node_modules/@zag-js/utils": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/utils/-/utils-1.26.1.tgz", + "integrity": "sha512-OeQia4UV/3osMdrhSa2NmhWvzlnlF1vxOBAdYP5R96ho0FpI15DvAaPQDvE94shzIMgzS4k0pqK5lZWK5FTmqw==", + "license": "MIT" + }, "node_modules/abab": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", @@ -30960,6 +32459,15 @@ "integrity": "sha512-AI2r85+4MquTw9ZYqabu4nMwy9Oftlfa/e/52t9IjtfG+mGBbTNdAoZ3RQKLHR6r0wQnwZnPIEh/Ya6XTWAKNA==", "optional": true }, + "node_modules/lucide-react": { + "version": "0.503.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.503.0.tgz", + "integrity": "sha512-HGGkdlPWQ0vTF8jJ5TdIqhQXZi6uh3LnNgfZ8MHiuxFfX3RZeA79r2MW2tHAZKlAVfoNE8esm3p+O6VkIvpj6w==", + "license": "ISC", + "peerDependencies": { + "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, "node_modules/lunr": { "version": "2.3.9", "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz", @@ -37845,6 +39353,21 @@ "node": ">= 0.10" } }, + "node_modules/proxy-compare": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/proxy-compare/-/proxy-compare-3.0.1.tgz", + "integrity": "sha512-V9plBAt3qjMlS1+nC8771KNf6oJ12gExvaxnNzN/9yVRLdTv/lc+oJlnSzrdYDAvBfTStPCoiaCOTmTs0adv7Q==", + "license": "MIT" + }, + "node_modules/proxy-memoize": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/proxy-memoize/-/proxy-memoize-3.0.1.tgz", + "integrity": "sha512-VDdG/VYtOgdGkWJx7y0o7p+zArSf2383Isci8C+BP3YXgMYDoPd3cCBjw0JdWb6YBb9sFiOPbAADDVTPJnh+9g==", + "license": "MIT", + "dependencies": { + "proxy-compare": "^3.0.0" + } + }, "node_modules/prr": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", @@ -40846,6 +42369,22 @@ "dev": true, "license": "MIT" }, + "node_modules/shiki": { + "version": "3.13.0", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-3.13.0.tgz", + "integrity": "sha512-aZW4l8Og16CokuCLf8CF8kq+KK2yOygapU5m3+hoGw0Mdosc6fPitjM+ujYarppj5ZIKGyPDPP1vqmQhr+5/0g==", + "license": "MIT", + "dependencies": { + "@shikijs/core": "3.13.0", + "@shikijs/engine-javascript": "3.13.0", + "@shikijs/engine-oniguruma": "3.13.0", + "@shikijs/langs": "3.13.0", + "@shikijs/themes": "3.13.0", + "@shikijs/types": "3.13.0", + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.4" + } + }, "node_modules/side-channel": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", @@ -42665,9 +44204,9 @@ } }, "node_modules/tslib": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", - "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==", + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", "license": "0BSD" }, "node_modules/tunnel-rat": { @@ -43643,6 +45182,12 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, + "node_modules/uqr": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/uqr/-/uqr-0.1.2.tgz", + "integrity": "sha512-MJu7ypHq6QasgF5YRTjqscSzQp/W11zoUk6kvmlH+fmWEs63Y0Eib13hYFwAzagRJcVY8WVnlV+eBDUGMJ5IbA==", + "license": "MIT" + }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -46294,6 +47839,7 @@ "version": "0.37.0", "license": "MIT", "dependencies": { + "@lexical/extension": "0.37.0", "@lexical/html": "0.37.0", "@lexical/list": "0.37.0", "@lexical/selection": "0.37.0", @@ -46332,21 +47878,6 @@ "@shikijs/types": "^3.7.0" } }, - "packages/lexical-code-shiki/node_modules/shiki": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/shiki/-/shiki-3.7.0.tgz", - "integrity": "sha512-ZcI4UT9n6N2pDuM2n3Jbk0sR4Swzq43nLPgS/4h0E3B/NrFn2HKElrDtceSf8Zx/OWYOo7G1SAtBLypCp+YXqg==", - "dependencies": { - "@shikijs/core": "3.7.0", - "@shikijs/engine-javascript": "3.7.0", - "@shikijs/engine-oniguruma": "3.7.0", - "@shikijs/langs": "3.7.0", - "@shikijs/themes": "3.7.0", - "@shikijs/types": "3.7.0", - "@shikijs/vscode-textmate": "^10.0.2", - "@types/hast": "^3.0.4" - } - }, "packages/lexical-devtools": { "name": "@lexical/devtools", "version": "0.37.0", @@ -46473,6 +48004,7 @@ "version": "0.37.0", "license": "MIT", "dependencies": { + "@lexical/extension": "0.37.0", "@lexical/selection": "0.37.0", "@lexical/utils": "0.37.0", "lexical": "0.37.0" @@ -47330,6 +48862,84 @@ "resolved": "https://registry.npmjs.org/@antfu/utils/-/utils-8.1.1.tgz", "integrity": "sha512-Mex9nXf9vR6AhcXmMrlz/HVgYYZpVGJ6YlPgwl7UnaFpnshXs6EK/oa5Gpf3CzENMjkvEx2tQtntGnb7UtSTOQ==" }, + "@ark-ui/react": { + "version": "5.26.0", + "resolved": "https://registry.npmjs.org/@ark-ui/react/-/react-5.26.0.tgz", + "integrity": "sha512-SxuGfRNEu+Y9A8ixR8RJevS0RAg0hInMkhFD47X/8pFjYCdajSm6C+Pv23eDLpldV7SNFXEDUfU6wEkont/NGw==", + "requires": { + "@internationalized/date": "3.10.0", + "@zag-js/accordion": "1.26.1", + "@zag-js/anatomy": "1.26.1", + "@zag-js/angle-slider": "1.26.1", + "@zag-js/async-list": "1.26.1", + "@zag-js/auto-resize": "1.26.1", + "@zag-js/avatar": "1.26.1", + "@zag-js/bottom-sheet": "1.26.1", + "@zag-js/carousel": "1.26.1", + "@zag-js/checkbox": "1.26.1", + "@zag-js/clipboard": "1.26.1", + "@zag-js/collapsible": "1.26.1", + "@zag-js/collection": "1.26.1", + "@zag-js/color-picker": "1.26.1", + "@zag-js/color-utils": "1.26.1", + "@zag-js/combobox": "1.26.1", + "@zag-js/core": "1.26.1", + "@zag-js/date-picker": "1.26.1", + "@zag-js/date-utils": "1.26.1", + "@zag-js/dialog": "1.26.1", + "@zag-js/dom-query": "1.26.1", + "@zag-js/editable": "1.26.1", + "@zag-js/file-upload": "1.26.1", + "@zag-js/file-utils": "1.26.1", + "@zag-js/floating-panel": "1.26.1", + "@zag-js/focus-trap": "1.26.1", + "@zag-js/highlight-word": "1.26.1", + "@zag-js/hover-card": "1.26.1", + "@zag-js/i18n-utils": "1.26.1", + "@zag-js/json-tree-utils": "1.26.1", + "@zag-js/listbox": "1.26.1", + "@zag-js/menu": "1.26.1", + "@zag-js/number-input": "1.26.1", + "@zag-js/pagination": "1.26.1", + "@zag-js/password-input": "1.26.1", + "@zag-js/pin-input": "1.26.1", + "@zag-js/popover": "1.26.1", + "@zag-js/presence": "1.26.1", + "@zag-js/progress": "1.26.1", + "@zag-js/qr-code": "1.26.1", + "@zag-js/radio-group": "1.26.1", + "@zag-js/rating-group": "1.26.1", + "@zag-js/react": "1.26.1", + "@zag-js/scroll-area": "1.26.1", + "@zag-js/select": "1.26.1", + "@zag-js/signature-pad": "1.26.1", + "@zag-js/slider": "1.26.1", + "@zag-js/splitter": "1.26.1", + "@zag-js/steps": "1.26.1", + "@zag-js/switch": "1.26.1", + "@zag-js/tabs": "1.26.1", + "@zag-js/tags-input": "1.26.1", + "@zag-js/timer": "1.26.1", + "@zag-js/toast": "1.26.1", + "@zag-js/toggle": "1.26.1", + "@zag-js/toggle-group": "1.26.1", + "@zag-js/tooltip": "1.26.1", + "@zag-js/tour": "1.26.1", + "@zag-js/tree-view": "1.26.1", + "@zag-js/types": "1.26.1", + "@zag-js/utils": "1.26.1" + }, + "dependencies": { + "@zag-js/dom-query": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/dom-query/-/dom-query-1.26.1.tgz", + "integrity": "sha512-AcdNV+Zn+Mrz6jq6IpgBJmiooFCuGBzhbbjjU6Drh3TfNDL7q2YXinrZydoD4EGDaFOQ1h71iChTtCZSHw/yNw==", + "requires": { + "@zag-js/types": "1.26.1" + } + } + } + }, "@babel/code-frame": { "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", @@ -51736,46 +53346,6 @@ "@shikijs/themes": "^3.12.2", "@shikijs/types": "^3.12.2", "@shikijs/vscode-textmate": "^10.0.2" - }, - "dependencies": { - "@shikijs/engine-oniguruma": { - "version": "3.12.2", - "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-3.12.2.tgz", - "integrity": "sha512-hozwnFHsLvujK4/CPVHNo3Bcg2EsnG8krI/ZQ2FlBlCRpPZW4XAEQmEwqegJsypsTAN9ehu2tEYe30lYKSZW/w==", - "dev": true, - "requires": { - "@shikijs/types": "3.12.2", - "@shikijs/vscode-textmate": "^10.0.2" - } - }, - "@shikijs/langs": { - "version": "3.12.2", - "resolved": "https://registry.npmjs.org/@shikijs/langs/-/langs-3.12.2.tgz", - "integrity": "sha512-bVx5PfuZHDSHoBal+KzJZGheFuyH4qwwcwG/n+MsWno5cTlKmaNtTsGzJpHYQ8YPbB5BdEdKU1rga5/6JGY8ww==", - "dev": true, - "requires": { - "@shikijs/types": "3.12.2" - } - }, - "@shikijs/themes": { - "version": "3.12.2", - "resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-3.12.2.tgz", - "integrity": "sha512-fTR3QAgnwYpfGczpIbzPjlRnxyONJOerguQv1iwpyQZ9QXX4qy/XFQqXlf17XTsorxnHoJGbH/LXBvwtqDsF5A==", - "dev": true, - "requires": { - "@shikijs/types": "3.12.2" - } - }, - "@shikijs/types": { - "version": "3.12.2", - "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-3.12.2.tgz", - "integrity": "sha512-K5UIBzxCyv0YoxN3LMrKB9zuhp1bV+LgewxuVwHdl4Gz5oePoUFrr9EfgJlGlDeXCU1b/yhdnXeuRvAnz8HN8Q==", - "dev": true, - "requires": { - "@shikijs/vscode-textmate": "^10.0.2", - "@types/hast": "^3.0.4" - } - } } }, "@hapi/hoek": { @@ -51849,6 +53419,22 @@ } } }, + "@internationalized/date": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/@internationalized/date/-/date-3.10.0.tgz", + "integrity": "sha512-oxDR/NTEJ1k+UFVQElaNIk65E/Z83HK1z1WI3lQyhTtnNg4R5oVXaPzK3jcpKG8UHKDVuDQHzn+wsxSz8RP3aw==", + "requires": { + "@swc/helpers": "^0.5.0" + } + }, + "@internationalized/number": { + "version": "3.6.5", + "resolved": "https://registry.npmjs.org/@internationalized/number/-/number-3.6.5.tgz", + "integrity": "sha512-6hY4Kl4HPBvtfS62asS/R22JzNNy8vi/Ssev7x6EobfCp+9QIB2hKvI2EtbdJ0VSQacxVNtqhE/NmF/NZ0gm6g==", + "requires": { + "@swc/helpers": "^0.5.0" + } + }, "@isaacs/balanced-match": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz", @@ -52564,6 +54150,7 @@ "@lexical/clipboard": { "version": "file:packages/lexical-clipboard", "requires": { + "@lexical/extension": "0.37.0", "@lexical/html": "0.37.0", "@lexical/list": "0.37.0", "@lexical/selection": "0.37.0", @@ -52592,22 +54179,42 @@ "@shikijs/types": "^3.7.0", "lexical": "0.37.0", "shiki": "^3.7.0" + } + }, + "@lexical/dev-node-state-style-example": { + "version": "file:examples/dev-node-state-style", + "requires": { + "@ark-ui/react": "^5.6.0", + "@lexical/clipboard": "0.37.0", + "@lexical/extension": "0.37.0", + "@lexical/history": "0.37.0", + "@lexical/html": "0.37.0", + "@lexical/react": "0.37.0", + "@lexical/rich-text": "0.37.0", + "@lexical/selection": "0.37.0", + "@lexical/utils": "0.37.0", + "@shikijs/langs": "^3.3.0", + "@shikijs/themes": "^3.3.0", + "@types/react": "^19.1.2", + "@types/react-dom": "^19.1.2", + "@vitejs/plugin-react": "^5.0.2", + "cross-env": "^7.0.3", + "csstype": "^3.1.3", + "inline-style-parser": "^0.2.4", + "lexical": "0.37.0", + "lucide-react": "^0.503.0", + "prettier": "^3.5.3", + "react": "^19.1.0", + "react-dom": "^19.1.0", + "shiki": "^3.3.0", + "typescript": "^5.9.2", + "vite": "^7.1.4" }, "dependencies": { - "shiki": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/shiki/-/shiki-3.7.0.tgz", - "integrity": "sha512-ZcI4UT9n6N2pDuM2n3Jbk0sR4Swzq43nLPgS/4h0E3B/NrFn2HKElrDtceSf8Zx/OWYOo7G1SAtBLypCp+YXqg==", - "requires": { - "@shikijs/core": "3.7.0", - "@shikijs/engine-javascript": "3.7.0", - "@shikijs/engine-oniguruma": "3.7.0", - "@shikijs/langs": "3.7.0", - "@shikijs/themes": "3.7.0", - "@shikijs/types": "3.7.0", - "@shikijs/vscode-textmate": "^10.0.2", - "@types/hast": "^3.0.4" - } + "inline-style-parser": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.6.tgz", + "integrity": "sha512-gtGXVaBdl5mAes3rPcMedEBm12ibjt1kDMFfheul1wUAOVEJW60voNdMVzVkfLN06O7ZaD/rxhfKgtlgtTbMjg==" } } }, @@ -52704,6 +54311,7 @@ "@lexical/html": { "version": "file:packages/lexical-html", "requires": { + "@lexical/extension": "0.37.0", "@lexical/selection": "0.37.0", "@lexical/utils": "0.37.0", "lexical": "0.37.0" @@ -54508,55 +56116,55 @@ "integrity": "sha512-VynGOEsVw2s8TAlLf/uESfrgfrq2+rcXB1muPJYBWbsm1Oa6r5qVQhjA5ggM6z/coYPrsVMgovl3Ff7Q7OCp1w==" }, "@shikijs/core": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-3.7.0.tgz", - "integrity": "sha512-yilc0S9HvTPyahHpcum8eonYrQtmGTU0lbtwxhA6jHv4Bm1cAdlPFRCJX4AHebkCm75aKTjjRAW+DezqD1b/cg==", + "version": "3.13.0", + "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-3.13.0.tgz", + "integrity": "sha512-3P8rGsg2Eh2qIHekwuQjzWhKI4jV97PhvYjYUzGqjvJfqdQPz+nMlfWahU24GZAyW1FxFI1sYjyhfh5CoLmIUA==", "requires": { - "@shikijs/types": "3.7.0", + "@shikijs/types": "3.13.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "@shikijs/engine-javascript": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-3.7.0.tgz", - "integrity": "sha512-0t17s03Cbv+ZcUvv+y33GtX75WBLQELgNdVghnsdhTgU3hVcWcMsoP6Lb0nDTl95ZJfbP1mVMO0p3byVh3uuzA==", + "version": "3.13.0", + "resolved": "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-3.13.0.tgz", + "integrity": "sha512-Ty7xv32XCp8u0eQt8rItpMs6rU9Ki6LJ1dQOW3V/56PKDcpvfHPnYFbsx5FFUP2Yim34m/UkazidamMNVR4vKg==", "requires": { - "@shikijs/types": "3.7.0", + "@shikijs/types": "3.13.0", "@shikijs/vscode-textmate": "^10.0.2", "oniguruma-to-es": "^4.3.3" } }, "@shikijs/engine-oniguruma": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-3.7.0.tgz", - "integrity": "sha512-5BxcD6LjVWsGu4xyaBC5bu8LdNgPCVBnAkWTtOCs/CZxcB22L8rcoWfv7Hh/3WooVjBZmFtyxhgvkQFedPGnFw==", + "version": "3.13.0", + "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-3.13.0.tgz", + "integrity": "sha512-O42rBGr4UDSlhT2ZFMxqM7QzIU+IcpoTMzb3W7AlziI1ZF7R8eS2M0yt5Ry35nnnTX/LTLXFPUjRFCIW+Operg==", "requires": { - "@shikijs/types": "3.7.0", + "@shikijs/types": "3.13.0", "@shikijs/vscode-textmate": "^10.0.2" } }, "@shikijs/langs": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/@shikijs/langs/-/langs-3.7.0.tgz", - "integrity": "sha512-1zYtdfXLr9xDKLTGy5kb7O0zDQsxXiIsw1iIBcNOO8Yi5/Y1qDbJ+0VsFoqTlzdmneO8Ij35g7QKF8kcLyznCQ==", + "version": "3.13.0", + "resolved": "https://registry.npmjs.org/@shikijs/langs/-/langs-3.13.0.tgz", + "integrity": "sha512-672c3WAETDYHwrRP0yLy3W1QYB89Hbpj+pO4KhxK6FzIrDI2FoEXNiNCut6BQmEApYLfuYfpgOZaqbY+E9b8wQ==", "requires": { - "@shikijs/types": "3.7.0" + "@shikijs/types": "3.13.0" } }, "@shikijs/themes": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-3.7.0.tgz", - "integrity": "sha512-VJx8497iZPy5zLiiCTSIaOChIcKQwR0FebwE9S3rcN0+J/GTWwQ1v/bqhTbpbY3zybPKeO8wdammqkpXc4NVjQ==", + "version": "3.13.0", + "resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-3.13.0.tgz", + "integrity": "sha512-Vxw1Nm1/Od8jyA7QuAenaV78BG2nSr3/gCGdBkLpfLscddCkzkL36Q5b67SrLLfvAJTOUzW39x4FHVCFriPVgg==", "requires": { - "@shikijs/types": "3.7.0" + "@shikijs/types": "3.13.0" } }, "@shikijs/types": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-3.7.0.tgz", - "integrity": "sha512-MGaLeaRlSWpnP0XSAum3kP3a8vtcTsITqoEPYdt3lQG3YCdQH4DnEhodkYcNMcU0uW0RffhoD1O3e0vG5eSBBg==", + "version": "3.13.0", + "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-3.13.0.tgz", + "integrity": "sha512-oM9P+NCFri/mmQ8LoFGVfVyemm5Hi27330zuOBp0annwJdKH1kOLndw3zCtAVDehPLg9fKqoEx3Ht/wNZxolfw==", "requires": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" @@ -54881,6 +56489,14 @@ "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==" }, + "@swc/helpers": { + "version": "0.5.17", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.17.tgz", + "integrity": "sha512-5IKx/Y13RsYd+sauPb2x+U/xZikHjolzfuDgTAl/Tdf3Q8rslRvC19NKDLgAJQ6wsqADk10ntlv08nPFw/gO/A==", + "requires": { + "tslib": "^2.8.0" + } + }, "@swc/html": { "version": "1.13.20", "resolved": "https://registry.npmjs.org/@swc/html/-/html-1.13.20.tgz", @@ -56896,16 +58512,518 @@ "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==" }, + "@zag-js/accordion": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/accordion/-/accordion-1.26.1.tgz", + "integrity": "sha512-Rqp5zPyWn7w1D2teZAlLytK7okRjfdU4qLuwO7SPdXgeqxr+bn7qP9Bxs4NU78nySA8ZbLZqPbmTA31m9Ya6lw==", + "requires": { + "@zag-js/anatomy": "1.26.1", + "@zag-js/core": "1.26.1", + "@zag-js/dom-query": "1.26.1", + "@zag-js/types": "1.26.1", + "@zag-js/utils": "1.26.1" + }, + "dependencies": { + "@zag-js/dom-query": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/dom-query/-/dom-query-1.26.1.tgz", + "integrity": "sha512-AcdNV+Zn+Mrz6jq6IpgBJmiooFCuGBzhbbjjU6Drh3TfNDL7q2YXinrZydoD4EGDaFOQ1h71iChTtCZSHw/yNw==", + "requires": { + "@zag-js/types": "1.26.1" + } + } + } + }, + "@zag-js/anatomy": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/anatomy/-/anatomy-1.26.1.tgz", + "integrity": "sha512-1GVk5D/gFTvBd06w3MgDkSUGtUyU5n6XL8G6KsmuIh6dq16MgZ0TnDsUOkPVBQL3YEiHqlJfeCS6zkgcsu3q2g==" + }, + "@zag-js/angle-slider": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/angle-slider/-/angle-slider-1.26.1.tgz", + "integrity": "sha512-fdT6caDvT8ECyszhV6NN9CfCK5OJNK/0/456nYRclHIzrrRZdQL1ZJhSGKrZhrFM2TbEQIf/qrgoPCK3J4/GCg==", + "requires": { + "@zag-js/anatomy": "1.26.1", + "@zag-js/core": "1.26.1", + "@zag-js/dom-query": "1.26.1", + "@zag-js/rect-utils": "1.26.1", + "@zag-js/types": "1.26.1", + "@zag-js/utils": "1.26.1" + }, + "dependencies": { + "@zag-js/dom-query": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/dom-query/-/dom-query-1.26.1.tgz", + "integrity": "sha512-AcdNV+Zn+Mrz6jq6IpgBJmiooFCuGBzhbbjjU6Drh3TfNDL7q2YXinrZydoD4EGDaFOQ1h71iChTtCZSHw/yNw==", + "requires": { + "@zag-js/types": "1.26.1" + } + } + } + }, + "@zag-js/aria-hidden": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/aria-hidden/-/aria-hidden-1.26.1.tgz", + "integrity": "sha512-8zZQDODCfXuJ0zfsx6+WVaYh+ScQoFG2ib2FvKr9sxztoJAn1hiLYFKzzlA1aWOxTAh+dFQC/jJI+C1nWbhBkQ==", + "requires": { + "@zag-js/dom-query": "1.26.1" + }, + "dependencies": { + "@zag-js/dom-query": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/dom-query/-/dom-query-1.26.1.tgz", + "integrity": "sha512-AcdNV+Zn+Mrz6jq6IpgBJmiooFCuGBzhbbjjU6Drh3TfNDL7q2YXinrZydoD4EGDaFOQ1h71iChTtCZSHw/yNw==", + "requires": { + "@zag-js/types": "1.26.1" + } + } + } + }, + "@zag-js/async-list": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/async-list/-/async-list-1.26.1.tgz", + "integrity": "sha512-jzgypjD/2tx90EIhb8MnjXjd6A//Veiba5NJkAfxAYsr++tG1K/aTNARBPfTunRJq6ZjDoNSZWubjaNX+llD2g==", + "requires": { + "@zag-js/core": "1.26.1", + "@zag-js/utils": "1.26.1" + } + }, + "@zag-js/auto-resize": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/auto-resize/-/auto-resize-1.26.1.tgz", + "integrity": "sha512-/t53ykgDOttYzroQQUgJqyOb+xlKClI5T1aJe/d7HOvGFqJtSSOYB9ZOJmywpkN9/11nYdH7HaZtSGmRjCQBzw==", + "requires": { + "@zag-js/dom-query": "1.26.1" + }, + "dependencies": { + "@zag-js/dom-query": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/dom-query/-/dom-query-1.26.1.tgz", + "integrity": "sha512-AcdNV+Zn+Mrz6jq6IpgBJmiooFCuGBzhbbjjU6Drh3TfNDL7q2YXinrZydoD4EGDaFOQ1h71iChTtCZSHw/yNw==", + "requires": { + "@zag-js/types": "1.26.1" + } + } + } + }, + "@zag-js/avatar": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/avatar/-/avatar-1.26.1.tgz", + "integrity": "sha512-s34+KSqv/frY3W4Ng9uuuqQbVlgpeWG8b0CtGcELUw9VSldDSOE2ZMIZ1inFLoby9vOOovttHSd/QdGseXkFqQ==", + "requires": { + "@zag-js/anatomy": "1.26.1", + "@zag-js/core": "1.26.1", + "@zag-js/dom-query": "1.26.1", + "@zag-js/types": "1.26.1", + "@zag-js/utils": "1.26.1" + }, + "dependencies": { + "@zag-js/dom-query": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/dom-query/-/dom-query-1.26.1.tgz", + "integrity": "sha512-AcdNV+Zn+Mrz6jq6IpgBJmiooFCuGBzhbbjjU6Drh3TfNDL7q2YXinrZydoD4EGDaFOQ1h71iChTtCZSHw/yNw==", + "requires": { + "@zag-js/types": "1.26.1" + } + } + } + }, + "@zag-js/bottom-sheet": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/bottom-sheet/-/bottom-sheet-1.26.1.tgz", + "integrity": "sha512-Vroa6ivzSikSdYmX/Km88g/z7W7LNEBFnYAUMSCpRb/QFStRdbVPd7POzV27sCZfoKmUONYqGCBNs3g29DAGrQ==", + "requires": { + "@zag-js/anatomy": "1.26.1", + "@zag-js/aria-hidden": "1.26.1", + "@zag-js/core": "1.26.1", + "@zag-js/dismissable": "1.26.1", + "@zag-js/dom-query": "1.26.1", + "@zag-js/focus-trap": "1.26.1", + "@zag-js/remove-scroll": "1.26.1", + "@zag-js/types": "1.26.1", + "@zag-js/utils": "1.26.1" + }, + "dependencies": { + "@zag-js/dom-query": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/dom-query/-/dom-query-1.26.1.tgz", + "integrity": "sha512-AcdNV+Zn+Mrz6jq6IpgBJmiooFCuGBzhbbjjU6Drh3TfNDL7q2YXinrZydoD4EGDaFOQ1h71iChTtCZSHw/yNw==", + "requires": { + "@zag-js/types": "1.26.1" + } + } + } + }, + "@zag-js/carousel": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/carousel/-/carousel-1.26.1.tgz", + "integrity": "sha512-hwxO1p+fsGpu+b8ufxdQAO2UhfP1jUugmBoYnaT3eQJ7Xl/E3MuJ3OMdxgeM9zbBRZuv7IWXVzfLQTNB1lz78g==", + "requires": { + "@zag-js/anatomy": "1.26.1", + "@zag-js/core": "1.26.1", + "@zag-js/dom-query": "1.26.1", + "@zag-js/scroll-snap": "1.26.1", + "@zag-js/types": "1.26.1", + "@zag-js/utils": "1.26.1" + }, + "dependencies": { + "@zag-js/dom-query": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/dom-query/-/dom-query-1.26.1.tgz", + "integrity": "sha512-AcdNV+Zn+Mrz6jq6IpgBJmiooFCuGBzhbbjjU6Drh3TfNDL7q2YXinrZydoD4EGDaFOQ1h71iChTtCZSHw/yNw==", + "requires": { + "@zag-js/types": "1.26.1" + } + } + } + }, + "@zag-js/checkbox": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/checkbox/-/checkbox-1.26.1.tgz", + "integrity": "sha512-IESEO/WfQ6o45E8beCIOdg0u8tmgSF9gb2F0S1HNBSTmeTq5KSaz47Z8SDkci0g9cw2i+OZIX7BPZI4XpbT3tg==", + "requires": { + "@zag-js/anatomy": "1.26.1", + "@zag-js/core": "1.26.1", + "@zag-js/dom-query": "1.26.1", + "@zag-js/focus-visible": "1.26.1", + "@zag-js/types": "1.26.1", + "@zag-js/utils": "1.26.1" + }, + "dependencies": { + "@zag-js/dom-query": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/dom-query/-/dom-query-1.26.1.tgz", + "integrity": "sha512-AcdNV+Zn+Mrz6jq6IpgBJmiooFCuGBzhbbjjU6Drh3TfNDL7q2YXinrZydoD4EGDaFOQ1h71iChTtCZSHw/yNw==", + "requires": { + "@zag-js/types": "1.26.1" + } + }, + "@zag-js/focus-visible": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/focus-visible/-/focus-visible-1.26.1.tgz", + "integrity": "sha512-Hq57XwQWS0a5rjiZcgWhC8ca1e75VoqVLaSGn8XM29lv13423MPRtJ6b/zkEx5G4yZmddgA+wFh2/U2KPztcYA==", + "requires": { + "@zag-js/dom-query": "1.26.1" + } + } + } + }, + "@zag-js/clipboard": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/clipboard/-/clipboard-1.26.1.tgz", + "integrity": "sha512-1yWTvFELfkRoM9GUjscU0Dl3CsLmXHzRyKttYY478xiL9jOKLaJfg6R0c42HJIXqfgIf7shG1aNOse4qmR266Q==", + "requires": { + "@zag-js/anatomy": "1.26.1", + "@zag-js/core": "1.26.1", + "@zag-js/dom-query": "1.26.1", + "@zag-js/types": "1.26.1", + "@zag-js/utils": "1.26.1" + }, + "dependencies": { + "@zag-js/dom-query": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/dom-query/-/dom-query-1.26.1.tgz", + "integrity": "sha512-AcdNV+Zn+Mrz6jq6IpgBJmiooFCuGBzhbbjjU6Drh3TfNDL7q2YXinrZydoD4EGDaFOQ1h71iChTtCZSHw/yNw==", + "requires": { + "@zag-js/types": "1.26.1" + } + } + } + }, + "@zag-js/collapsible": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/collapsible/-/collapsible-1.26.1.tgz", + "integrity": "sha512-B8zMtX508R71mpOunB25DXFaeXJEX1n38eOq0kUuGyggmoJdOMwLPCuzo9L9ETROynOcPwJ5YzMnhAkw7kjaBg==", + "requires": { + "@zag-js/anatomy": "1.26.1", + "@zag-js/core": "1.26.1", + "@zag-js/dom-query": "1.26.1", + "@zag-js/types": "1.26.1", + "@zag-js/utils": "1.26.1" + }, + "dependencies": { + "@zag-js/dom-query": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/dom-query/-/dom-query-1.26.1.tgz", + "integrity": "sha512-AcdNV+Zn+Mrz6jq6IpgBJmiooFCuGBzhbbjjU6Drh3TfNDL7q2YXinrZydoD4EGDaFOQ1h71iChTtCZSHw/yNw==", + "requires": { + "@zag-js/types": "1.26.1" + } + } + } + }, + "@zag-js/collection": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/collection/-/collection-1.26.1.tgz", + "integrity": "sha512-b23L4urNhCCH89TRr5UIWZsCtV9eaBhgTPu91qO2CAgz0fbvmkZJ/E5yr7EI4KeNg0xExbiQrhinZQt2v0LjCA==", + "requires": { + "@zag-js/utils": "1.26.1" + } + }, + "@zag-js/color-picker": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/color-picker/-/color-picker-1.26.1.tgz", + "integrity": "sha512-c3Y9RCBYqXb4HhzxZIhcNQkG4Vpr/gQa86lbxF7JnrGRCB0CyurRxhTSALZGAOKV7/VovnJEaJYDiYtQz5nGCQ==", + "requires": { + "@zag-js/anatomy": "1.26.1", + "@zag-js/color-utils": "1.26.1", + "@zag-js/core": "1.26.1", + "@zag-js/dismissable": "1.26.1", + "@zag-js/dom-query": "1.26.1", + "@zag-js/popper": "1.26.1", + "@zag-js/types": "1.26.1", + "@zag-js/utils": "1.26.1" + }, + "dependencies": { + "@zag-js/dom-query": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/dom-query/-/dom-query-1.26.1.tgz", + "integrity": "sha512-AcdNV+Zn+Mrz6jq6IpgBJmiooFCuGBzhbbjjU6Drh3TfNDL7q2YXinrZydoD4EGDaFOQ1h71iChTtCZSHw/yNw==", + "requires": { + "@zag-js/types": "1.26.1" + } + } + } + }, + "@zag-js/color-utils": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/color-utils/-/color-utils-1.26.1.tgz", + "integrity": "sha512-umTm464gTqMRHm3OPNkGDlb65yRlF1Ka4HrMe8eGo3l1AbQipiYE0WJf/aupUuwzm+CXKi244F1lYVnHesXHPQ==", + "requires": { + "@zag-js/utils": "1.26.1" + } + }, + "@zag-js/combobox": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/combobox/-/combobox-1.26.1.tgz", + "integrity": "sha512-VLTTLvzgurvYrsg7UEb11Ad0ZPxWp3ui9/+J3rzgrTMEuLWj8mPfWT0i0l3AG+ruyFlQv5aZNvA/pupk3UK1Tg==", + "requires": { + "@zag-js/anatomy": "1.26.1", + "@zag-js/aria-hidden": "1.26.1", + "@zag-js/collection": "1.26.1", + "@zag-js/core": "1.26.1", + "@zag-js/dismissable": "1.26.1", + "@zag-js/dom-query": "1.26.1", + "@zag-js/popper": "1.26.1", + "@zag-js/types": "1.26.1", + "@zag-js/utils": "1.26.1" + }, + "dependencies": { + "@zag-js/dom-query": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/dom-query/-/dom-query-1.26.1.tgz", + "integrity": "sha512-AcdNV+Zn+Mrz6jq6IpgBJmiooFCuGBzhbbjjU6Drh3TfNDL7q2YXinrZydoD4EGDaFOQ1h71iChTtCZSHw/yNw==", + "requires": { + "@zag-js/types": "1.26.1" + } + } + } + }, + "@zag-js/core": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/core/-/core-1.26.1.tgz", + "integrity": "sha512-JesW6C1dlrG36Aa+yteL0v5nt4Zqa9n9coqDJUQ9L1AYWzjz6NTHubsA7ysJlTKwl411gSsssmH9Ey5sRxFEWQ==", + "requires": { + "@zag-js/dom-query": "1.26.1", + "@zag-js/utils": "1.26.1" + }, + "dependencies": { + "@zag-js/dom-query": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/dom-query/-/dom-query-1.26.1.tgz", + "integrity": "sha512-AcdNV+Zn+Mrz6jq6IpgBJmiooFCuGBzhbbjjU6Drh3TfNDL7q2YXinrZydoD4EGDaFOQ1h71iChTtCZSHw/yNw==", + "requires": { + "@zag-js/types": "1.26.1" + } + } + } + }, + "@zag-js/date-picker": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/date-picker/-/date-picker-1.26.1.tgz", + "integrity": "sha512-dh105RY/SKXa0iPDmIH5qpXZ1W/Ls+AU0d2l5phcEsVItbgE+FBKYTSuC91w+LNv+MpGPH+PXvatt10tW6SYGA==", + "requires": { + "@zag-js/anatomy": "1.26.1", + "@zag-js/core": "1.26.1", + "@zag-js/date-utils": "1.26.1", + "@zag-js/dismissable": "1.26.1", + "@zag-js/dom-query": "1.26.1", + "@zag-js/live-region": "1.26.1", + "@zag-js/popper": "1.26.1", + "@zag-js/types": "1.26.1", + "@zag-js/utils": "1.26.1" + }, + "dependencies": { + "@zag-js/dom-query": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/dom-query/-/dom-query-1.26.1.tgz", + "integrity": "sha512-AcdNV+Zn+Mrz6jq6IpgBJmiooFCuGBzhbbjjU6Drh3TfNDL7q2YXinrZydoD4EGDaFOQ1h71iChTtCZSHw/yNw==", + "requires": { + "@zag-js/types": "1.26.1" + } + } + } + }, + "@zag-js/date-utils": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/date-utils/-/date-utils-1.26.1.tgz", + "integrity": "sha512-nMTSfIk0B7MfOIJ0EpFFrHHOGDEI/F0OCG9LVvkwlKZ3evKmgAT5Hw18h2FbQ8ovQ9gNbWoUGPPnrnZJtkzhNw==" + }, + "@zag-js/dialog": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/dialog/-/dialog-1.26.1.tgz", + "integrity": "sha512-kbNzTIMUkn19RcqDKF+3kH5cGPW3bY2FKiw6qtPi072SshHQtuAeE4g5K3wGFdSTJXHBhXYSK04ZR+ok00YbGA==", + "requires": { + "@zag-js/anatomy": "1.26.1", + "@zag-js/aria-hidden": "1.26.1", + "@zag-js/core": "1.26.1", + "@zag-js/dismissable": "1.26.1", + "@zag-js/dom-query": "1.26.1", + "@zag-js/focus-trap": "1.26.1", + "@zag-js/remove-scroll": "1.26.1", + "@zag-js/types": "1.26.1", + "@zag-js/utils": "1.26.1" + }, + "dependencies": { + "@zag-js/dom-query": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/dom-query/-/dom-query-1.26.1.tgz", + "integrity": "sha512-AcdNV+Zn+Mrz6jq6IpgBJmiooFCuGBzhbbjjU6Drh3TfNDL7q2YXinrZydoD4EGDaFOQ1h71iChTtCZSHw/yNw==", + "requires": { + "@zag-js/types": "1.26.1" + } + } + } + }, + "@zag-js/dismissable": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/dismissable/-/dismissable-1.26.1.tgz", + "integrity": "sha512-n7MdvInqfOh+UJ+VpeIovVU734vf8ekHhfhIugDt41xAzndZW8hdXcBsKnDFK0uY9OVk8cq5FRqpdzhoZuZSgw==", + "requires": { + "@zag-js/dom-query": "1.26.1", + "@zag-js/interact-outside": "1.26.1", + "@zag-js/utils": "1.26.1" + }, + "dependencies": { + "@zag-js/dom-query": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/dom-query/-/dom-query-1.26.1.tgz", + "integrity": "sha512-AcdNV+Zn+Mrz6jq6IpgBJmiooFCuGBzhbbjjU6Drh3TfNDL7q2YXinrZydoD4EGDaFOQ1h71iChTtCZSHw/yNw==", + "requires": { + "@zag-js/types": "1.26.1" + } + } + } + }, "@zag-js/dom-query": { "version": "0.16.0", "resolved": "https://registry.npmjs.org/@zag-js/dom-query/-/dom-query-0.16.0.tgz", "integrity": "sha512-Oqhd6+biWyKnhKwFFuZrrf6lxBz2tX2pRQe6grUnYwO6HJ8BcbqZomy2lpOdr+3itlaUqx+Ywj5E5ZZDr/LBfQ==" }, + "@zag-js/editable": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/editable/-/editable-1.26.1.tgz", + "integrity": "sha512-/QBKunz9PJvd26b/ZfHSktrXWI2oHp8U9+MDVQasQnpEbG5HEGkXw6Ew3y4v/Rfq4Ih3iUSVdAMmyMU7XpYZ7g==", + "requires": { + "@zag-js/anatomy": "1.26.1", + "@zag-js/core": "1.26.1", + "@zag-js/dom-query": "1.26.1", + "@zag-js/interact-outside": "1.26.1", + "@zag-js/types": "1.26.1", + "@zag-js/utils": "1.26.1" + }, + "dependencies": { + "@zag-js/dom-query": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/dom-query/-/dom-query-1.26.1.tgz", + "integrity": "sha512-AcdNV+Zn+Mrz6jq6IpgBJmiooFCuGBzhbbjjU6Drh3TfNDL7q2YXinrZydoD4EGDaFOQ1h71iChTtCZSHw/yNw==", + "requires": { + "@zag-js/types": "1.26.1" + } + } + } + }, "@zag-js/element-size": { "version": "0.10.5", "resolved": "https://registry.npmjs.org/@zag-js/element-size/-/element-size-0.10.5.tgz", "integrity": "sha512-uQre5IidULANvVkNOBQ1tfgwTQcGl4hliPSe69Fct1VfYb2Fd0jdAcGzqQgPhfrXFpR62MxLPB7erxJ/ngtL8w==" }, + "@zag-js/file-upload": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/file-upload/-/file-upload-1.26.1.tgz", + "integrity": "sha512-fu6UpK8+qj98g2h3XrFqWzaSD5JHNt71qEg6hlYCwsXdcpYhTObps9YsTKo4sd/XGVh0lriyzSIsjBUeoctR6A==", + "requires": { + "@zag-js/anatomy": "1.26.1", + "@zag-js/core": "1.26.1", + "@zag-js/dom-query": "1.26.1", + "@zag-js/file-utils": "1.26.1", + "@zag-js/i18n-utils": "1.26.1", + "@zag-js/types": "1.26.1", + "@zag-js/utils": "1.26.1" + }, + "dependencies": { + "@zag-js/dom-query": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/dom-query/-/dom-query-1.26.1.tgz", + "integrity": "sha512-AcdNV+Zn+Mrz6jq6IpgBJmiooFCuGBzhbbjjU6Drh3TfNDL7q2YXinrZydoD4EGDaFOQ1h71iChTtCZSHw/yNw==", + "requires": { + "@zag-js/types": "1.26.1" + } + } + } + }, + "@zag-js/file-utils": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/file-utils/-/file-utils-1.26.1.tgz", + "integrity": "sha512-JW0vr7fzDDi9GEQLmGWSiBbNlSK8FQ/W+tavk0E4LNVb2Hs28/4F1XQXYKNuzfN+CoBYI74EIasb7rublozJMA==", + "requires": { + "@zag-js/i18n-utils": "1.26.1" + } + }, + "@zag-js/floating-panel": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/floating-panel/-/floating-panel-1.26.1.tgz", + "integrity": "sha512-45fVFASBxM+Lj6nvq4LjlCSl/JuBwzPchv8LYfR4hRHFNUuoNpQ2MDQMFJuMzxCk50U2FAa2R0QXvi3aniEJwA==", + "requires": { + "@zag-js/anatomy": "1.26.1", + "@zag-js/core": "1.26.1", + "@zag-js/dom-query": "1.26.1", + "@zag-js/popper": "1.26.1", + "@zag-js/rect-utils": "1.26.1", + "@zag-js/store": "1.26.1", + "@zag-js/types": "1.26.1", + "@zag-js/utils": "1.26.1" + }, + "dependencies": { + "@zag-js/dom-query": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/dom-query/-/dom-query-1.26.1.tgz", + "integrity": "sha512-AcdNV+Zn+Mrz6jq6IpgBJmiooFCuGBzhbbjjU6Drh3TfNDL7q2YXinrZydoD4EGDaFOQ1h71iChTtCZSHw/yNw==", + "requires": { + "@zag-js/types": "1.26.1" + } + } + } + }, + "@zag-js/focus-trap": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/focus-trap/-/focus-trap-1.26.1.tgz", + "integrity": "sha512-88NvG1vxga/Umx54p9K6+nkrC5QLOA/hc5FV68eMKn83u1PCCH8U2+RV/vhMJ7IUTEwI0VTZLOmDfyfbkfomTQ==", + "requires": { + "@zag-js/dom-query": "1.26.1" + }, + "dependencies": { + "@zag-js/dom-query": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/dom-query/-/dom-query-1.26.1.tgz", + "integrity": "sha512-AcdNV+Zn+Mrz6jq6IpgBJmiooFCuGBzhbbjjU6Drh3TfNDL7q2YXinrZydoD4EGDaFOQ1h71iChTtCZSHw/yNw==", + "requires": { + "@zag-js/types": "1.26.1" + } + } + } + }, "@zag-js/focus-visible": { "version": "0.16.0", "resolved": "https://registry.npmjs.org/@zag-js/focus-visible/-/focus-visible-0.16.0.tgz", @@ -56914,6 +59032,856 @@ "@zag-js/dom-query": "0.16.0" } }, + "@zag-js/highlight-word": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/highlight-word/-/highlight-word-1.26.1.tgz", + "integrity": "sha512-xHEeUX8Qhie+tYUBdiR2GllrVpR4gqPCOHoI8HmH/8dWgUqFScRk9BFpR2/HetV9CrURsdjT4Za0mbGaJOssag==" + }, + "@zag-js/hover-card": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/hover-card/-/hover-card-1.26.1.tgz", + "integrity": "sha512-Qmd6FY51JsUAxLFQEkbeNCsvzLRL2YX75EupFqNdBPL509yEJkFa1ASry6usgUZgpgMlNP9PrjFIwywyDa8gFw==", + "requires": { + "@zag-js/anatomy": "1.26.1", + "@zag-js/core": "1.26.1", + "@zag-js/dismissable": "1.26.1", + "@zag-js/dom-query": "1.26.1", + "@zag-js/popper": "1.26.1", + "@zag-js/types": "1.26.1", + "@zag-js/utils": "1.26.1" + }, + "dependencies": { + "@zag-js/dom-query": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/dom-query/-/dom-query-1.26.1.tgz", + "integrity": "sha512-AcdNV+Zn+Mrz6jq6IpgBJmiooFCuGBzhbbjjU6Drh3TfNDL7q2YXinrZydoD4EGDaFOQ1h71iChTtCZSHw/yNw==", + "requires": { + "@zag-js/types": "1.26.1" + } + } + } + }, + "@zag-js/i18n-utils": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/i18n-utils/-/i18n-utils-1.26.1.tgz", + "integrity": "sha512-bvzfdsiY4taqZA/QfgqofsynlJGoJGmtmV5zKcVdyTJRVQz04vSEIJ+bzEDpiqK0FtrOI2mOq3Ixmv8fKNRxlQ==", + "requires": { + "@zag-js/dom-query": "1.26.1" + }, + "dependencies": { + "@zag-js/dom-query": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/dom-query/-/dom-query-1.26.1.tgz", + "integrity": "sha512-AcdNV+Zn+Mrz6jq6IpgBJmiooFCuGBzhbbjjU6Drh3TfNDL7q2YXinrZydoD4EGDaFOQ1h71iChTtCZSHw/yNw==", + "requires": { + "@zag-js/types": "1.26.1" + } + } + } + }, + "@zag-js/interact-outside": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/interact-outside/-/interact-outside-1.26.1.tgz", + "integrity": "sha512-Kxyg8AuJq4WssVbFyCtY14O611DU7UEiKgDwiNBXdQ5V+XXd4BBBwrA5QK9pk/qjgxZawKOalwjcYsyWhFbwLQ==", + "requires": { + "@zag-js/dom-query": "1.26.1", + "@zag-js/utils": "1.26.1" + }, + "dependencies": { + "@zag-js/dom-query": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/dom-query/-/dom-query-1.26.1.tgz", + "integrity": "sha512-AcdNV+Zn+Mrz6jq6IpgBJmiooFCuGBzhbbjjU6Drh3TfNDL7q2YXinrZydoD4EGDaFOQ1h71iChTtCZSHw/yNw==", + "requires": { + "@zag-js/types": "1.26.1" + } + } + } + }, + "@zag-js/json-tree-utils": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/json-tree-utils/-/json-tree-utils-1.26.1.tgz", + "integrity": "sha512-E3IDz6DgSZPJ6rKWoOARqicF9PMK5avUoqcDsQTsAM/z1SqQT1/++dzYpJ8r9rBej5n5rH1mhr+e5PmbkI5b5A==" + }, + "@zag-js/listbox": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/listbox/-/listbox-1.26.1.tgz", + "integrity": "sha512-Xqeb/PO4Yrj87RwP/X357ilLmrEgBIqHy/1lYnVwEVQI8l2TvElRe95oAXGORnqWRM1H0dtoNH6EjN+nm2g1WA==", + "requires": { + "@zag-js/anatomy": "1.26.1", + "@zag-js/collection": "1.26.1", + "@zag-js/core": "1.26.1", + "@zag-js/dom-query": "1.26.1", + "@zag-js/focus-visible": "1.26.1", + "@zag-js/types": "1.26.1", + "@zag-js/utils": "1.26.1" + }, + "dependencies": { + "@zag-js/dom-query": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/dom-query/-/dom-query-1.26.1.tgz", + "integrity": "sha512-AcdNV+Zn+Mrz6jq6IpgBJmiooFCuGBzhbbjjU6Drh3TfNDL7q2YXinrZydoD4EGDaFOQ1h71iChTtCZSHw/yNw==", + "requires": { + "@zag-js/types": "1.26.1" + } + }, + "@zag-js/focus-visible": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/focus-visible/-/focus-visible-1.26.1.tgz", + "integrity": "sha512-Hq57XwQWS0a5rjiZcgWhC8ca1e75VoqVLaSGn8XM29lv13423MPRtJ6b/zkEx5G4yZmddgA+wFh2/U2KPztcYA==", + "requires": { + "@zag-js/dom-query": "1.26.1" + } + } + } + }, + "@zag-js/live-region": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/live-region/-/live-region-1.26.1.tgz", + "integrity": "sha512-Z3c2/ssr5ERIVr0sLchua0e/JrPlbIN3suLZh4oYYPcxpKRjhwZQbN66xa5jm0LfIlW/Z21Obrb8X91GFwXR6w==" + }, + "@zag-js/menu": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/menu/-/menu-1.26.1.tgz", + "integrity": "sha512-ElFlY9yqea14XUkRXL1WA3t3SGDh+4jBMXZLIG8GY14GOzoTvruATBg1+Sbd5VVpggAbrUui33F/umWgwh6X4w==", + "requires": { + "@zag-js/anatomy": "1.26.1", + "@zag-js/core": "1.26.1", + "@zag-js/dismissable": "1.26.1", + "@zag-js/dom-query": "1.26.1", + "@zag-js/popper": "1.26.1", + "@zag-js/rect-utils": "1.26.1", + "@zag-js/types": "1.26.1", + "@zag-js/utils": "1.26.1" + }, + "dependencies": { + "@zag-js/dom-query": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/dom-query/-/dom-query-1.26.1.tgz", + "integrity": "sha512-AcdNV+Zn+Mrz6jq6IpgBJmiooFCuGBzhbbjjU6Drh3TfNDL7q2YXinrZydoD4EGDaFOQ1h71iChTtCZSHw/yNw==", + "requires": { + "@zag-js/types": "1.26.1" + } + } + } + }, + "@zag-js/number-input": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/number-input/-/number-input-1.26.1.tgz", + "integrity": "sha512-T3QnhDqKjUNn3eIQvwfOk2AEmJo1q6CrvKl71BfqO9PLOo930RfSBg0SWU16dv6kqGrH5bleRigPEwR6gLkDZQ==", + "requires": { + "@internationalized/number": "3.6.5", + "@zag-js/anatomy": "1.26.1", + "@zag-js/core": "1.26.1", + "@zag-js/dom-query": "1.26.1", + "@zag-js/types": "1.26.1", + "@zag-js/utils": "1.26.1" + }, + "dependencies": { + "@zag-js/dom-query": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/dom-query/-/dom-query-1.26.1.tgz", + "integrity": "sha512-AcdNV+Zn+Mrz6jq6IpgBJmiooFCuGBzhbbjjU6Drh3TfNDL7q2YXinrZydoD4EGDaFOQ1h71iChTtCZSHw/yNw==", + "requires": { + "@zag-js/types": "1.26.1" + } + } + } + }, + "@zag-js/pagination": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/pagination/-/pagination-1.26.1.tgz", + "integrity": "sha512-tInkqiMj1+VoFPSN80Y7lS1ABfxTA+xgyjMyx3Y09dgHNhiOKMOYva3eTGIJaEd6OskUayc3TFKqvfT5R43KlQ==", + "requires": { + "@zag-js/anatomy": "1.26.1", + "@zag-js/core": "1.26.1", + "@zag-js/dom-query": "1.26.1", + "@zag-js/types": "1.26.1", + "@zag-js/utils": "1.26.1" + }, + "dependencies": { + "@zag-js/dom-query": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/dom-query/-/dom-query-1.26.1.tgz", + "integrity": "sha512-AcdNV+Zn+Mrz6jq6IpgBJmiooFCuGBzhbbjjU6Drh3TfNDL7q2YXinrZydoD4EGDaFOQ1h71iChTtCZSHw/yNw==", + "requires": { + "@zag-js/types": "1.26.1" + } + } + } + }, + "@zag-js/password-input": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/password-input/-/password-input-1.26.1.tgz", + "integrity": "sha512-XI5M7uHWEX3K4uKYE5sCwm4gOCup5XHAYRO63ELFOZSKKRhPGl1IN8DeLNweDOtBJuab4ZHT2wLKgSxs/BoJLg==", + "requires": { + "@zag-js/anatomy": "1.26.1", + "@zag-js/core": "1.26.1", + "@zag-js/dom-query": "1.26.1", + "@zag-js/types": "1.26.1", + "@zag-js/utils": "1.26.1" + }, + "dependencies": { + "@zag-js/dom-query": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/dom-query/-/dom-query-1.26.1.tgz", + "integrity": "sha512-AcdNV+Zn+Mrz6jq6IpgBJmiooFCuGBzhbbjjU6Drh3TfNDL7q2YXinrZydoD4EGDaFOQ1h71iChTtCZSHw/yNw==", + "requires": { + "@zag-js/types": "1.26.1" + } + } + } + }, + "@zag-js/pin-input": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/pin-input/-/pin-input-1.26.1.tgz", + "integrity": "sha512-M/vFnCVUQe13Thcz+bYmx3va8yD1kfCaNvMNBb7TzRVrEl/XF+nKdx1IjRi6HmCN/TkWOBCk0Lj/p/dqoALiGg==", + "requires": { + "@zag-js/anatomy": "1.26.1", + "@zag-js/core": "1.26.1", + "@zag-js/dom-query": "1.26.1", + "@zag-js/types": "1.26.1", + "@zag-js/utils": "1.26.1" + }, + "dependencies": { + "@zag-js/dom-query": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/dom-query/-/dom-query-1.26.1.tgz", + "integrity": "sha512-AcdNV+Zn+Mrz6jq6IpgBJmiooFCuGBzhbbjjU6Drh3TfNDL7q2YXinrZydoD4EGDaFOQ1h71iChTtCZSHw/yNw==", + "requires": { + "@zag-js/types": "1.26.1" + } + } + } + }, + "@zag-js/popover": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/popover/-/popover-1.26.1.tgz", + "integrity": "sha512-nwZfOa7W8NW7npZXqw898nGIBJ7/sk89jglO9Ealqgay5ypOSGBg4v3Afc188Q9c4jFLLqNLQ/LzUhox+h7oLA==", + "requires": { + "@zag-js/anatomy": "1.26.1", + "@zag-js/aria-hidden": "1.26.1", + "@zag-js/core": "1.26.1", + "@zag-js/dismissable": "1.26.1", + "@zag-js/dom-query": "1.26.1", + "@zag-js/focus-trap": "1.26.1", + "@zag-js/popper": "1.26.1", + "@zag-js/remove-scroll": "1.26.1", + "@zag-js/types": "1.26.1", + "@zag-js/utils": "1.26.1" + }, + "dependencies": { + "@zag-js/dom-query": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/dom-query/-/dom-query-1.26.1.tgz", + "integrity": "sha512-AcdNV+Zn+Mrz6jq6IpgBJmiooFCuGBzhbbjjU6Drh3TfNDL7q2YXinrZydoD4EGDaFOQ1h71iChTtCZSHw/yNw==", + "requires": { + "@zag-js/types": "1.26.1" + } + } + } + }, + "@zag-js/popper": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/popper/-/popper-1.26.1.tgz", + "integrity": "sha512-wY/YzdXT97gIw8wpPDyU+qf1nlnf6wYwVs3SGDCd6cbgU7iy2jo3Pn6bMJ2OrsFrLZqFVZp6lF2zQtBTNJMZtg==", + "requires": { + "@floating-ui/dom": "1.7.4", + "@zag-js/dom-query": "1.26.1", + "@zag-js/utils": "1.26.1" + }, + "dependencies": { + "@zag-js/dom-query": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/dom-query/-/dom-query-1.26.1.tgz", + "integrity": "sha512-AcdNV+Zn+Mrz6jq6IpgBJmiooFCuGBzhbbjjU6Drh3TfNDL7q2YXinrZydoD4EGDaFOQ1h71iChTtCZSHw/yNw==", + "requires": { + "@zag-js/types": "1.26.1" + } + } + } + }, + "@zag-js/presence": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/presence/-/presence-1.26.1.tgz", + "integrity": "sha512-qcbdmwVQdrc4nbHsGTTRBT9UI6lJTmT2ZVv+QQW1ZeegiIEEg/IQAuMHVaPcGGnS2Kec2OJAA174+Br8rZCKtQ==", + "requires": { + "@zag-js/core": "1.26.1", + "@zag-js/dom-query": "1.26.1", + "@zag-js/types": "1.26.1" + }, + "dependencies": { + "@zag-js/dom-query": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/dom-query/-/dom-query-1.26.1.tgz", + "integrity": "sha512-AcdNV+Zn+Mrz6jq6IpgBJmiooFCuGBzhbbjjU6Drh3TfNDL7q2YXinrZydoD4EGDaFOQ1h71iChTtCZSHw/yNw==", + "requires": { + "@zag-js/types": "1.26.1" + } + } + } + }, + "@zag-js/progress": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/progress/-/progress-1.26.1.tgz", + "integrity": "sha512-NEmLImi7ZvsTQ1B+AAZARsl3IwvdS4+qv9Wpgf+RIjJVCa0CbE3N7XS+sHuI5X4nWAKbP6Kpc3VKPwZ/9dZ7jQ==", + "requires": { + "@zag-js/anatomy": "1.26.1", + "@zag-js/core": "1.26.1", + "@zag-js/dom-query": "1.26.1", + "@zag-js/types": "1.26.1", + "@zag-js/utils": "1.26.1" + }, + "dependencies": { + "@zag-js/dom-query": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/dom-query/-/dom-query-1.26.1.tgz", + "integrity": "sha512-AcdNV+Zn+Mrz6jq6IpgBJmiooFCuGBzhbbjjU6Drh3TfNDL7q2YXinrZydoD4EGDaFOQ1h71iChTtCZSHw/yNw==", + "requires": { + "@zag-js/types": "1.26.1" + } + } + } + }, + "@zag-js/qr-code": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/qr-code/-/qr-code-1.26.1.tgz", + "integrity": "sha512-UdFzV65pSVkgzdX6k/zbGwk35GDN3b/WmHVWmOaDgUE0ySyx+Mty97Ho+qomoNVRIoKQ0z5Oshz0qyrEvX131Q==", + "requires": { + "@zag-js/anatomy": "1.26.1", + "@zag-js/core": "1.26.1", + "@zag-js/dom-query": "1.26.1", + "@zag-js/types": "1.26.1", + "@zag-js/utils": "1.26.1", + "proxy-memoize": "3.0.1", + "uqr": "0.1.2" + }, + "dependencies": { + "@zag-js/dom-query": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/dom-query/-/dom-query-1.26.1.tgz", + "integrity": "sha512-AcdNV+Zn+Mrz6jq6IpgBJmiooFCuGBzhbbjjU6Drh3TfNDL7q2YXinrZydoD4EGDaFOQ1h71iChTtCZSHw/yNw==", + "requires": { + "@zag-js/types": "1.26.1" + } + } + } + }, + "@zag-js/radio-group": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/radio-group/-/radio-group-1.26.1.tgz", + "integrity": "sha512-85BA4b4h6Dq3Qh9VhlRM49gJUk0NGnEqmd86LctNLXMTMeJQ9i40LMeRVlKJqeDDcXMPQDKsINCfATegq2mj2Q==", + "requires": { + "@zag-js/anatomy": "1.26.1", + "@zag-js/core": "1.26.1", + "@zag-js/dom-query": "1.26.1", + "@zag-js/focus-visible": "1.26.1", + "@zag-js/types": "1.26.1", + "@zag-js/utils": "1.26.1" + }, + "dependencies": { + "@zag-js/dom-query": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/dom-query/-/dom-query-1.26.1.tgz", + "integrity": "sha512-AcdNV+Zn+Mrz6jq6IpgBJmiooFCuGBzhbbjjU6Drh3TfNDL7q2YXinrZydoD4EGDaFOQ1h71iChTtCZSHw/yNw==", + "requires": { + "@zag-js/types": "1.26.1" + } + }, + "@zag-js/focus-visible": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/focus-visible/-/focus-visible-1.26.1.tgz", + "integrity": "sha512-Hq57XwQWS0a5rjiZcgWhC8ca1e75VoqVLaSGn8XM29lv13423MPRtJ6b/zkEx5G4yZmddgA+wFh2/U2KPztcYA==", + "requires": { + "@zag-js/dom-query": "1.26.1" + } + } + } + }, + "@zag-js/rating-group": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/rating-group/-/rating-group-1.26.1.tgz", + "integrity": "sha512-bhwqi7c9GtMC+ooO/BiP8NNY1RiMVYhkj6b18CCtkJHrkl+yOB3liqqE5sb7fuVe1A37S7JL+TmAySsRQLkC2g==", + "requires": { + "@zag-js/anatomy": "1.26.1", + "@zag-js/core": "1.26.1", + "@zag-js/dom-query": "1.26.1", + "@zag-js/types": "1.26.1", + "@zag-js/utils": "1.26.1" + }, + "dependencies": { + "@zag-js/dom-query": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/dom-query/-/dom-query-1.26.1.tgz", + "integrity": "sha512-AcdNV+Zn+Mrz6jq6IpgBJmiooFCuGBzhbbjjU6Drh3TfNDL7q2YXinrZydoD4EGDaFOQ1h71iChTtCZSHw/yNw==", + "requires": { + "@zag-js/types": "1.26.1" + } + } + } + }, + "@zag-js/react": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/react/-/react-1.26.1.tgz", + "integrity": "sha512-O9Egcylyqfw1HNGBfBFtzV5MaEvlWCjbsmdPBKbN0oPOQhIGmjPSFgU+Zvyq3IwW4fHcyUJOVgjZy2EVwMDNfA==", + "requires": { + "@zag-js/core": "1.26.1", + "@zag-js/store": "1.26.1", + "@zag-js/types": "1.26.1", + "@zag-js/utils": "1.26.1" + } + }, + "@zag-js/rect-utils": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/rect-utils/-/rect-utils-1.26.1.tgz", + "integrity": "sha512-ZvO4kyXXfSPrFmVKgHDTSlTagioU5KNE2VhN6tN/uJYVh2M1us2DBIWtZLGZeGSpdcCuHEpThUp1eES7ZVN8ng==" + }, + "@zag-js/remove-scroll": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/remove-scroll/-/remove-scroll-1.26.1.tgz", + "integrity": "sha512-m1wVmykWfpvmp3Q3jLArCvojR8SM4/DTH6I5KIZAwgIp9QiI16K83ZTSS7TiVkhwt3SgH72F9oAaJT/HA9ZHnQ==", + "requires": { + "@zag-js/dom-query": "1.26.1" + }, + "dependencies": { + "@zag-js/dom-query": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/dom-query/-/dom-query-1.26.1.tgz", + "integrity": "sha512-AcdNV+Zn+Mrz6jq6IpgBJmiooFCuGBzhbbjjU6Drh3TfNDL7q2YXinrZydoD4EGDaFOQ1h71iChTtCZSHw/yNw==", + "requires": { + "@zag-js/types": "1.26.1" + } + } + } + }, + "@zag-js/scroll-area": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/scroll-area/-/scroll-area-1.26.1.tgz", + "integrity": "sha512-+QzuY/zYzl0ZH5Xf7tLg2nTKt8h3B55lu8yvg7WavqT2nLTMcocK6Vr1r7b+9FpGyt3nMYuZ6iUUU1Rdkk2vwg==", + "requires": { + "@zag-js/anatomy": "1.26.1", + "@zag-js/core": "1.26.1", + "@zag-js/dom-query": "1.26.1", + "@zag-js/types": "1.26.1", + "@zag-js/utils": "1.26.1" + }, + "dependencies": { + "@zag-js/dom-query": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/dom-query/-/dom-query-1.26.1.tgz", + "integrity": "sha512-AcdNV+Zn+Mrz6jq6IpgBJmiooFCuGBzhbbjjU6Drh3TfNDL7q2YXinrZydoD4EGDaFOQ1h71iChTtCZSHw/yNw==", + "requires": { + "@zag-js/types": "1.26.1" + } + } + } + }, + "@zag-js/scroll-snap": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/scroll-snap/-/scroll-snap-1.26.1.tgz", + "integrity": "sha512-fRRvXO0Yl9fMwrjN7zofVcFSQvHEKYRptq4W4lG2DXIKuUrmJwyTLKMREvLka6kRZ420hDfVlTj7PpSPp4OoGg==", + "requires": { + "@zag-js/dom-query": "1.26.1" + }, + "dependencies": { + "@zag-js/dom-query": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/dom-query/-/dom-query-1.26.1.tgz", + "integrity": "sha512-AcdNV+Zn+Mrz6jq6IpgBJmiooFCuGBzhbbjjU6Drh3TfNDL7q2YXinrZydoD4EGDaFOQ1h71iChTtCZSHw/yNw==", + "requires": { + "@zag-js/types": "1.26.1" + } + } + } + }, + "@zag-js/select": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/select/-/select-1.26.1.tgz", + "integrity": "sha512-posQbGiEXiLHoUeD2mGsuPPd/cSKat99h78krivAEJqFDijPSmX9l8A0o6sXXxWHFje50gqWxSH4p25IKBGQkA==", + "requires": { + "@zag-js/anatomy": "1.26.1", + "@zag-js/collection": "1.26.1", + "@zag-js/core": "1.26.1", + "@zag-js/dismissable": "1.26.1", + "@zag-js/dom-query": "1.26.1", + "@zag-js/popper": "1.26.1", + "@zag-js/types": "1.26.1", + "@zag-js/utils": "1.26.1" + }, + "dependencies": { + "@zag-js/dom-query": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/dom-query/-/dom-query-1.26.1.tgz", + "integrity": "sha512-AcdNV+Zn+Mrz6jq6IpgBJmiooFCuGBzhbbjjU6Drh3TfNDL7q2YXinrZydoD4EGDaFOQ1h71iChTtCZSHw/yNw==", + "requires": { + "@zag-js/types": "1.26.1" + } + } + } + }, + "@zag-js/signature-pad": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/signature-pad/-/signature-pad-1.26.1.tgz", + "integrity": "sha512-OCx0EJ9bZ3ePz5AmPxPvSq5nQWSVUQvI4vwRFHwZXULitIycwNm7avfCVSyvCCUj0f6IlgI6SiZDeU68lpNHnA==", + "requires": { + "@zag-js/anatomy": "1.26.1", + "@zag-js/core": "1.26.1", + "@zag-js/dom-query": "1.26.1", + "@zag-js/types": "1.26.1", + "@zag-js/utils": "1.26.1", + "perfect-freehand": "^1.2.2" + }, + "dependencies": { + "@zag-js/dom-query": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/dom-query/-/dom-query-1.26.1.tgz", + "integrity": "sha512-AcdNV+Zn+Mrz6jq6IpgBJmiooFCuGBzhbbjjU6Drh3TfNDL7q2YXinrZydoD4EGDaFOQ1h71iChTtCZSHw/yNw==", + "requires": { + "@zag-js/types": "1.26.1" + } + }, + "perfect-freehand": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/perfect-freehand/-/perfect-freehand-1.2.2.tgz", + "integrity": "sha512-eh31l019WICQ03pkF3FSzHxB8n07ItqIQ++G5UV8JX0zVOXzgTGCqnRR0jJ2h9U8/2uW4W4mtGJELt9kEV0CFQ==" + } + } + }, + "@zag-js/slider": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/slider/-/slider-1.26.1.tgz", + "integrity": "sha512-iHFeugMk9pNZLoijEYeV49h7lLC+twxkpeiTc5KjFgKQB9n8a+H22fIVkl+B+dmIPcfr8tDpri1FfN4hh9kxpA==", + "requires": { + "@zag-js/anatomy": "1.26.1", + "@zag-js/core": "1.26.1", + "@zag-js/dom-query": "1.26.1", + "@zag-js/types": "1.26.1", + "@zag-js/utils": "1.26.1" + }, + "dependencies": { + "@zag-js/dom-query": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/dom-query/-/dom-query-1.26.1.tgz", + "integrity": "sha512-AcdNV+Zn+Mrz6jq6IpgBJmiooFCuGBzhbbjjU6Drh3TfNDL7q2YXinrZydoD4EGDaFOQ1h71iChTtCZSHw/yNw==", + "requires": { + "@zag-js/types": "1.26.1" + } + } + } + }, + "@zag-js/splitter": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/splitter/-/splitter-1.26.1.tgz", + "integrity": "sha512-QMwNOrBQ2FQU82aPkHtnfaHqI9wzJKJj2ZU0CHVRBgK9V9D77VJvtufFU0qP+6x/p5Ba26i2y9AGv2jpDGBs6w==", + "requires": { + "@zag-js/anatomy": "1.26.1", + "@zag-js/core": "1.26.1", + "@zag-js/dom-query": "1.26.1", + "@zag-js/types": "1.26.1", + "@zag-js/utils": "1.26.1" + }, + "dependencies": { + "@zag-js/dom-query": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/dom-query/-/dom-query-1.26.1.tgz", + "integrity": "sha512-AcdNV+Zn+Mrz6jq6IpgBJmiooFCuGBzhbbjjU6Drh3TfNDL7q2YXinrZydoD4EGDaFOQ1h71iChTtCZSHw/yNw==", + "requires": { + "@zag-js/types": "1.26.1" + } + } + } + }, + "@zag-js/steps": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/steps/-/steps-1.26.1.tgz", + "integrity": "sha512-EUVpGOdOd/bjG1PiJ0lRZmfdPt7YcqL3y87mS5HZ4ZSyPp8piUOMDy7ZhZvweJB3DScF7FMfmV7ygZTNcq4dLQ==", + "requires": { + "@zag-js/anatomy": "1.26.1", + "@zag-js/core": "1.26.1", + "@zag-js/dom-query": "1.26.1", + "@zag-js/types": "1.26.1", + "@zag-js/utils": "1.26.1" + }, + "dependencies": { + "@zag-js/dom-query": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/dom-query/-/dom-query-1.26.1.tgz", + "integrity": "sha512-AcdNV+Zn+Mrz6jq6IpgBJmiooFCuGBzhbbjjU6Drh3TfNDL7q2YXinrZydoD4EGDaFOQ1h71iChTtCZSHw/yNw==", + "requires": { + "@zag-js/types": "1.26.1" + } + } + } + }, + "@zag-js/store": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/store/-/store-1.26.1.tgz", + "integrity": "sha512-0473Z38EVnl6RcjXecevzfxYbzZWEcCqsx+Q3Eti+5LKvSnl05SomiF5dYDd8qPQ167iWAQWhBYSTvasfjKa4w==", + "requires": { + "proxy-compare": "3.0.1" + } + }, + "@zag-js/switch": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/switch/-/switch-1.26.1.tgz", + "integrity": "sha512-OfOF2BYBtEeoI4RU0eYTFH3QAORNQBuVtDrsERJuWldLk9p1sukETXooTFxqaQuLRx98J3Y7duryjvTvNyA8nA==", + "requires": { + "@zag-js/anatomy": "1.26.1", + "@zag-js/core": "1.26.1", + "@zag-js/dom-query": "1.26.1", + "@zag-js/focus-visible": "1.26.1", + "@zag-js/types": "1.26.1", + "@zag-js/utils": "1.26.1" + }, + "dependencies": { + "@zag-js/dom-query": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/dom-query/-/dom-query-1.26.1.tgz", + "integrity": "sha512-AcdNV+Zn+Mrz6jq6IpgBJmiooFCuGBzhbbjjU6Drh3TfNDL7q2YXinrZydoD4EGDaFOQ1h71iChTtCZSHw/yNw==", + "requires": { + "@zag-js/types": "1.26.1" + } + }, + "@zag-js/focus-visible": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/focus-visible/-/focus-visible-1.26.1.tgz", + "integrity": "sha512-Hq57XwQWS0a5rjiZcgWhC8ca1e75VoqVLaSGn8XM29lv13423MPRtJ6b/zkEx5G4yZmddgA+wFh2/U2KPztcYA==", + "requires": { + "@zag-js/dom-query": "1.26.1" + } + } + } + }, + "@zag-js/tabs": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/tabs/-/tabs-1.26.1.tgz", + "integrity": "sha512-RI3Od0H6guYfflmCmpKO0hrh1FOjE/OduEEiD1XDn5foeD0uV/qhS8Wdh8R3ikgcaVjb5XrSOrCZXkuc732g8w==", + "requires": { + "@zag-js/anatomy": "1.26.1", + "@zag-js/core": "1.26.1", + "@zag-js/dom-query": "1.26.1", + "@zag-js/types": "1.26.1", + "@zag-js/utils": "1.26.1" + }, + "dependencies": { + "@zag-js/dom-query": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/dom-query/-/dom-query-1.26.1.tgz", + "integrity": "sha512-AcdNV+Zn+Mrz6jq6IpgBJmiooFCuGBzhbbjjU6Drh3TfNDL7q2YXinrZydoD4EGDaFOQ1h71iChTtCZSHw/yNw==", + "requires": { + "@zag-js/types": "1.26.1" + } + } + } + }, + "@zag-js/tags-input": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/tags-input/-/tags-input-1.26.1.tgz", + "integrity": "sha512-Gjjsq7CQolog1mEjU07hwnTPXQ9jjln35X52EjlsjOuohkzT0TFRDj5Nuf8fqu82k2OcSx8IX09wrdGgxMKklA==", + "requires": { + "@zag-js/anatomy": "1.26.1", + "@zag-js/auto-resize": "1.26.1", + "@zag-js/core": "1.26.1", + "@zag-js/dom-query": "1.26.1", + "@zag-js/interact-outside": "1.26.1", + "@zag-js/live-region": "1.26.1", + "@zag-js/types": "1.26.1", + "@zag-js/utils": "1.26.1" + }, + "dependencies": { + "@zag-js/dom-query": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/dom-query/-/dom-query-1.26.1.tgz", + "integrity": "sha512-AcdNV+Zn+Mrz6jq6IpgBJmiooFCuGBzhbbjjU6Drh3TfNDL7q2YXinrZydoD4EGDaFOQ1h71iChTtCZSHw/yNw==", + "requires": { + "@zag-js/types": "1.26.1" + } + } + } + }, + "@zag-js/timer": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/timer/-/timer-1.26.1.tgz", + "integrity": "sha512-+Xi7wtxCD9kjdHNeeNpCENMezWh2ZVO6KfRoLx31PTz1Dm6h457pofsAO0u8LZEpbI3IypTiTYFeaJn3qJtVrw==", + "requires": { + "@zag-js/anatomy": "1.26.1", + "@zag-js/core": "1.26.1", + "@zag-js/dom-query": "1.26.1", + "@zag-js/types": "1.26.1", + "@zag-js/utils": "1.26.1" + }, + "dependencies": { + "@zag-js/dom-query": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/dom-query/-/dom-query-1.26.1.tgz", + "integrity": "sha512-AcdNV+Zn+Mrz6jq6IpgBJmiooFCuGBzhbbjjU6Drh3TfNDL7q2YXinrZydoD4EGDaFOQ1h71iChTtCZSHw/yNw==", + "requires": { + "@zag-js/types": "1.26.1" + } + } + } + }, + "@zag-js/toast": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/toast/-/toast-1.26.1.tgz", + "integrity": "sha512-TLcN9Bd17fI0069VOV7Ug1kGk5feUK38YKJys8/1cujG+Cu4d5iUif/GUJDrRjcI/P6dPVTzrZ9ag+pCzxkWOA==", + "requires": { + "@zag-js/anatomy": "1.26.1", + "@zag-js/core": "1.26.1", + "@zag-js/dismissable": "1.26.1", + "@zag-js/dom-query": "1.26.1", + "@zag-js/types": "1.26.1", + "@zag-js/utils": "1.26.1" + }, + "dependencies": { + "@zag-js/dom-query": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/dom-query/-/dom-query-1.26.1.tgz", + "integrity": "sha512-AcdNV+Zn+Mrz6jq6IpgBJmiooFCuGBzhbbjjU6Drh3TfNDL7q2YXinrZydoD4EGDaFOQ1h71iChTtCZSHw/yNw==", + "requires": { + "@zag-js/types": "1.26.1" + } + } + } + }, + "@zag-js/toggle": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/toggle/-/toggle-1.26.1.tgz", + "integrity": "sha512-MrwXORUWpe5adyK9ieXt7KsxsBwdaFwUn42IoPLYnw61RS92qd4DPttxENXTpdL1RpqpO/G0piIzeAm7tVB/Yw==", + "requires": { + "@zag-js/anatomy": "1.26.1", + "@zag-js/core": "1.26.1", + "@zag-js/dom-query": "1.26.1", + "@zag-js/types": "1.26.1", + "@zag-js/utils": "1.26.1" + }, + "dependencies": { + "@zag-js/dom-query": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/dom-query/-/dom-query-1.26.1.tgz", + "integrity": "sha512-AcdNV+Zn+Mrz6jq6IpgBJmiooFCuGBzhbbjjU6Drh3TfNDL7q2YXinrZydoD4EGDaFOQ1h71iChTtCZSHw/yNw==", + "requires": { + "@zag-js/types": "1.26.1" + } + } + } + }, + "@zag-js/toggle-group": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/toggle-group/-/toggle-group-1.26.1.tgz", + "integrity": "sha512-0YXWmK59E+PPxpxj/YYbRY/gP1rVRnJ/hR57XNQykLrl+uYWy16lM3CD9tke3mmLwevdMpC3JwpdPAF9AGVBVw==", + "requires": { + "@zag-js/anatomy": "1.26.1", + "@zag-js/core": "1.26.1", + "@zag-js/dom-query": "1.26.1", + "@zag-js/types": "1.26.1", + "@zag-js/utils": "1.26.1" + }, + "dependencies": { + "@zag-js/dom-query": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/dom-query/-/dom-query-1.26.1.tgz", + "integrity": "sha512-AcdNV+Zn+Mrz6jq6IpgBJmiooFCuGBzhbbjjU6Drh3TfNDL7q2YXinrZydoD4EGDaFOQ1h71iChTtCZSHw/yNw==", + "requires": { + "@zag-js/types": "1.26.1" + } + } + } + }, + "@zag-js/tooltip": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/tooltip/-/tooltip-1.26.1.tgz", + "integrity": "sha512-ezBF4DZYo7E+AjVvpOoXnoPwhSnbS3y6Fp9BMvzQRSn5qElZbdQOEZSI9R6MZBTfEo6syXE68MYyCMzKFpkQFg==", + "requires": { + "@zag-js/anatomy": "1.26.1", + "@zag-js/core": "1.26.1", + "@zag-js/dom-query": "1.26.1", + "@zag-js/focus-visible": "1.26.1", + "@zag-js/popper": "1.26.1", + "@zag-js/types": "1.26.1", + "@zag-js/utils": "1.26.1" + }, + "dependencies": { + "@zag-js/dom-query": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/dom-query/-/dom-query-1.26.1.tgz", + "integrity": "sha512-AcdNV+Zn+Mrz6jq6IpgBJmiooFCuGBzhbbjjU6Drh3TfNDL7q2YXinrZydoD4EGDaFOQ1h71iChTtCZSHw/yNw==", + "requires": { + "@zag-js/types": "1.26.1" + } + }, + "@zag-js/focus-visible": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/focus-visible/-/focus-visible-1.26.1.tgz", + "integrity": "sha512-Hq57XwQWS0a5rjiZcgWhC8ca1e75VoqVLaSGn8XM29lv13423MPRtJ6b/zkEx5G4yZmddgA+wFh2/U2KPztcYA==", + "requires": { + "@zag-js/dom-query": "1.26.1" + } + } + } + }, + "@zag-js/tour": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/tour/-/tour-1.26.1.tgz", + "integrity": "sha512-dNG8+fH6sVUjYsGQ8fO5gHvIZ1temhR3fe4H3lbSjqeb+8FHNXfkM1PbgNkUTmBar4JdEdWU60E/26cChDkxGQ==", + "requires": { + "@zag-js/anatomy": "1.26.1", + "@zag-js/core": "1.26.1", + "@zag-js/dismissable": "1.26.1", + "@zag-js/dom-query": "1.26.1", + "@zag-js/focus-trap": "1.26.1", + "@zag-js/interact-outside": "1.26.1", + "@zag-js/popper": "1.26.1", + "@zag-js/types": "1.26.1", + "@zag-js/utils": "1.26.1" + }, + "dependencies": { + "@zag-js/dom-query": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/dom-query/-/dom-query-1.26.1.tgz", + "integrity": "sha512-AcdNV+Zn+Mrz6jq6IpgBJmiooFCuGBzhbbjjU6Drh3TfNDL7q2YXinrZydoD4EGDaFOQ1h71iChTtCZSHw/yNw==", + "requires": { + "@zag-js/types": "1.26.1" + } + } + } + }, + "@zag-js/tree-view": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/tree-view/-/tree-view-1.26.1.tgz", + "integrity": "sha512-t+vzJ8EsDO0hAAwYbwQmQqy2aoWRWdUHJKhYk9pU8+tEwU7shepX2kSP3xbxpsXcHih5TBAzSQhGcqXi8rfnlA==", + "requires": { + "@zag-js/anatomy": "1.26.1", + "@zag-js/collection": "1.26.1", + "@zag-js/core": "1.26.1", + "@zag-js/dom-query": "1.26.1", + "@zag-js/types": "1.26.1", + "@zag-js/utils": "1.26.1" + }, + "dependencies": { + "@zag-js/dom-query": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/dom-query/-/dom-query-1.26.1.tgz", + "integrity": "sha512-AcdNV+Zn+Mrz6jq6IpgBJmiooFCuGBzhbbjjU6Drh3TfNDL7q2YXinrZydoD4EGDaFOQ1h71iChTtCZSHw/yNw==", + "requires": { + "@zag-js/types": "1.26.1" + } + } + } + }, + "@zag-js/types": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/types/-/types-1.26.1.tgz", + "integrity": "sha512-MXRn8x0af7ZVJ50cxCoJU7hLZ3FmPnD53UQnjkYSmOcwr1eE0Wyt7bjGUP1Zv+6hPMTlyZ18XR2rnUkKAtr7/w==", + "requires": { + "csstype": "3.1.3" + } + }, + "@zag-js/utils": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@zag-js/utils/-/utils-1.26.1.tgz", + "integrity": "sha512-OeQia4UV/3osMdrhSa2NmhWvzlnlF1vxOBAdYP5R96ho0FpI15DvAaPQDvE94shzIMgzS4k0pqK5lZWK5FTmqw==" + }, "abab": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", @@ -67756,6 +70724,11 @@ "integrity": "sha512-AI2r85+4MquTw9ZYqabu4nMwy9Oftlfa/e/52t9IjtfG+mGBbTNdAoZ3RQKLHR6r0wQnwZnPIEh/Ya6XTWAKNA==", "optional": true }, + "lucide-react": { + "version": "0.503.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.503.0.tgz", + "integrity": "sha512-HGGkdlPWQ0vTF8jJ5TdIqhQXZi6uh3LnNgfZ8MHiuxFfX3RZeA79r2MW2tHAZKlAVfoNE8esm3p+O6VkIvpj6w==" + }, "lunr": { "version": "2.3.9", "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz", @@ -71806,6 +74779,19 @@ } } }, + "proxy-compare": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/proxy-compare/-/proxy-compare-3.0.1.tgz", + "integrity": "sha512-V9plBAt3qjMlS1+nC8771KNf6oJ12gExvaxnNzN/9yVRLdTv/lc+oJlnSzrdYDAvBfTStPCoiaCOTmTs0adv7Q==" + }, + "proxy-memoize": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/proxy-memoize/-/proxy-memoize-3.0.1.tgz", + "integrity": "sha512-VDdG/VYtOgdGkWJx7y0o7p+zArSf2383Isci8C+BP3YXgMYDoPd3cCBjw0JdWb6YBb9sFiOPbAADDVTPJnh+9g==", + "requires": { + "proxy-compare": "^3.0.0" + } + }, "prr": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", @@ -73892,6 +76878,21 @@ "integrity": "sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==", "dev": true }, + "shiki": { + "version": "3.13.0", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-3.13.0.tgz", + "integrity": "sha512-aZW4l8Og16CokuCLf8CF8kq+KK2yOygapU5m3+hoGw0Mdosc6fPitjM+ujYarppj5ZIKGyPDPP1vqmQhr+5/0g==", + "requires": { + "@shikijs/core": "3.13.0", + "@shikijs/engine-javascript": "3.13.0", + "@shikijs/engine-oniguruma": "3.13.0", + "@shikijs/langs": "3.13.0", + "@shikijs/themes": "3.13.0", + "@shikijs/types": "3.13.0", + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.4" + } + }, "side-channel": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", @@ -75186,9 +78187,9 @@ } }, "tslib": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", - "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" }, "tunnel-rat": { "version": "0.1.2", @@ -75837,6 +78838,11 @@ } } }, + "uqr": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/uqr/-/uqr-0.1.2.tgz", + "integrity": "sha512-MJu7ypHq6QasgF5YRTjqscSzQp/W11zoUk6kvmlH+fmWEs63Y0Eib13hYFwAzagRJcVY8WVnlV+eBDUGMJ5IbA==" + }, "uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", diff --git a/package.json b/package.json index f7c35d86f60..9102bd81d4f 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,8 @@ "license": "MIT", "private": true, "workspaces": [ - "packages/*" + "packages/*", + "examples/dev-*" ], "engines": { "npm": ">=8.2.3", diff --git a/packages/lexical-clipboard/package.json b/packages/lexical-clipboard/package.json index 6d323b42937..d46c3859d9d 100644 --- a/packages/lexical-clipboard/package.json +++ b/packages/lexical-clipboard/package.json @@ -13,6 +13,7 @@ "main": "LexicalClipboard.js", "types": "index.d.ts", "dependencies": { + "@lexical/extension": "0.37.0", "@lexical/html": "0.37.0", "@lexical/list": "0.37.0", "@lexical/selection": "0.37.0", diff --git a/packages/lexical-clipboard/src/clipboard.ts b/packages/lexical-clipboard/src/clipboard.ts index 3693b5bc08d..aae79fe26a5 100644 --- a/packages/lexical-clipboard/src/clipboard.ts +++ b/packages/lexical-clipboard/src/clipboard.ts @@ -6,6 +6,10 @@ * */ +import { + getExtensionDependencyFromEditor, + LexicalBuilder, +} from '@lexical/extension'; import {$generateHtmlFromNodes, $generateNodesFromDOM} from '@lexical/html'; import {$addNodeStyle, $sliceSelectedTextNodeContent} from '@lexical/selection'; import {objectKlassEquals} from '@lexical/utils'; @@ -25,12 +29,15 @@ import { BaseSelection, COMMAND_PRIORITY_CRITICAL, COPY_COMMAND, + defineExtension, getDOMSelection, isSelectionWithinEditor, LexicalEditor, LexicalNode, + safeCast, SELECTION_INSERT_CLIPBOARD_NODES_COMMAND, SerializedElementNode, + shallowMergeConfig, } from 'lexical'; import invariant from 'shared/invariant'; @@ -38,6 +45,7 @@ export interface LexicalClipboardData { 'text/html'?: string | undefined; 'application/x-lexical-editor'?: string | undefined; 'text/plain': string; + [mimeType: string]: string | undefined; } /** @@ -537,32 +545,6 @@ const clipboardDataFunctions = [ ['application/x-lexical-editor', $getLexicalContent], ] as const; -/** - * Serialize the content of the current selection to strings in - * text/plain, text/html, and application/x-lexical-editor (Lexical JSON) - * formats (as available). - * - * @param selection the selection to serialize (defaults to $getSelection()) - * @returns LexicalClipboardData - */ -export function $getClipboardDataFromSelection( - selection: BaseSelection | null = $getSelection(), -): LexicalClipboardData { - const clipboardData: LexicalClipboardData = { - 'text/plain': selection ? selection.getTextContent() : '', - }; - if (selection) { - const editor = $getEditor(); - for (const [mimeType, $editorFn] of clipboardDataFunctions) { - const v = $editorFn(editor, selection); - if (v !== null) { - clipboardData[mimeType] = v; - } - } - } - return clipboardData; -} - /** * Call setData on the given clipboardData for each MIME type present * in the given data (from {@link $getClipboardDataFromSelection}) @@ -586,3 +568,108 @@ export function setLexicalClipboardDataTransfer( } } } + +/** + * Serialize the content of the current selection to strings in + * text/plain, text/html, and application/x-lexical-editor (Lexical JSON) + * formats (as available). + * + * @param selection the selection to serialize (defaults to $getSelection()) + * @returns LexicalClipboardData + */ +export function $getClipboardDataFromSelection( + selection: BaseSelection | null = $getSelection(), +): LexicalClipboardData { + return $getClipboardDataWithConfigFromSelection( + $getExportConfig(), + selection, + ); +} + +export type ExportMimeTypeFunction = ( + selection: null | BaseSelection, + next: () => null | string, +) => null | string; + +export interface GetClipboardDataConfig { + $exportMimeType: ExportMimeTypeConfig; +} + +export type ExportMimeTypeConfig = Record< + keyof LexicalClipboardData | (string & {}), + ExportMimeTypeFunction[] +>; + +function $getExportConfig() { + const editor = $getEditor(); + const builder = LexicalBuilder.maybeFromEditor(editor); + if (builder && builder.hasExtensionByName(GetClipboardDataExtension.name)) { + return getExtensionDependencyFromEditor(editor, GetClipboardDataExtension) + .output; + } + return DEFAULT_EXPORT_MIME_TYPE; +} + +const DEFAULT_EXPORT_MIME_TYPE: ExportMimeTypeConfig = { + 'application/x-lexical-editor': [ + (sel, next) => (sel ? $getLexicalContent($getEditor(), sel) : next()), + ], + 'text/html': [ + (sel, next) => (sel ? $getHtmlContent($getEditor(), sel) : next()), + ], + 'text/plain': [(sel, next) => (sel ? sel.getTextContent() : next())], +}; + +function $getClipboardDataWithConfigFromSelection( + $exportMimeType: ExportMimeTypeConfig, + selection: null | BaseSelection, +): LexicalClipboardData { + const clipboardData: LexicalClipboardData = {'text/plain': ''}; + for (const [k, fns] of Object.entries($exportMimeType)) { + const v = callExportMimeTypeFunctionStack(fns, selection); + if (v !== null) { + clipboardData[k] = v; + } + } + return clipboardData; +} + +function callExportMimeTypeFunctionStack( + fns: ExportMimeTypeFunction[], + selection: null | BaseSelection, +) { + const callAt = (i: number): string | null => + fns[i] ? fns[i](selection, callAt.bind(null, i - 1)) : null; + return callAt(fns.length - 1); +} + +export function $exportMimeTypeFromSelection( + mimeType: keyof ExportMimeTypeConfig, + selection: null | BaseSelection = $getSelection(), +): string | null { + return callExportMimeTypeFunctionStack( + $getExportConfig()[mimeType] || [], + selection, + ); +} + +export const GetClipboardDataExtension = defineExtension({ + build(editor, config, state) { + return config.$exportMimeType; + }, + config: safeCast({ + $exportMimeType: DEFAULT_EXPORT_MIME_TYPE, + }), + mergeConfig(config, partial) { + const merged = shallowMergeConfig(config, partial); + if (partial.$exportMimeType) { + const $exportMimeType = {...config.$exportMimeType}; + for (const [k, v] of Object.entries(partial.$exportMimeType)) { + $exportMimeType[k] = [...$exportMimeType[k], ...v]; + } + merged.$exportMimeType = $exportMimeType; + } + return merged; + }, + name: '@lexical/clipboard/GetClipboardData', +}); diff --git a/packages/lexical-extension/src/config.ts b/packages/lexical-extension/src/config.ts index 28e45e684ea..e8df5c7e6b4 100644 --- a/packages/lexical-extension/src/config.ts +++ b/packages/lexical-extension/src/config.ts @@ -5,11 +5,12 @@ * LICENSE file in the root directory of this source tree. * */ -import type { - CreateEditorArgs, - InitialEditorConfig, - KlassConstructor, - LexicalNode, +import { + type CreateEditorArgs, + getStaticNodeConfig, + type InitialEditorConfig, + type KlassConstructor, + type LexicalNode, } from 'lexical'; export interface KnownTypesAndNodes { @@ -25,7 +26,9 @@ export interface KnownTypesAndNodes { * @param config The InitialEditorConfig (accessible from an extension's init) * @returns The known types and nodes as Sets */ -export function getKnownTypesAndNodes(config: InitialEditorConfig) { +export function getKnownTypesAndNodes( + config: Pick, +): KnownTypesAndNodes { const types: KnownTypesAndNodes['types'] = new Set(); const nodes: KnownTypesAndNodes['nodes'] = new Set(); for (const klassOrReplacement of getNodeConfig(config)) { @@ -33,6 +36,8 @@ export function getKnownTypesAndNodes(config: InitialEditorConfig) { typeof klassOrReplacement === 'function' ? klassOrReplacement : klassOrReplacement.replace; + // For the side-effect of filling in the static methods + void getStaticNodeConfig(klass); types.add(klass.getType()); nodes.add(klass); } @@ -40,7 +45,7 @@ export function getKnownTypesAndNodes(config: InitialEditorConfig) { } export function getNodeConfig( - config: InitialEditorConfig, + config: Pick, ): NonNullable { return ( (typeof config.nodes === 'function' ? config.nodes() : config.nodes) || [] diff --git a/packages/lexical-html/package.json b/packages/lexical-html/package.json index 6fe47c8ee71..4f3c021f29f 100644 --- a/packages/lexical-html/package.json +++ b/packages/lexical-html/package.json @@ -17,6 +17,7 @@ "directory": "packages/lexical-html" }, "dependencies": { + "@lexical/extension": "0.37.0", "@lexical/selection": "0.37.0", "@lexical/utils": "0.37.0", "lexical": "0.37.0" diff --git a/packages/lexical-html/src/$generateDOMFromNodes.ts b/packages/lexical-html/src/$generateDOMFromNodes.ts new file mode 100644 index 00000000000..df7a4a1c3ce --- /dev/null +++ b/packages/lexical-html/src/$generateDOMFromNodes.ts @@ -0,0 +1,175 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + */ +import {$sliceSelectedTextNodeContent} from '@lexical/selection'; +import { + $getEditor, + $getEditorDOMRenderConfig, + $getRoot, + $isElementNode, + $isTextNode, + type BaseSelection, + type EditorDOMRenderConfig, + isDocumentFragment, + isHTMLElement, + type LexicalEditor, + type LexicalNode, +} from 'lexical'; +import invariant from 'shared/invariant'; + +import {contextValue} from './ContextRecord'; +import { + $withRenderContext, + RenderContextExport, + RenderContextRoot, +} from './RenderContext'; + +export function $generateDOMFromNodes( + container: T, + selection: null | BaseSelection = null, + editor: LexicalEditor = $getEditor(), +): T { + return $withRenderContext( + [contextValue(RenderContextExport, true)], + editor, + )(() => { + const root = $getRoot(); + const domConfig = $getEditorDOMRenderConfig(editor); + + const parentElementAppend = container.append.bind(container); + for (const topLevelNode of root.getChildren()) { + $appendNodesToHTML( + editor, + topLevelNode, + parentElementAppend, + selection, + domConfig, + ); + } + return container; + }); +} + +export function $generateDOMFromRoot( + container: T, + root: LexicalNode = $getRoot(), +): T { + const editor = $getEditor(); + return $withRenderContext( + [ + contextValue(RenderContextExport, true), + contextValue(RenderContextRoot, true), + ], + editor, + )(() => { + const selection = null; + const domConfig = $getEditorDOMRenderConfig(editor); + const parentElementAppend = container.append.bind(container); + $appendNodesToHTML(editor, root, parentElementAppend, selection, domConfig); + return container; + }); +} +function $appendNodesToHTML( + editor: LexicalEditor, + currentNode: LexicalNode, + parentElementAppend: (element: Node) => void, + selection: BaseSelection | null = null, + domConfig: EditorDOMRenderConfig = $getEditorDOMRenderConfig(editor), +): boolean { + let shouldInclude = domConfig.$shouldInclude(currentNode, selection, editor); + const shouldExclude = domConfig.$shouldExclude( + currentNode, + selection, + editor, + ); + let target = currentNode; + + if (selection !== null && $isTextNode(currentNode)) { + target = $sliceSelectedTextNodeContent(selection, currentNode, 'clone'); + } + const exportProps = domConfig.$exportDOM(target, editor); + const {element, after, append, $getChildNodes} = exportProps; + + if (!element) { + return false; + } + + const fragment = document.createDocumentFragment(); + const children = $getChildNodes + ? $getChildNodes() + : $isElementNode(target) + ? target.getChildren() + : []; + + const fragmentAppend = fragment.append.bind(fragment); + for (const childNode of children) { + const shouldIncludeChild = $appendNodesToHTML( + editor, + childNode, + fragmentAppend, + selection, + domConfig, + ); + + if ( + !shouldInclude && + shouldIncludeChild && + domConfig.$extractWithChild( + currentNode, + childNode, + selection, + 'html', + editor, + ) + ) { + shouldInclude = true; + } + } + + if (shouldInclude && !shouldExclude) { + if (isHTMLElement(element) || isDocumentFragment(element)) { + if (append) { + append(fragment); + } else { + element.append(fragment); + } + } + parentElementAppend(element); + + if (after) { + const newElement = after.call(target, element); + if (newElement) { + if (isDocumentFragment(element)) { + element.replaceChildren(newElement); + } else { + element.replaceWith(newElement); + } + } + } + } else { + parentElementAppend(fragment); + } + + return shouldInclude; +} + +export function $generateHtmlFromNodes( + editor: LexicalEditor, + selection: BaseSelection | null = null, +): string { + if ( + typeof document === 'undefined' || + (typeof window === 'undefined' && typeof global.window === 'undefined') + ) { + invariant( + false, + 'To use $generateHtmlFromNodes in headless mode please initialize a headless browser implementation such as JSDom or use withDOM from @lexical/headless/dom before calling this function.', + ); + } + return $generateDOMFromNodes(document.createElement('div'), selection, editor) + .innerHTML; +} diff --git a/packages/lexical-html/src/$generateNodesFromDOM.ts b/packages/lexical-html/src/$generateNodesFromDOM.ts new file mode 100644 index 00000000000..cce30ed827c --- /dev/null +++ b/packages/lexical-html/src/$generateNodesFromDOM.ts @@ -0,0 +1,219 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + */ +import { + $createLineBreakNode, + $createParagraphNode, + $isBlockElementNode, + $isElementNode, + $isRootOrShadowRoot, + ArtificialNode__DO_NOT_USE, + type DOMChildConversion, + type ElementFormatType, + type ElementNode, + isBlockDomNode, + isDOMDocumentNode, + type LexicalEditor, + type LexicalNode, +} from 'lexical'; + +import {$unwrapArtificialNodes} from './$unwrapArtificialNodes'; +import {IGNORE_TAGS} from './constants'; +import {getConversionFunction} from './getConversionFunction'; +import {isDomNodeBetweenTwoInlineNodes} from './isDomNodeBetweenTwoInlineNodes'; + +function $wrapContinuousInlines( + domNode: Node, + nodes: Array, + $createWrapperFn: () => ElementNode, +): Array { + const textAlign = (domNode as HTMLElement).style + .textAlign as ElementFormatType; + const out: Array = []; + let continuousInlines: Array = []; + // wrap contiguous inline child nodes in para + for (let i = 0; i < nodes.length; i++) { + const node = nodes[i]; + if ($isBlockElementNode(node)) { + if (textAlign && !node.getFormat()) { + node.setFormat(textAlign); + } + out.push(node); + } else { + continuousInlines.push(node); + if ( + i === nodes.length - 1 || + (i < nodes.length - 1 && $isBlockElementNode(nodes[i + 1])) + ) { + const wrapper = $createWrapperFn(); + wrapper.setFormat(textAlign); + wrapper.append(...continuousInlines); + out.push(wrapper); + continuousInlines = []; + } + } + } + return out; +} + +/** + * How you parse your html string to get a document is left up to you. In the browser you can use the native + * DOMParser API to generate a document (see clipboard.ts), but to use in a headless environment you can use JSDom + * or an equivalent library and pass in the document here. + */ + +export function $generateNodesFromDOM( + editor: LexicalEditor, + dom: Document | ParentNode, +): Array { + const elements = isDOMDocumentNode(dom) + ? dom.body.childNodes + : dom.childNodes; + let lexicalNodes: Array = []; + const allArtificialNodes: Array = []; + for (const element of elements) { + if (!IGNORE_TAGS.has(element.nodeName)) { + const lexicalNode = $createNodesFromDOM( + element, + editor, + allArtificialNodes, + false, + ); + if (lexicalNode !== null) { + lexicalNodes = lexicalNodes.concat(lexicalNode); + } + } + } + $unwrapArtificialNodes(allArtificialNodes); + + return lexicalNodes; +} + +export function $createNodesFromDOM( + node: Node, + editor: LexicalEditor, + allArtificialNodes: Array, + hasBlockAncestorLexicalNode: boolean, + forChildMap: Map = new Map(), + parentLexicalNode?: LexicalNode | null | undefined, +): Array { + let lexicalNodes: Array = []; + + if (IGNORE_TAGS.has(node.nodeName)) { + return lexicalNodes; + } + + let currentLexicalNode = null; + const transformFunction = getConversionFunction(node, editor); + const transformOutput = transformFunction + ? transformFunction(node as HTMLElement) + : null; + let postTransform = null; + + if (transformOutput !== null) { + postTransform = transformOutput.after; + const transformNodes = transformOutput.node; + currentLexicalNode = Array.isArray(transformNodes) + ? transformNodes[transformNodes.length - 1] + : transformNodes; + + if (currentLexicalNode !== null) { + for (const [, forChildFunction] of forChildMap) { + currentLexicalNode = forChildFunction( + currentLexicalNode, + parentLexicalNode, + ); + + if (!currentLexicalNode) { + break; + } + } + + if (currentLexicalNode) { + lexicalNodes.push( + ...(Array.isArray(transformNodes) + ? transformNodes + : [currentLexicalNode]), + ); + } + } + + if (transformOutput.forChild != null) { + forChildMap.set(node.nodeName, transformOutput.forChild); + } + } + + // If the DOM node doesn't have a transformer, we don't know what + // to do with it but we still need to process any childNodes. + const children = node.childNodes; + let childLexicalNodes = []; + + const hasBlockAncestorLexicalNodeForChildren = + currentLexicalNode != null && $isRootOrShadowRoot(currentLexicalNode) + ? false + : (currentLexicalNode != null && + $isBlockElementNode(currentLexicalNode)) || + hasBlockAncestorLexicalNode; + + for (let i = 0; i < children.length; i++) { + childLexicalNodes.push( + ...$createNodesFromDOM( + children[i], + editor, + allArtificialNodes, + hasBlockAncestorLexicalNodeForChildren, + new Map(forChildMap), + currentLexicalNode, + ), + ); + } + + if (postTransform != null) { + childLexicalNodes = postTransform(childLexicalNodes); + } + + if (isBlockDomNode(node)) { + if (!hasBlockAncestorLexicalNodeForChildren) { + childLexicalNodes = $wrapContinuousInlines( + node, + childLexicalNodes, + $createParagraphNode, + ); + } else { + childLexicalNodes = $wrapContinuousInlines( + node, + childLexicalNodes, + () => { + const artificialNode = new ArtificialNode__DO_NOT_USE(); + allArtificialNodes.push(artificialNode); + return artificialNode; + }, + ); + } + } + + if (currentLexicalNode == null) { + if (childLexicalNodes.length > 0) { + // If it hasn't been converted to a LexicalNode, we hoist its children + // up to the same level as it. + lexicalNodes = lexicalNodes.concat(childLexicalNodes); + } else { + if (isBlockDomNode(node) && isDomNodeBetweenTwoInlineNodes(node)) { + // Empty block dom node that hasnt been converted, we replace it with a linebreak if its between inline nodes + lexicalNodes = lexicalNodes.concat($createLineBreakNode()); + } + } + } else { + if ($isElementNode(currentLexicalNode)) { + // If the current node is a ElementNode after conversion, + // we can append all the children to it. + currentLexicalNode.append(...childLexicalNodes); + } + } + + return lexicalNodes; +} diff --git a/packages/lexical-html/src/$unwrapArtificialNodes.ts b/packages/lexical-html/src/$unwrapArtificialNodes.ts new file mode 100644 index 00000000000..08c9d1f1488 --- /dev/null +++ b/packages/lexical-html/src/$unwrapArtificialNodes.ts @@ -0,0 +1,29 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + */ +import {$createLineBreakNode, ArtificialNode__DO_NOT_USE} from 'lexical'; + +export function $unwrapArtificialNodes( + allArtificialNodes: Array, +) { + // Replace artificial node with its children, inserting a linebreak + // between adjacent artificial nodes + for (const node of allArtificialNodes) { + if ( + node.getParent() && + node.getNextSibling() instanceof ArtificialNode__DO_NOT_USE + ) { + node.insertAfter($createLineBreakNode()); + } + } + for (const node of allArtificialNodes) { + const parent = node.getParent(); + if (parent) { + parent.splice(node.getIndexWithinParent(), 1, node.getChildren()); + } + } +} diff --git a/packages/lexical-html/src/$wrapContinuousInlinesInPlace.ts b/packages/lexical-html/src/$wrapContinuousInlinesInPlace.ts new file mode 100644 index 00000000000..4315ae44c8e --- /dev/null +++ b/packages/lexical-html/src/$wrapContinuousInlinesInPlace.ts @@ -0,0 +1,40 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + */ +import { + $isBlockElementNode, + type ElementFormatType, + type ElementNode, + type LexicalNode, +} from 'lexical'; + +export function $wrapContinuousInlinesInPlace( + domNode: Node, + nodes: LexicalNode[], + $createWrapperFn: () => ElementNode, +): void { + const textAlign = (domNode as HTMLElement).style + .textAlign as ElementFormatType; + // wrap contiguous inline child nodes in para + let j = 0; + for (let i = 0, wrapper: undefined | ElementNode; i < nodes.length; i++) { + const node = nodes[i]; + if ($isBlockElementNode(node)) { + if (textAlign && !node.getFormat()) { + node.setFormat(textAlign); + } + wrapper = undefined; + nodes[j++] = node; + } else { + if (!wrapper) { + nodes[j++] = wrapper = $createWrapperFn().setFormat(textAlign); + } + wrapper.append(node); + } + } + nodes.length = j; +} diff --git a/packages/lexical-html/src/ContextRecord.ts b/packages/lexical-html/src/ContextRecord.ts new file mode 100644 index 00000000000..e553ddf5826 --- /dev/null +++ b/packages/lexical-html/src/ContextRecord.ts @@ -0,0 +1,216 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + */ +import type { + AnyContextConfigPairOrUpdater, + AnyContextSymbol, + ContextConfig, + ContextConfigPair, + ContextConfigUpdater, + ContextRecord, +} from './types'; + +import {$getEditor, createState, type LexicalEditor} from 'lexical'; + +let activeContext: undefined | EditorContext; + +type WithContext = { + [K in Ctx]?: undefined | ContextRecord; +}; + +export type EditorContext = { + editor: LexicalEditor; +} & WithContext; + +export function getContextValue( + contextRecord: undefined | ContextRecord, + cfg: ContextConfig, +): V { + const {key} = cfg; + return contextRecord && key in contextRecord + ? (contextRecord[key] as V) + : cfg.defaultValue; +} + +export function popOwnContextValue( + contextRecord: ContextRecord, + cfg: ContextConfig, +): undefined | V { + const rval = getOwnContextValue(contextRecord, cfg); + delete contextRecord[cfg.key]; + return rval; +} + +export function getOwnContextValue( + contextRecord: ContextRecord, + cfg: ContextConfig, +): undefined | V { + const {key} = cfg; + return key in contextRecord ? (contextRecord[key] as V) : undefined; +} + +function getEditorContext(editor: LexicalEditor): undefined | EditorContext { + return activeContext && activeContext.editor === editor + ? activeContext + : undefined; +} + +export function getContextRecord( + sym: Ctx, + editor: LexicalEditor, +): undefined | ContextRecord { + const editorContext = getEditorContext(editor); + return editorContext && editorContext[sym]; +} + +function toPair( + contextRecord: undefined | ContextRecord, + pairOrUpdater: ContextConfigPair | ContextConfigUpdater, +): ContextConfigPair { + if ('cfg' in pairOrUpdater) { + const {cfg, updater} = pairOrUpdater; + return [cfg, updater(getContextValue(contextRecord, cfg))]; + } + return pairOrUpdater; +} + +export function contextFromPairs( + pairs: readonly AnyContextConfigPairOrUpdater[], + parent: undefined | ContextRecord, +): undefined | ContextRecord { + let rval = parent; + for (const pairOrUpdater of pairs) { + const [k, v] = toPair(rval, pairOrUpdater); + const key = k.key; + if (rval === parent && getContextValue(rval, k) === v) { + continue; + } + const ctx = rval || createChildContext(parent); + ctx[key] = v; + rval = ctx; + } + return rval; +} + +export function createChildContext( + parent: undefined | ContextRecord, +): ContextRecord { + return Object.create(parent || null); +} + +export function setContextValue( + contextRecord: ContextRecord, + cfg: ContextConfig, + value: V, +): V { + contextRecord[cfg.key] = value; + return value; +} + +export function contextValue( + cfg: ContextConfig, + value: V, +): ContextConfigPair { + return [cfg, value]; +} + +export function contextUpdater( + cfg: ContextConfig, + updater: (prev: V) => V, +): ContextConfigUpdater { + return {cfg, updater}; +} + +export function updateContextValue( + contextRecord: ContextRecord, + cfg: ContextConfig, + updater: (prev: V) => V, +): V { + const value = updater(getContextValue(contextRecord, cfg)); + return setContextValue(contextRecord, cfg, value); +} + +export function updateContextFromPairs( + contextRecord: ContextRecord, + pairs: undefined | readonly AnyContextConfigPairOrUpdater[], +): ContextRecord { + if (pairs) { + for (const pairOrUpdater of pairs) { + const [cfg, value] = toPair(contextRecord, pairOrUpdater); + setContextValue(contextRecord, cfg, value); + } + } + return contextRecord; +} + +/** + * @__NO_SIDE_EFFECTS__ + */ +export function $withFullContext( + sym: Ctx, + contextRecord: ContextRecord, + f: () => T, + editor: LexicalEditor = $getEditor(), +): T { + const prevDOMContext = activeContext; + const parentEditorContext = getEditorContext(editor); + try { + activeContext = {...parentEditorContext, editor, [sym]: contextRecord}; + return f(); + } finally { + activeContext = prevDOMContext; + } +} + +/** + * @__NO_SIDE_EFFECTS__ + */ +export function $withContext( + sym: Ctx, + $defaults: (editor: LexicalEditor) => undefined | ContextRecord = () => + undefined, +) { + return ( + cfg: readonly AnyContextConfigPairOrUpdater[], + editor = $getEditor(), + ): ((f: () => T) => T) => { + return (f) => { + const prevDOMContext = activeContext; + const parentEditorContext = getEditorContext(editor); + const parentContextRecord = + parentEditorContext && parentEditorContext[sym]; + const contextRecord = contextFromPairs( + cfg, + parentContextRecord || $defaults(editor), + ); + if (!contextRecord || contextRecord === parentContextRecord) { + return f(); + } + try { + activeContext = {...parentEditorContext, editor, [sym]: contextRecord}; + return f(); + } finally { + activeContext = prevDOMContext; + } + }; + }; +} + +/** + * @__NO_SIDE_EFFECTS__ + */ +export function createContextState( + tag: Tag, + name: string, + getDefaultValue: () => V, + isEqual?: (a: V, b: V) => boolean, +): ContextConfig { + return Object.assign( + createState(Symbol(name), {isEqual, parse: getDefaultValue}), + {[tag]: true} as const, + ); +} diff --git a/packages/lexical-html/src/DOMImportExtension.ts b/packages/lexical-html/src/DOMImportExtension.ts new file mode 100644 index 00000000000..e2d19a6b7cb --- /dev/null +++ b/packages/lexical-html/src/DOMImportExtension.ts @@ -0,0 +1,35 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + */ +import type {DOMImportConfig, DOMImportExtensionOutput} from './types'; + +import {defineExtension, shallowMergeConfig} from 'lexical'; + +import {compileDOMImportOverrides} from './compileDOMImportOverrides'; +import {compileLegacyImportDOM} from './compileLegacyImportDOM'; +import {DOMImportExtensionName} from './constants'; + +/** @internal @experimental */ +export const DOMImportExtension = defineExtension< + DOMImportConfig, + typeof DOMImportExtensionName, + DOMImportExtensionOutput, + null +>({ + build: compileDOMImportOverrides, + config: {compileLegacyImportNode: compileLegacyImportDOM, overrides: []}, + mergeConfig(config, partial) { + const merged = shallowMergeConfig(config, partial); + for (const k of ['overrides'] as const) { + if (partial[k]) { + (merged[k] as unknown[]) = [...config[k], ...partial[k]]; + } + } + return merged; + }, + name: DOMImportExtensionName, +}); diff --git a/packages/lexical-html/src/DOMRenderExtension.ts b/packages/lexical-html/src/DOMRenderExtension.ts new file mode 100644 index 00000000000..6e845b2f1cb --- /dev/null +++ b/packages/lexical-html/src/DOMRenderExtension.ts @@ -0,0 +1,59 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + */ +import type {DOMRenderConfig, DOMRenderExtensionOutput} from './types'; + +import {defineExtension, RootNode, shallowMergeConfig} from 'lexical'; + +import {compileDOMRenderConfigOverrides} from './compileDOMRenderConfigOverrides'; +import {DOMRenderExtensionName} from './constants'; +import {contextFromPairs} from './ContextRecord'; + +/** @internal @experimental */ + +export const DOMRenderExtension = defineExtension< + DOMRenderConfig, + typeof DOMRenderExtensionName, + DOMRenderExtensionOutput, + void +>({ + build(editor, config, state) { + return { + defaults: contextFromPairs(config.contextDefaults, undefined), + }; + }, + config: { + contextDefaults: [], + overrides: [], + }, + html: { + // Define a RootNode export for $generateDOMFromRoot + export: new Map([ + [ + RootNode, + () => { + const element = document.createElement('div'); + element.role = 'textbox'; + return {element}; + }, + ], + ]), + }, + init(editorConfig, config) { + editorConfig.dom = compileDOMRenderConfigOverrides(editorConfig, config); + }, + mergeConfig(config, partial) { + const merged = shallowMergeConfig(config, partial); + for (const k of ['overrides', 'contextDefaults'] as const) { + if (partial[k]) { + (merged[k] as unknown[]) = [...config[k], ...partial[k]]; + } + } + return merged; + }, + name: DOMRenderExtensionName, +}); diff --git a/packages/lexical-html/src/ImportContext.ts b/packages/lexical-html/src/ImportContext.ts new file mode 100644 index 00000000000..f4f1678ce33 --- /dev/null +++ b/packages/lexical-html/src/ImportContext.ts @@ -0,0 +1,188 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + */ + +import type { + AnyImportStateConfigPairOrUpdater, + ContextPairOrUpdater, + ContextRecord, + DOMImportContextFinalizer, + DOMTextWrapMode, + DOMWhiteSpaceCollapse, + ImportStateConfig, +} from './types'; + +import { + $getEditor, + type ArtificialNode__DO_NOT_USE, + type DOMChildConversion, + type ElementFormatType, + type ElementNode, + type LexicalEditor, + type LexicalNode, + type TextFormatType, + type TextNode, +} from 'lexical'; +import invariant from 'shared/invariant'; + +import {DOMImportContextSymbol} from './constants'; +import { + createContextState, + getContextRecord, + getContextValue, + getOwnContextValue, + setContextValue, + updateContextValue, +} from './ContextRecord'; + +/** + * Create a context state to be used during import. + * + * Note that to support the ValueOrUpdater pattern you can not use a + * function for V (but you may wrap it in an array or object). + * + * @__NO_SIDE_EFFECTS__ + */ +export function createImportState( + name: string, + getDefaultValue: () => V, + isEqual?: (a: V, b: V) => boolean, +): ImportStateConfig { + return createContextState( + DOMImportContextSymbol, + name, + getDefaultValue, + isEqual, + ); +} + +export function $getImportContextValue( + cfg: ImportStateConfig, + editor: LexicalEditor = $getEditor(), +): V { + return getContextValue(getContextRecord(DOMImportContextSymbol, editor), cfg); +} + +function getImportContextOrThrow( + editor: LexicalEditor, +): ContextRecord { + const ctx = getContextRecord(DOMImportContextSymbol, editor); + invariant( + ctx !== undefined, + 'getImportContextOrThrow: Import context used outside of DOM import', + ); + return ctx; +} + +export function $setImportContextValue( + cfg: ImportStateConfig, + value: V, + editor: LexicalEditor = $getEditor(), +): V { + return setContextValue(getImportContextOrThrow(editor), cfg, value); +} + +export function $updateImportContextValue( + cfg: ImportStateConfig, + updater: (prev: V) => V, + editor: LexicalEditor = $getEditor(), +): V { + return updateContextValue(getImportContextOrThrow(editor), cfg, updater); +} + +export const ImportContextDOMNode = createImportState( + 'domNode', + (): null | Node => null, +); + +const NO_FORMATS: {readonly [K in TextFormatType]?: undefined | boolean} = + Object.create(null); + +export const ImportContextTextAlign = createImportState( + 'textAlign', + (): undefined | ElementFormatType => undefined, +); + +export function $applyTextAlignToElement(node: T): T { + const align = $getImportContextValue(ImportContextTextAlign); + return align ? node.setFormat(align) : node; +} + +export const ImportContextTextFormats = createImportState( + 'textFormats', + () => NO_FORMATS, +); + +export function $applyTextFormatsFromContext(node: T): T { + const fmt = $getImportContextValue(ImportContextTextFormats); + for (const k in fmt) { + const textFormat = k as keyof typeof fmt; + if (fmt[textFormat]) { + node = node.toggleFormat(textFormat); + } + } + return node; +} + +export const ImportChildContext = createImportState( + 'childContext', + (): undefined | AnyImportStateConfigPairOrUpdater[] => undefined, +); + +export function $addImportChildContext( + pairOrUpdater: ContextPairOrUpdater, + editor: LexicalEditor = $getEditor(), +): void { + const ctx = getImportContextOrThrow(editor); + const childPairs = getOwnContextValue(ctx, ImportChildContext) || []; + childPairs.push(pairOrUpdater); + setContextValue(ctx, ImportChildContext, childPairs); +} + +export const ImportContextFinalizers = createImportState( + 'finalizers', + (): undefined | DOMImportContextFinalizer[] => undefined, +); + +export function $addImportContextFinalizer( + finalizer: DOMImportContextFinalizer, + editor: LexicalEditor = $getEditor(), +): void { + const ctx = getImportContextOrThrow(editor); + const finalizers = getOwnContextValue(ctx, ImportContextFinalizers) || []; + finalizers.push(finalizer); + setContextValue(ctx, ImportContextFinalizers, finalizers); +} + +export const ImportContextWhiteSpaceCollapse = createImportState( + 'whiteSpaceCollapse', + (): DOMWhiteSpaceCollapse => 'collapse', +); + +export const ImportContextTextWrapMode = createImportState( + 'textWrapMode', + (): DOMTextWrapMode => 'wrap', +); + +export const ImportContextParentLexicalNode = createImportState( + 'parentLexicalNode', + (): null | LexicalNode => null, +); +export const ImportContextHasBlockAncestorLexicalNode = createImportState( + 'hasBlockAncestorLexicalNode', + Boolean, +); + +export const ImportContextForChildMap = createImportState( + 'forChildMap', + (): null | Map => null, +); + +export const ImportContextArtificialNodes = createImportState( + 'ArtificialNodes', + (): null | ArtificialNode__DO_NOT_USE[] => null, +); diff --git a/packages/lexical-html/src/RenderContext.ts b/packages/lexical-html/src/RenderContext.ts new file mode 100644 index 00000000000..bdf7f9a65d7 --- /dev/null +++ b/packages/lexical-html/src/RenderContext.ts @@ -0,0 +1,91 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + */ +import { + getExtensionDependencyFromEditor, + LexicalBuilder, +} from '@lexical/extension'; +import {$getEditor, LexicalEditor} from 'lexical'; + +import {DOMRenderContextSymbol, DOMRenderExtensionName} from './constants'; +import { + $withContext, + createContextState, + getContextRecord, + getContextValue, +} from './ContextRecord'; +import {DOMRenderExtension} from './DOMRenderExtension'; +import { + AnyRenderStateConfigPairOrUpdater, + ContextRecord, + RenderStateConfig, +} from './types'; + +/** + * Create a context state to be used during render. + * + * Note that to support the ValueOrUpdater pattern you can not use a + * function for V (but you may wrap it in an array or object). + * + * @__NO_SIDE_EFFECTS__ + */ +export function createRenderState( + name: string, + getDefaultValue: () => V, + isEqual?: (a: V, b: V) => boolean, +): RenderStateConfig { + return createContextState( + DOMRenderContextSymbol, + name, + getDefaultValue, + isEqual, + ); +} + +/** + * true if the export was initiated from the root of the document + */ +export const RenderContextRoot = createRenderState('root', Boolean); + +/** + * true if this is an export operation ($generateHtmlFromNodes) + */ +export const RenderContextExport = createRenderState('isExport', Boolean); + +function getDefaultRenderContext( + editor: LexicalEditor, +): undefined | ContextRecord { + const builder = LexicalBuilder.maybeFromEditor(editor); + return builder && builder.hasExtensionByName(DOMRenderExtensionName) + ? getExtensionDependencyFromEditor(editor, DOMRenderExtension).output + .defaults + : undefined; +} + +function getRenderContext( + editor: LexicalEditor, +): undefined | ContextRecord { + return ( + getContextRecord(DOMRenderContextSymbol, editor) || + getDefaultRenderContext(editor) + ); +} + +export function $getRenderContextValue( + cfg: RenderStateConfig, + editor: LexicalEditor = $getEditor(), +): V { + return getContextValue(getRenderContext(editor), cfg); +} + +export const $withRenderContext: ( + cfg: readonly AnyRenderStateConfigPairOrUpdater[], + editor?: LexicalEditor, +) => (f: () => T) => T = $withContext( + DOMRenderContextSymbol, + getDefaultRenderContext, +); diff --git a/packages/lexical-html/src/__tests__/unit/DOMImportExtension.test.ts b/packages/lexical-html/src/__tests__/unit/DOMImportExtension.test.ts new file mode 100644 index 00000000000..e167c5058b7 --- /dev/null +++ b/packages/lexical-html/src/__tests__/unit/DOMImportExtension.test.ts @@ -0,0 +1,361 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + */ + +import {$insertGeneratedNodes} from '@lexical/clipboard'; +import { + buildEditorFromExtensions, + getExtensionDependencyFromEditor, +} from '@lexical/extension'; +import { + $generateNodesFromDOM, + DOMImportConfig, + DOMImportExtension, + DOMRenderConfig, + DOMRenderExtension, +} from '@lexical/html'; +import {CheckListExtension, ListExtension} from '@lexical/list'; +import { + $getEditor, + $getSelection, + $isElementNode, + $isRangeSelection, + $selectAll, + $setSelection, + configExtension, + defineExtension, + LexicalNode, +} from 'lexical'; +import { + expectHtmlToBeEqual, + html, + // prettifyHtml, +} from 'lexical/src/__tests__/utils'; +import {assert, describe, expect, test} from 'vitest'; + +interface ImportTestCase { + name: string; + pastedHTML: string; + expectedHTML: string; + plainTextInsert?: string; + importConfig?: Partial; + exportConfig?: Partial; +} + +function importCase( + name: string, + pastedHTML: string, + expectedHTML: string, +): ImportTestCase { + return {expectedHTML, name, pastedHTML}; +} + +describe('DOMImportExtension', () => { + test.each([ + importCase( + 'center aligned', + html` +

Hello world!

+ `, + html` +

+ Hello world! +

+ `, + ), + importCase( + 'reduced ul>li>p', + `
  • first

  • second

`, + html` +
    +
  • first
  • +
  • second
  • +
+ `, + ), + { + expectedHTML: html` +

Hello!

+ `, + name: 'plain DOM text node', + pastedHTML: html` + Hello! + `, + }, + { + expectedHTML: html` +

Hello!

+


+ `, + name: 'a paragraph element', + pastedHTML: html` +

Hello!

+

+ `, + }, + { + expectedHTML: html` +

123

+

456

+ `, + name: 'a single div', + pastedHTML: html` + 123 +
456
+ `, + }, + { + expectedHTML: html` +

a b c d e

+

f g h

+ `, + name: 'multiple nested spans and divs', + pastedHTML: html` +
+ a b + + c d + e + +
+ f + g h +
+
+ `, + }, + { + expectedHTML: html` +

123

+

456

+ `, + name: 'nested span in a div', + pastedHTML: html` +
+ + 123 +
456
+
+
+ `, + }, + { + expectedHTML: html` +

123

+

456

+ `, + name: 'nested div in a span', + pastedHTML: html` + + 123 +
456
+
+ `, + }, + { + expectedHTML: html` +
    +
  • + done +
  • +
  • + todo +
  • +
  • +
      +
    • + done +
    • +
    • + todo +
    • +
    +
  • +
  • + todo +
  • +
+ `, + name: 'google doc checklist', + // We can't use the HTML template literal formatter here because it's white-space:pre + pastedHTML: `
  • checked

    done

  • unchecked

    todo

    • checked

      done

    • unchecked

      todo

  • unchecked

    todo

`, + }, + { + expectedHTML: html` +

+ checklist +

+
    +
  • + done +
  • +
  • + todo +
  • +
+ `, + name: 'github checklist', + pastedHTML: html` + +

+ checklist +

+
    +
  • + + + + + + done +
  • +
  • + + + + + + todo +
  • +
+ `, + }, + { + expectedHTML: html` +

+ + hello world + +

+ `, + name: 'pasting inheritance', + pastedHTML: html` + hello + `, + plainTextInsert: ' world', + }, + ])( + '$name', + ({ + expectedHTML, + pastedHTML, + plainTextInsert, + importConfig = {}, + exportConfig = {}, + }: ImportTestCase) => { + const builtEditor = buildEditorFromExtensions( + defineExtension({ + $initialEditorState: (editor) => { + const parser = new DOMParser(); + const doc = parser.parseFromString(pastedHTML, 'text/html'); + const nodes = getExtensionDependencyFromEditor( + $getEditor(), + DOMImportExtension, + ).output.$importNodes(doc); + + // Compare legacy $generateNodesFromDOM to $generateNodes + const legacyNodes = $generateNodesFromDOM(editor, doc); + expect(nodes.length).toEqual(legacyNodes.length); + function compareJSON(a: LexicalNode, b: LexicalNode) { + expect(a.exportJSON()).toEqual(b.exportJSON()); + if ($isElementNode(a) && $isElementNode(b)) { + const as = a.getChildren(); + const bs = b.getChildren(); + expect(as.length).toEqual(bs.length); + for (let i = 0; i < as.length; i++) { + compareJSON(as[i], bs[i]); + } + } + } + for (let i = 0; i < nodes.length; i++) { + compareJSON(nodes[i], legacyNodes[i]); + } + + $insertGeneratedNodes(editor, nodes, $selectAll()); + if (plainTextInsert) { + const newSelection = $getSelection(); + assert( + $isRangeSelection(newSelection), + 'isRangeSelection(newSelection) for plainTextInsert', + ); + newSelection.insertText(plainTextInsert); + } + $setSelection(null); + }, + dependencies: [ + configExtension(DOMImportExtension, importConfig), + configExtension(DOMRenderExtension, exportConfig), + ListExtension, + CheckListExtension, + ], + name: 'root', + theme: { + text: { + bold: 'editor-text-bold', + italic: 'editor-text-italic', + underline: 'editor-text-underline', + }, + }, + }), + ); + const rootElement = document.createElement('div'); + builtEditor.setRootElement(rootElement); + // try { + expectHtmlToBeEqual(rootElement.innerHTML, expectedHTML); + // } catch (err) { + // console.log(prettifyHtml(rootElement.innerHTML)); + // console.log(prettifyHtml(expectedHTML)); + // throw err; + // } + }, + ); +}); diff --git a/packages/lexical-html/src/__tests__/unit/DOMImportExtensionNoLegacy.test.ts b/packages/lexical-html/src/__tests__/unit/DOMImportExtensionNoLegacy.test.ts new file mode 100644 index 00000000000..58d065c24e7 --- /dev/null +++ b/packages/lexical-html/src/__tests__/unit/DOMImportExtensionNoLegacy.test.ts @@ -0,0 +1,771 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + */ + +import {$insertGeneratedNodes} from '@lexical/clipboard'; +import { + buildEditorFromExtensions, + getExtensionDependencyFromEditor, +} from '@lexical/extension'; +import { + $applyTextAlignToElement, + $applyTextFormatsFromContext, + $generateNodesFromDOM, + $getImportContextValue, + $setImportContextValue, + $updateImportContextValue, + type DOMImportConfig, + DOMImportExtension, + type DOMImportOutput, + type DOMRenderConfig, + DOMRenderExtension, + ImportContextParentLexicalNode, + ImportContextTextAlign, + ImportContextTextFormats, + ImportContextWhiteSpaceCollapse, + importOverride, +} from '@lexical/html'; +import { + $createListItemNode, + $createListNode, + $isListItemNode, + $isListNode, + CheckListExtension, + ListExtension, + ListType, +} from '@lexical/list'; +import { + $copyNode, + $createLineBreakNode, + $createParagraphNode, + $createTabNode, + $createTextNode, + $getChildCaret, + $getEditor, + $getSelection, + $isElementNode, + $isRangeSelection, + $selectAll, + $setSelection, + CaretDirection, + configExtension, + defineExtension, + ElementNode, + isBlockDomNode, + isDOMTextNode, + isHTMLElement, + isInlineDomNode, + LexicalNode, + StateConfigValue, + TextFormatType, + TextNode, +} from 'lexical'; +import { + expectHtmlToBeEqual, + html, + // prettifyHtml, +} from 'lexical/src/__tests__/utils'; +import invariant from 'shared/invariant'; +import {assert, describe, expect, test} from 'vitest'; + +import {$addImportContextFinalizer} from '../../ImportContext'; + +interface ImportTestCase { + name: string; + pastedHTML: string; + expectedHTML: string; + plainTextInsert?: string; + importConfig?: Partial; + exportConfig?: Partial; +} + +function importCase( + name: string, + pastedHTML: string, + expectedHTML: string, +): ImportTestCase { + return {expectedHTML, name, pastedHTML}; +} + +function listTypeFromDOM(dom: HTMLUListElement | HTMLOListElement): ListType { + if ( + // lexical html + dom.getAttribute('__lexicallisttype') === 'check' || + // is github checklist + dom.classList.contains('contains-task-list') || + // if children are checklist items, the node is a checklist ul. Applicable for googledoc checklist pasting. + dom.querySelector(':scope > li[aria-checked]') + ) { + return 'check'; + } + return dom.tagName === 'OL' ? 'number' : 'bullet'; +} + +/* + * This function normalizes the children of a ListNode after the conversion from HTML, + * ensuring that they are all ListItemNodes + */ +function $normalizeListNode( + node: null | LexicalNode | LexicalNode[], +): null | LexicalNode | LexicalNode[] { + if (Array.isArray(node) || !$isListNode(node)) { + return node; + } + for (const child of node.getChildren()) { + // Wrap all children in li elements + if (!$isListItemNode(child)) { + const li = $createListItemNode(); + child.replace(li).append(child); + } + } + // Wrap self in a ListItem if it's directly in a ListNode + return $isListNode($getImportContextValue(ImportContextParentLexicalNode)) + ? $createListItemNode().append(node) + : node; +} + +function $isOnlyChild(node: LexicalNode): boolean { + return !!(node.getPreviousSibling() || node.getNextSibling()); +} + +function $normalizeListItemNode( + node: null | LexicalNode | LexicalNode[], +): null | LexicalNode | LexicalNode[] { + if (Array.isArray(node) || !$isListItemNode(node)) { + return node; + } + return $unwrapBlockDOM( + node, + (el) => !el.isInline() || ($isListNode(el) && !$isOnlyChild(el)), + ); +} + +function $importListNode( + dom: HTMLUListElement | HTMLOListElement, +): DOMImportOutput { + const listNode = $createListNode().setListType(listTypeFromDOM(dom)); + $addImportContextFinalizer($normalizeListNode); + return {node: listNode}; +} + +const listOverrides = (['ul', 'ol'] as const).map((tag) => + importOverride(tag, $importListNode), +); + +// https://drafts.csswg.org/css-text-4/#line-break-transform +// const collapsePreserve = (s: string): string => s; +// const collapseFunctions: Record string> = +// { +// 'break-spaces': collapsePreserve, +// collapse: (s) => s.replace(/\s+/g, ' '), +// discard: (s) => s.replace(/\s+/g, ''), +// preserve: collapsePreserve, +// 'preserve-breaks': (s) => s.replace(/[ \t]+/g, ' '), +// 'preserve-spaces': (s) => s.replace(/(?:\r?\n|\t)/g, ' '), +// }; + +function $addTextFormatContinue(format: TextFormatType): () => undefined { + return () => { + $updateImportContextValue(ImportContextTextFormats, (prev) => ({ + ...prev, + [format]: true, + })); + }; +} + +function findTextInLine(text: Text, direction: CaretDirection): null | Text { + let node: Node = text; + const siblingProp = `${direction}Sibling` as const; + const childProp = `${direction === 'next' ? 'first' : 'last'}Child` as const; + // eslint-disable-next-line no-constant-condition + while (true) { + let sibling: null | Node; + while ((sibling = node[siblingProp]) === null) { + const parentElement = node.parentElement; + if (parentElement === null) { + return null; + } + node = parentElement; + } + node = sibling; + if (isHTMLElement(node)) { + const display = node.style.display; + if (!(display ? display.startsWith('inline') : isInlineDomNode(node))) { + return null; + } + } + let descendant: null | Node = node; + while ((descendant = node[childProp]) !== null) { + node = descendant; + } + if (isDOMTextNode(node)) { + return node; + } else if (node.nodeName === 'BR') { + return null; + } + } +} + +function $createTextNodeWithCurrentFormat(text: string = ''): TextNode { + return $applyTextFormatsFromContext($createTextNode(text)); +} + +function $convertTextDOMNode(domNode: Text): DOMImportOutput { + const domNode_ = domNode as Text; + const parentDom = domNode.parentElement; + invariant( + parentDom !== null, + 'Expected parentElement of Text not to be null', + ); + let textContent = domNode_.textContent || ''; + // No collapse and preserve segment break for pre, pre-wrap and pre-line + if ( + $getImportContextValue(ImportContextWhiteSpaceCollapse).startsWith('pre') + ) { + const parts = textContent.split(/(\r?\n|\t)/); + const nodes: Array = []; + const length = parts.length; + for (let i = 0; i < length; i++) { + const part = parts[i]; + if (part === '\n' || part === '\r\n') { + nodes.push($createLineBreakNode()); + } else if (part === '\t') { + nodes.push($createTabNode()); + } else if (part !== '') { + nodes.push($createTextNodeWithCurrentFormat(part)); + } + } + return {node: nodes}; + } + textContent = textContent.replace(/\r/g, '').replace(/[ \t\n]+/g, ' '); + if (textContent === '') { + return {node: null}; + } + if (textContent[0] === ' ') { + // Traverse backward while in the same line. If content contains new line or tab -> potential + // delete, other elements can borrow from this one. Deletion depends on whether it's also the + // last space (see next condition: textContent[textContent.length - 1] === ' ')) + let previousText: null | Text = domNode_; + let isStartOfLine = true; + while ( + previousText !== null && + (previousText = findTextInLine(previousText, 'previous')) !== null + ) { + const previousTextContent = previousText.textContent || ''; + if (previousTextContent.length > 0) { + if (/[ \t\n]$/.test(previousTextContent)) { + textContent = textContent.slice(1); + } + isStartOfLine = false; + break; + } + } + if (isStartOfLine) { + textContent = textContent.slice(1); + } + } + if (textContent[textContent.length - 1] === ' ') { + // Traverse forward while in the same line, preserve if next inline will require a space + let nextText: null | Text = domNode_; + let isEndOfLine = true; + while ( + nextText !== null && + (nextText = findTextInLine(nextText, 'next')) !== null + ) { + const nextTextContent = (nextText.textContent || '').replace( + /^( |\t|\r?\n)+/, + '', + ); + if (nextTextContent.length > 0) { + isEndOfLine = false; + break; + } + } + if (isEndOfLine) { + textContent = textContent.slice(0, textContent.length - 1); + } + } + if (textContent === '') { + return {node: null}; + } + return {node: $createTextNodeWithCurrentFormat(textContent)}; +} + +const TO_FORMAT = { + code: 'code', + em: 'italic', + i: 'italic', + mark: 'highlight', + s: 'strikethrough', + strong: 'bold', + sub: 'subscript', + sup: 'superscript', + u: 'underline', +} as const; +const formatOverrides = Object.entries(TO_FORMAT).map(([tag, format]) => + importOverride(tag as keyof typeof TO_FORMAT, $addTextFormatContinue(format)), +); + +function $unwrapBlockDOM( + node: LexicalNode | LexicalNode[] | null, + $splitPredicate: (el: ElementNode) => boolean = (el) => !el.isInline(), + $createNextElement: (el: ElementNode) => ElementNode = $copyNode, +): null | LexicalNode | LexicalNode[] { + if (Array.isArray(node) || !$isElementNode(node)) { + return node; + } + let adjacentNodes: undefined | ElementNode[]; + let lastParent: undefined | ElementNode; + for (const {origin} of $getChildCaret(node, 'next')) { + if ($isElementNode(origin) && $splitPredicate(origin)) { + lastParent = undefined; + adjacentNodes = adjacentNodes || []; + origin.remove(); + adjacentNodes.push(origin); + } else if (adjacentNodes) { + lastParent = lastParent || $createNextElement(node); + origin.remove(); + lastParent.append(origin); + } + } + if (adjacentNodes) { + if (node.isEmpty()) { + node.remove(); + } else { + adjacentNodes.unshift(node); + } + } + return adjacentNodes || node; +} + +type Writable = {-readonly [K in keyof T]: T[K]}; + +function $updateFormatContextFromDOM(dom: HTMLElement) { + const {fontWeight, fontStyle, textDecoration, verticalAlign} = dom.style; + let formats: + | undefined + | Writable>; + const setFormat = (k: TextFormatType, v: boolean) => { + const fmt = formats || {}; + fmt[k] = v; + formats = fmt; + }; + switch (fontWeight) { + case '400': + case 'normal': + setFormat('bold', false); + break; + case '700': + case 'bold': + setFormat('bold', true); + break; + default: + break; + } + const italic = 'italic'; + if (fontStyle === 'normal') { + setFormat(italic, false); + } else if (fontStyle === italic) { + setFormat(italic, true); + } + const underline = 'underline'; + const strikethrough = 'strikethrough'; + for (const dec of textDecoration.split(' ')) { + if (dec === 'none') { + setFormat(underline, false); + setFormat(strikethrough, false); + } else if (dec === underline) { + setFormat(underline, true); + } else if (dec === 'line-through') { + setFormat('strikethrough', true); + } + } + if (verticalAlign === 'sub') { + setFormat('subscript', true); + } else if (verticalAlign === 'super') { + setFormat('superscript', true); + } + if (formats) { + $updateImportContextValue(ImportContextTextFormats, (v) => ({ + ...v, + ...formats, + })); + } +} + +function $updateTextAlignmentContextFromDOM(dom: HTMLElement): void { + const {textAlign} = dom.style; + switch (textAlign) { + case 'center': + case 'end': + case 'justify': + case 'left': + case 'right': + case 'start': + $setImportContextValue(ImportContextTextAlign, textAlign); + break; + default: + break; + } +} + +const NO_LEGACY_CONFIG: Partial = { + compileLegacyImportNode: () => () => null, + overrides: [ + importOverride('#text', $convertTextDOMNode), + importOverride('br', () => ({node: $createLineBreakNode()})), + importOverride('*', function $overrideCreateParagraphFromBlock(dom) { + if (isBlockDomNode(dom)) { + $addImportContextFinalizer($unwrapBlockDOM); + return { + node: $applyTextAlignToElement($createParagraphNode()), + }; + } + }), + ...formatOverrides, + ...listOverrides, + importOverride('li', (dom) => { + const node = $applyTextAlignToElement($createListItemNode()); + let ariaChecked: boolean | undefined; + if (dom.ariaChecked === 'true') { + ariaChecked = true; + } else if (dom.ariaChecked === 'false') { + ariaChecked = false; + } else { + const input: null | HTMLInputElement = dom.querySelector( + 'input[type=checkbox]', + ); + if (input) { + ariaChecked = input.checked; + } + } + if (ariaChecked !== undefined) { + node.setChecked(ariaChecked); + } + $addImportContextFinalizer($normalizeListItemNode); + return {node}; + }), + importOverride( + '*', + function $overrideBlockFormatAndAlignment(dom): undefined { + if (isHTMLElement(dom)) { + if (isBlockDomNode(dom)) { + $updateTextAlignmentContextFromDOM(dom); + } + $updateFormatContextFromDOM(dom); + } + }, + {priority: 1}, + ), + ], +}; + +describe('DOMImportExtension (no legacy)', () => { + test.each([ + importCase( + 'center aligned', + html` +

Hello world!

+ `, + html` +

+ Hello world! +

+ `, + ), + importCase( + 'reduced ul>li>p', + `
  • first

  • second

`, + html` +
    +
  • first
  • +
  • second
  • +
+ `, + ), + { + expectedHTML: html` +

Hello!

+ `, + name: 'plain DOM text node', + pastedHTML: html` + Hello! + `, + }, + { + expectedHTML: html` +

Hello!

+


+ `, + name: 'a paragraph element', + pastedHTML: html` +

Hello!

+

+ `, + }, + { + expectedHTML: html` +

123

+

456

+ `, + name: 'a single div', + pastedHTML: html` + 123 +
456
+ `, + }, + { + expectedHTML: html` +

a b c d e

+

f g h

+ `, + name: 'multiple nested spans and divs', + pastedHTML: html` +
+ a b + + c d + e + +
+ f + g h +
+
+ `, + }, + { + expectedHTML: html` +

123

+

456

+ `, + name: 'nested span in a div', + pastedHTML: html` +
+ + 123 +
456
+
+
+ `, + }, + { + expectedHTML: html` +

123

+

456

+ `, + name: 'nested div in a span', + pastedHTML: html` + + 123 +
456
+
+ `, + }, + { + expectedHTML: html` +
    +
  • + done +
  • +
  • + todo +
  • +
  • +
      +
    • + done +
    • +
    • + todo +
    • +
    +
  • +
  • + todo +
  • +
+ `, + name: 'google doc checklist', + // We can't use the HTML template literal formatter here because it's white-space:pre + pastedHTML: `
  • checked

    done

  • unchecked

    todo

    • checked

      done

    • unchecked

      todo

  • unchecked

    todo

`, + }, + { + expectedHTML: html` +

+ checklist +

+
    +
  • + done +
  • +
  • + todo +
  • +
+ `, + name: 'github checklist', + pastedHTML: html` + +

+ checklist +

+
    +
  • + + + + + + done +
  • +
  • + + + + + + todo +
  • +
+ `, + }, + { + expectedHTML: html` +

+ + hello world + +

+ `, + name: 'pasting inheritance', + pastedHTML: html` + hello + `, + plainTextInsert: ' world', + }, + ])( + '$name', + ({ + expectedHTML, + pastedHTML, + plainTextInsert, + importConfig = {}, + exportConfig = {}, + }: ImportTestCase) => { + const builtEditor = buildEditorFromExtensions( + defineExtension({ + $initialEditorState: (editor) => { + const parser = new DOMParser(); + const doc = parser.parseFromString(pastedHTML, 'text/html'); + const nodes = getExtensionDependencyFromEditor( + $getEditor(), + DOMImportExtension, + ).output.$importNodes(doc); + + // Compare legacy $generateNodesFromDOM to $generateNodes + const legacyNodes = $generateNodesFromDOM(editor, doc); + expect(nodes.length).toEqual(legacyNodes.length); + function compareJSON(a: LexicalNode, b: LexicalNode) { + expect(a.exportJSON()).toEqual(b.exportJSON()); + if ($isElementNode(a) && $isElementNode(b)) { + const as = a.getChildren(); + const bs = b.getChildren(); + expect(as.length).toEqual(bs.length); + for (let i = 0; i < as.length; i++) { + compareJSON(as[i], bs[i]); + } + } + } + for (let i = 0; i < nodes.length; i++) { + compareJSON(nodes[i], legacyNodes[i]); + } + + $insertGeneratedNodes(editor, nodes, $selectAll()); + if (plainTextInsert) { + const newSelection = $getSelection(); + assert( + $isRangeSelection(newSelection), + 'isRangeSelection(newSelection) for plainTextInsert', + ); + newSelection.insertText(plainTextInsert); + } + $setSelection(null); + }, + dependencies: [ + configExtension(DOMImportExtension, NO_LEGACY_CONFIG, importConfig), + configExtension(DOMRenderExtension, exportConfig), + ListExtension, + CheckListExtension, + ], + name: 'root', + theme: { + text: { + bold: 'editor-text-bold', + italic: 'editor-text-italic', + underline: 'editor-text-underline', + }, + }, + }), + ); + const rootElement = document.createElement('div'); + builtEditor.setRootElement(rootElement); + // try { + expectHtmlToBeEqual(rootElement.innerHTML, expectedHTML); + // } catch (err) { + // console.log(prettifyHtml(rootElement.innerHTML)); + // console.log(prettifyHtml(expectedHTML)); + // throw err; + // } + }, + ); +}); diff --git a/packages/lexical-html/src/__tests__/unit/DOMRenderExtension.test.ts b/packages/lexical-html/src/__tests__/unit/DOMRenderExtension.test.ts new file mode 100644 index 00000000000..d4ce0c3a5ab --- /dev/null +++ b/packages/lexical-html/src/__tests__/unit/DOMRenderExtension.test.ts @@ -0,0 +1,277 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + */ + +import {buildEditorFromExtensions} from '@lexical/extension'; +import { + $generateDOMFromRoot, + $getRenderContextValue, + domOverride, + DOMRenderExtension, + RenderContextRoot, +} from '@lexical/html'; +import { + $create, + $createLineBreakNode, + $createParagraphNode, + $createTextNode, + $getRoot, + $getState, + $getStateChange, + $setState, + configExtension, + createState, + defineExtension, + isHTMLElement, + TextNode, +} from 'lexical'; +import {expectHtmlToBeEqual, html} from 'lexical/src/__tests__/utils'; +import {describe, expect, test} from 'vitest'; + +const idState = createState('id', { + parse: (v) => (typeof v === 'string' ? v : null), +}); + +describe('DOMRenderExtension', () => { + test('can override DOM create + update', () => { + const editor = buildEditorFromExtensions( + defineExtension({ + $initialEditorState: () => { + const root = $getRoot(); + $setState(root, idState, 'root').append( + $setState($createParagraphNode(), idState, 'paragraph').append( + $setState($createTextNode('text!'), idState, 'text'), + ), + ); + }, + dependencies: [ + configExtension(DOMRenderExtension, { + overrides: [ + domOverride('*', { + $createDOM(node, $next) { + const result = $next(); + const id = $getState(node, idState); + if (id) { + result.setAttribute('id', id); + } + return result; + }, + $updateDOM(nextNode, prevNode, dom, $next) { + if ($next()) { + return true; + } + const change = $getStateChange(nextNode, prevNode, idState); + if (change) { + const [id] = change; + if (id) { + dom.setAttribute('id', id); + } else { + dom.removeAttribute('id'); + } + } + return false; + }, + }), + ], + }), + ], + name: 'root', + }), + ); + const root = document.createElement('div'); + editor.setRootElement(root); + expect( + editor.read(() => { + expectHtmlToBeEqual( + root.innerHTML, + html` +

+ text! +

+ `, + ); + }), + ); + editor.update( + () => + $getRoot() + .getAllTextNodes() + .forEach((node) => + $setState(node, idState, (prev) => `${prev}-updated`), + ), + {discrete: true}, + ); + // Update works too + expect( + editor.read(() => { + expectHtmlToBeEqual( + root.innerHTML, + html` +

+ text! +

+ `, + ); + }), + ); + editor.update( + () => + $getRoot() + .getAllTextNodes() + .forEach((node) => $setState(node, idState, null)), + {discrete: true}, + ); + expect( + editor.read(() => { + expectHtmlToBeEqual( + root.innerHTML, + html` +

+ text! +

+ `, + ); + }), + ); + }); + test('can override DOM export', () => { + const editor = buildEditorFromExtensions( + defineExtension({ + $initialEditorState: () => { + const root = $getRoot(); + $setState(root, idState, 'root').append( + $setState($createParagraphNode(), idState, 'paragraph').append( + $setState($createTextNode('text!'), idState, 'text'), + ), + ); + }, + dependencies: [ + configExtension(DOMRenderExtension, { + overrides: [ + domOverride('*', { + $exportDOM(node, $next) { + const result = $next(); + const id = $getState(node, idState); + if (id && isHTMLElement(result.element)) { + result.element.setAttribute('id', id); + } + return result; + }, + }), + domOverride([TextNode], { + $exportDOM(node, $next) { + const result = $next(); + if ( + $getRenderContextValue(RenderContextRoot) && + isHTMLElement(result.element) && + result.element.style.getPropertyValue('white-space') === + 'pre-wrap' && + // we know there aren't tabs or newlines but if there are + // leading, trailing, or adjacent spaces then we need the + // pre-wrap to preserve the content + !/^\s|\s$|\s\s/.test(result.element.textContent) + ) { + result.element.style.setProperty('white-space', null); + if (result.element.style.cssText === '') { + result.element.removeAttribute('style'); + } + } + return result; + }, + }), + ], + }), + ], + name: 'root', + }), + ); + expect( + editor.read(() => { + expectHtmlToBeEqual( + $generateDOMFromRoot(document.createElement('div')).innerHTML, + html` +
+

+ text! +

+
+ `, + ); + }), + ); + }); + test('type merge', () => { + class TextNodeA extends TextNode { + $config() { + return this.config('text-a', {extends: TextNode}); + } + } + const editor = buildEditorFromExtensions( + defineExtension({ + $initialEditorState: () => { + $getRoot().append( + $createParagraphNode().append( + $create(TextNodeA).setTextContent('text a'), + $createLineBreakNode(), + $createTextNode().setTextContent('plain text'), + ), + ); + }, + dependencies: [ + configExtension(DOMRenderExtension, { + overrides: [ + domOverride([TextNode], { + $exportDOM(node) { + const span = document.createElement('span'); + span.append(node.getTextContent()); + return {element: span}; + }, + }), + domOverride([TextNodeA], { + $exportDOM(node) { + const span = document.createElement('span'); + span.append(node.getTextContent()); + span.dataset.lexicalType = node.getType(); + return {element: span}; + }, + }), + domOverride([TextNode], { + $exportDOM(node, $next) { + const r = $next(); + if (isHTMLElement(r.element)) { + r.element.dataset.didOverride = 'true'; + } + return r; + }, + }), + ], + }), + ], + name: 'root', + nodes: [TextNodeA], + }), + ); + expect( + editor.read(() => { + expectHtmlToBeEqual( + $generateDOMFromRoot(document.createElement('div')).innerHTML, + html` +
+

+ + text a + +
+ plain text +

+
+ `, + ); + }), + ); + }); +}); diff --git a/packages/lexical-html/src/__tests__/unit/compileDOMRenderConfigOverrides.test.ts b/packages/lexical-html/src/__tests__/unit/compileDOMRenderConfigOverrides.test.ts new file mode 100644 index 00000000000..ebceebf0768 --- /dev/null +++ b/packages/lexical-html/src/__tests__/unit/compileDOMRenderConfigOverrides.test.ts @@ -0,0 +1,182 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + */ + +import {buildEditorFromExtensions} from '@lexical/extension'; +import {domOverride} from '@lexical/html'; +import { + $isLineBreakNode, + isHTMLElement, + LineBreakNode, + ParagraphNode, + TabNode, + TextNode, +} from 'lexical'; +import {describe, expect, test} from 'vitest'; + +import { + buildTypeTree, + precompileDOMRenderConfigOverrides, +} from '../../compileDOMRenderConfigOverrides'; +import {ALWAYS_TRUE} from '../../constants'; + +describe('buildTypeTree', () => { + test('includes basic types', () => { + const editor = buildEditorFromExtensions(); + expect(buildTypeTree(editor._createEditorArgs!)).toMatchObject({ + linebreak: { + klass: LineBreakNode, + types: { + linebreak: true, + }, + }, + paragraph: { + klass: ParagraphNode, + types: {paragraph: true}, + }, + tab: {klass: TabNode, types: {tab: true}}, + text: {klass: TextNode, types: {tab: true, text: true}}, + }); + }); +}); + +describe('precompileDOMRenderConfigOverrides', () => { + test('precompiles with only type overrides', () => { + class TextNodeA extends TextNode { + $config() { + return this.config('text-a', {extends: TextNode}); + } + } + const overrides = [ + domOverride([TextNode], { + $exportDOM(node) { + const span = document.createElement('span'); + span.append(node.getTextContent()); + return {element: span}; + }, + }), + domOverride([TextNodeA], { + $exportDOM(node) { + const span = document.createElement('span'); + span.append(node.getTextContent()); + span.dataset.lexicalType = node.getType(); + return {element: span}; + }, + }), + domOverride([TextNode], { + $exportDOM(node, $next) { + const r = $next(); + if (isHTMLElement(r.element)) { + r.element.dataset.didOverride = 'true'; + } + return r; + }, + }), + ]; + const prerender = precompileDOMRenderConfigOverrides( + {nodes: [TextNode, TextNodeA]}, + overrides, + ); + expect(prerender).toEqual({ + $createDOM: [], + $exportDOM: [ + [ + 'types', + { + text: [overrides[0].$exportDOM, overrides[2].$exportDOM], + 'text-a': [ + overrides[0].$exportDOM, + overrides[1].$exportDOM, + overrides[2].$exportDOM, + ], + }, + ], + ], + $extractWithChild: [], + $getDOMSlot: [], + $shouldExclude: [], + $shouldInclude: [], + $updateDOM: [], + }); + }); + test('precompiles with wildcards, predicates, and type overrides', () => { + class TextNodeA extends TextNode { + $config() { + return this.config('text-a', {extends: TextNode}); + } + } + const overrides = [ + domOverride([TextNode], { + $exportDOM(node) { + const span = document.createElement('span'); + span.append(node.getTextContent()); + return {element: span}; + }, + }), + domOverride('*', { + $createDOM(node, $next) { + return $next(); + }, + $exportDOM(node, $next) { + return $next(); + }, + }), + domOverride([TextNodeA], { + $exportDOM(node) { + const span = document.createElement('span'); + span.append(node.getTextContent()); + span.dataset.lexicalType = node.getType(); + return {element: span}; + }, + }), + domOverride([TextNode], { + $exportDOM(node, $next) { + const r = $next(); + if (isHTMLElement(r.element)) { + r.element.dataset.didOverride = 'true'; + } + return r; + }, + }), + domOverride([$isLineBreakNode], { + $exportDOM(node, $next) { + return $next(); + }, + }), + ]; + const prerender = precompileDOMRenderConfigOverrides( + {nodes: [TextNode, TextNodeA]}, + overrides, + ); + expect(prerender).toEqual({ + $createDOM: [[ALWAYS_TRUE, overrides[1].$createDOM]], + $exportDOM: [ + [ + 'types', + { + text: [overrides[0].$exportDOM], + 'text-a': [overrides[0].$exportDOM], + }, + ], + [ALWAYS_TRUE, overrides[1].$exportDOM], + [ + 'types', + { + text: [overrides[3].$exportDOM], + 'text-a': [overrides[2].$exportDOM, overrides[3].$exportDOM], + }, + ], + [$isLineBreakNode, overrides[4].$exportDOM], + ], + $extractWithChild: [], + $getDOMSlot: [], + $shouldExclude: [], + $shouldInclude: [], + $updateDOM: [], + }); + }); +}); diff --git a/packages/lexical-html/src/compileDOMImportOverrides.ts b/packages/lexical-html/src/compileDOMImportOverrides.ts new file mode 100644 index 00000000000..c61a4496726 --- /dev/null +++ b/packages/lexical-html/src/compileDOMImportOverrides.ts @@ -0,0 +1,390 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + */ +import type { + AnyImportStateConfigPairOrUpdater, + ContextRecord, + DOMImportConfig, + DOMImportConfigMatch, + DOMImportContextFinalizer, + DOMImportExtensionOutput, + DOMImportOutput, + DOMTextWrapMode, + DOMWhiteSpaceCollapse, +} from './types'; + +import { + $isBlockElementNode, + $isElementNode, + $isRootOrShadowRoot, + ArtificialNode__DO_NOT_USE, + isDOMDocumentNode, + isHTMLElement, + type LexicalEditor, + type LexicalNode, +} from 'lexical'; +import invariant from 'shared/invariant'; + +import {$unwrapArtificialNodes} from './$unwrapArtificialNodes'; +import { + ALWAYS_NULL, + DOMImportContextSymbol, + DOMTextWrapModeKeys, + DOMWhiteSpaceCollapseKeys, + EMPTY_ARRAY, +} from './constants'; +import { + $withFullContext, + contextValue, + createChildContext, + popOwnContextValue, + updateContextFromPairs, +} from './ContextRecord'; +import { + $getImportContextValue, + ImportChildContext, + ImportContextArtificialNodes, + ImportContextDOMNode, + ImportContextFinalizers, + ImportContextHasBlockAncestorLexicalNode, + ImportContextParentLexicalNode, + ImportContextTextWrapMode, + ImportContextWhiteSpaceCollapse, +} from './ImportContext'; + +class MatchesImport { + tag: Tag; + matches: DOMImportConfigMatch[] = []; + constructor(tag: Tag) { + this.tag = tag; + } + push(match: DOMImportConfigMatch) { + invariant( + match.tag === this.tag, + 'MatchesImport.push: match tag %s !== this tag %s', + match.tag, + this.tag, + ); + this.matches.push(match); + } + compile( + $nextImport: (node: Node) => null | undefined | DOMImportOutput, + editor: LexicalEditor, + ): (node: Node) => null | undefined | DOMImportOutput { + const {matches, tag} = this; + return (node) => { + const el = isHTMLElement(node) ? node : null; + const $importAt = (start: number): null | undefined | DOMImportOutput => { + let rval: undefined | null | DOMImportOutput; + let $importFallback = $nextImport; + for ( + let i = start; + i >= 0 && !rval && $importFallback !== ALWAYS_NULL; + i-- + ) { + const match = matches[i]; + if (match) { + const {$import, selector} = matches[i]; + if (!selector || (el && el.matches(selector))) { + rval = $import( + node, + () => { + $importFallback = ALWAYS_NULL; + return $importAt(i - 1); + }, + editor, + ); + } + } + } + return rval || $importFallback(node); + }; + + return $importAt( + (tag === node.nodeName.toLowerCase() || (el && tag === '*') + ? matches.length + : 0) - 1, + ); + }; + } +} + +class TagImport { + tags: Map> = new Map(); + push(match: DOMImportConfigMatch) { + invariant( + match.tag !== '*', + 'TagImport can not handle wildcard tag %s', + match.tag, + ); + const matches = this.tags.get(match.tag) || new MatchesImport(match.tag); + this.tags.set(match.tag, matches); + matches.push(match); + } + compile( + $nextImport: (node: Node) => null | undefined | DOMImportOutput, + editor: LexicalEditor, + ): DOMImportExtensionOutput['$importNode'] { + const compiled = new Map(); + for (const [tag, matches] of this.tags.entries()) { + compiled.set(tag, matches.compile($nextImport, editor)); + } + return compiled.size === 0 + ? $nextImport + : (node: Node) => + (compiled.get(node.nodeName.toLowerCase()) || $nextImport)(node); + } +} + +/** + * Sort matches by lowest priority first. This is to preserve the invariant + * that overrides added "later" (closer to the root of the extension tree, + * or later in a given array) should run at a higher priority. + * + * For example given the overrides `[a,b,c]` it is expected that the execution + * order is `c -> b -> a` assuming equal priorities. This is because the + * "least specific" behavior is going to be naturally "earlier" in the array + * (e.g. the initial implementation). + */ +function importOverrideSort( + a: DOMImportConfigMatch, + b: DOMImportConfigMatch, +): number { + return (a.priority || 0) - (b.priority || 0); +} + +type ImportStackEntry = [ + dom: Node, + ctx: ContextRecord, + $importNode: DOMImportExtensionOutput['$importNode'], + $appendChild: NonNullable, +]; + +function parseDOMWhiteSpaceCollapseFromNode( + ctx: ContextRecord, + node: Node, +): ContextRecord { + if (isHTMLElement(node)) { + const {style} = node; + let textWrapMode: undefined | DOMTextWrapMode; + let whiteSpaceCollapse: undefined | DOMWhiteSpaceCollapse; + switch (style.whiteSpace) { + case 'normal': + whiteSpaceCollapse = 'collapse'; + textWrapMode = 'wrap'; + break; + case 'pre': + whiteSpaceCollapse = 'preserve'; + textWrapMode = 'nowrap'; + break; + case 'pre-wrap': + whiteSpaceCollapse = 'preserve'; + textWrapMode = 'wrap'; + break; + case 'pre-line': + whiteSpaceCollapse = 'preserve-breaks'; + textWrapMode = 'nowrap'; + break; + default: + break; + } + whiteSpaceCollapse = + ( + DOMWhiteSpaceCollapseKeys as Record< + string, + undefined | DOMWhiteSpaceCollapse + > + )[style.whiteSpaceCollapse] || whiteSpaceCollapse; + textWrapMode = + (DOMTextWrapModeKeys as Record)[ + style.textWrapMode + ] || textWrapMode; + if (textWrapMode) { + ctx[ImportContextTextWrapMode.key] = textWrapMode; + } + if (whiteSpaceCollapse) { + ctx[ImportContextWhiteSpaceCollapse.key] = whiteSpaceCollapse; + } + } + return ctx; +} + +function makeFinalizer( + outputNode: null | LexicalNode | LexicalNode[], + finalizers: DOMImportContextFinalizer[], +): () => DOMImportOutput { + return () => { + let node = outputNode; + for ( + let finalizer = finalizers.pop(); + finalizer; + finalizer = finalizers.pop() + ) { + node = finalizer(node); + } + return {childNodes: EMPTY_ARRAY, node}; + }; +} + +function compileImportNodes( + editor: LexicalEditor, + $importNode: DOMImportExtensionOutput['$importNode'], +) { + return function $importNodes( + rootOrDocument: ParentNode | Document, + ): LexicalNode[] { + const artificialNodes: ArtificialNode__DO_NOT_USE[] = []; + const nodes: LexicalNode[] = []; + const rootNode = isDOMDocumentNode(rootOrDocument) + ? rootOrDocument.body + : rootOrDocument; + const stack: ImportStackEntry[] = [ + [ + rootNode, + updateContextFromPairs(createChildContext(undefined), [ + contextValue(ImportContextArtificialNodes, artificialNodes), + ]), + () => ({node: null}), + (node) => { + nodes.push(node); + }, + ], + ]; + for (let entry = stack.pop(); entry; entry = stack.pop()) { + const [node, ctx, fn, $parentAppendChild] = entry; + ctx[ImportContextDOMNode.key] = node; + parseDOMWhiteSpaceCollapseFromNode(ctx, node); + let childContext: + | undefined + | ContextRecord; + const updateChildContext = ( + pairs: undefined | readonly AnyImportStateConfigPairOrUpdater[], + ) => { + if (pairs) { + childContext = updateContextFromPairs( + childContext || createChildContext(ctx), + pairs, + ); + } + }; + const output = $withFullContext( + DOMImportContextSymbol, + ctx, + fn.bind(null, node), + editor, + ); + let children: NodeListOf | readonly ChildNode[] = + isHTMLElement(node) ? node.childNodes : EMPTY_ARRAY; + let $appendChild = $parentAppendChild; + updateChildContext(popOwnContextValue(ctx, ImportChildContext)); + delete ctx[ImportChildContext.key]; + if (output) { + const outputNode = output.node; + if (output.$appendChild) { + $appendChild = output.$appendChild; + } else if (Array.isArray(outputNode)) { + $appendChild = (childNode, _dom) => outputNode.push(childNode); + } else if ($isElementNode(outputNode)) { + $appendChild = (childNode, _dom) => outputNode.append(childNode); + } + children = output.childNodes || children; + const finalizers = popOwnContextValue(ctx, ImportContextFinalizers); + if (finalizers && finalizers.length > 0) { + stack.push([ + node, + ctx, + makeFinalizer(outputNode, finalizers), + $parentAppendChild, + ]); + } else if (outputNode) { + for (const addNode of Array.isArray(outputNode) + ? outputNode + : [outputNode]) { + $parentAppendChild(addNode, node as ChildNode); + } + } + + const currentLexicalNode = Array.isArray(outputNode) + ? outputNode[outputNode.length - 1] || null + : outputNode; + const hasBlockAncestorLexicalNode = $getImportContextValue( + ImportContextHasBlockAncestorLexicalNode, + ); + const hasBlockAncestorLexicalNodeForChildren = + currentLexicalNode && $isRootOrShadowRoot(currentLexicalNode) + ? false + : (currentLexicalNode && $isBlockElementNode(currentLexicalNode)) || + hasBlockAncestorLexicalNode; + + if ( + hasBlockAncestorLexicalNode !== hasBlockAncestorLexicalNodeForChildren + ) { + updateChildContext([ + contextValue( + ImportContextHasBlockAncestorLexicalNode, + hasBlockAncestorLexicalNodeForChildren, + ), + ]); + } + if ($isElementNode(currentLexicalNode)) { + updateChildContext([ + contextValue(ImportContextParentLexicalNode, currentLexicalNode), + ]); + } + } + // Push children in reverse so they are popped off the stack in-order + for (let i = children.length - 1; i >= 0; i--) { + const childDom = children[i]; + stack.push([ + childDom, + createChildContext(childContext || ctx), + $importNode, + $appendChild, + ]); + } + } + $unwrapArtificialNodes(artificialNodes); + return nodes; + }; +} + +function matchHasTag( + match: DOMImportConfigMatch, + tag: T, +): match is DOMImportConfigMatch & {tag: T} { + return match.tag === tag; +} + +function compileImportNode(editor: LexicalEditor, config: DOMImportConfig) { + let $importNode = config.compileLegacyImportNode(editor); + let importer: TagImport | MatchesImport<'*'> = new TagImport(); + const sortedOverrides = config.overrides.sort(importOverrideSort); + for (const match of sortedOverrides) { + if (matchHasTag(match, '*')) { + if (importer instanceof TagImport) { + $importNode = importer.compile($importNode, editor); + importer = new MatchesImport(match.tag); + } + } else if (importer instanceof MatchesImport) { + $importNode = importer.compile($importNode, editor); + importer = new TagImport(); + } + importer.push(match); + } + return importer.compile($importNode, editor); +} + +export function compileDOMImportOverrides( + editor: LexicalEditor, + config: DOMImportConfig, +): DOMImportExtensionOutput { + const $importNode = compileImportNode(editor, config); + return { + $importNode, + $importNodes: compileImportNodes(editor, $importNode), + }; +} diff --git a/packages/lexical-html/src/compileDOMRenderConfigOverrides.ts b/packages/lexical-html/src/compileDOMRenderConfigOverrides.ts new file mode 100644 index 00000000000..9417cb24021 --- /dev/null +++ b/packages/lexical-html/src/compileDOMRenderConfigOverrides.ts @@ -0,0 +1,318 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + */ +import {getKnownTypesAndNodes} from '@lexical/extension'; +import { + $isLexicalNode, + DEFAULT_EDITOR_DOM_CONFIG, + type EditorDOMRenderConfig, + getStaticNodeConfig, + InitialEditorConfig, + Klass, + LexicalEditor, + type LexicalNode, +} from 'lexical'; +import invariant from 'shared/invariant'; + +import {ALWAYS_TRUE} from './constants'; +import {AnyDOMRenderMatch, DOMRenderConfig, DOMRenderMatch} from './types'; + +interface TypeRecord { + readonly klass: Klass; + readonly types: {[NodeAndSubclasses in string]?: boolean}; +} + +type TypeTree = { + [NodeType in string]?: TypeRecord; +}; + +export function buildTypeTree( + editorConfig: Pick, +): TypeTree { + const t: TypeTree = {}; + const {nodes} = getKnownTypesAndNodes(editorConfig); + for (const klass of nodes) { + const type = klass.getType(); + t[type] = {klass, types: {}}; + } + for (const baseRec of Object.values(t)) { + if (baseRec) { + const baseType = baseRec.klass.getType(); + for ( + let {klass} = baseRec; + $isLexicalNode(klass.prototype); + klass = Object.getPrototypeOf(klass) + ) { + const {ownNodeType} = getStaticNodeConfig(klass); + const superRec = ownNodeType && t[ownNodeType]; + if (superRec) { + superRec.types[baseType] = true; + } + } + } + } + return t; +} + +type PredicateOrTypes = + | ((node: LexicalNode) => boolean) + | {[NodeType in string]?: true}; +type TypeRender = {[NodeType in string]?: T[]}; +type AnyRender = + | readonly [(node: LexicalNode) => boolean, T] + | readonly ['types', TypeRender]; + +type PreEditorDOMRenderConfig = { + [K in keyof EditorDOMRenderConfig]: AnyRender[]; +}; + +function buildNodePredicate(klass: Klass) { + return (node: LexicalNode): node is T => node instanceof klass; +} + +function getPredicate( + typeTree: TypeTree, + {nodes}: DOMRenderMatch, +): {[NodeType in string]?: true} | ((node: LexicalNode) => boolean) { + if (nodes === '*') { + return ALWAYS_TRUE; + } + let types: undefined | {[NodeType in string]?: true} = {}; + const predicates: ((node: LexicalNode) => boolean)[] = []; + for (const klassOrPredicate of nodes) { + if ('getType' in klassOrPredicate) { + const type = klassOrPredicate.getType(); + if (types) { + const tree = typeTree[type]; + invariant( + tree !== undefined, + 'Node class %s with type %s not registered in editor', + klassOrPredicate.name, + type, + ); + types = Object.assign(types, tree.types); + } + predicates.push(buildNodePredicate(klassOrPredicate)); + } else { + types = undefined; + predicates.push(klassOrPredicate); + } + } + if (types) { + return types; + } else if (predicates.length === 1) { + return predicates[0]; + } + return (node: LexicalNode): boolean => { + for (const predicate of predicates) { + if (predicate(node)) { + return true; + } + } + return false; + }; +} + +function makePrerender(): PreEditorDOMRenderConfig { + return { + $createDOM: [], + $exportDOM: [], + $extractWithChild: [], + $getDOMSlot: [], + $shouldExclude: [], + $shouldInclude: [], + $updateDOM: [], + }; +} + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +type AccFn = ( + node: N, + ...rest: [...Args, editor: LexicalEditor] +) => T; + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +type GetOverrideFn = ( + n: N, +) => undefined | OverrideFn; + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +type OverrideFn = ( + node: N, + ...rest: [...Args, $next: () => T, editor: LexicalEditor] +) => T; + +function ignoreNext2( + acc: AccFn, +): OverrideFn { + return (node: N, _$next: () => T, editor: LexicalEditor) => acc(node, editor); +} +function ignoreNext3( + acc: AccFn, +): OverrideFn { + return (node: N, a: A, _$next: () => T, editor: LexicalEditor) => + acc(node, a, editor); +} +function ignoreNext4( + acc: AccFn, +): OverrideFn { + return (node: N, a: A, b: B, _$next: () => T, editor: LexicalEditor) => + acc(node, a, b, editor); +} +function ignoreNext5( + acc: AccFn, +): OverrideFn { + return (node: N, a: A, b: B, c: C, _$next: () => T, editor: LexicalEditor) => + acc(node, a, b, c, editor); +} + +function merge2( + $acc: AccFn, + $getOverride: GetOverrideFn, +): typeof $acc { + return (node, editor) => { + const $next = () => $acc(node, editor); + const $override = $getOverride(node); + return $override ? $override(node, $next, editor) : $next(); + }; +} + +function merge3( + acc: AccFn, + $getOverride: GetOverrideFn, +): typeof acc { + return (node, a, editor) => { + const $next = () => acc(node, a, editor); + const $override = $getOverride(node); + return $override ? $override(node, a, $next, editor) : $next(); + }; +} + +function merge4( + $acc: AccFn, + $getOverride: GetOverrideFn, +): typeof $acc { + return (node, a, b, editor) => { + const $next = () => $acc(node, a, b, editor); + const $override = $getOverride(node); + return $override ? $override(node, a, b, $next, editor) : $next(); + }; +} + +function merge5( + acc: AccFn, + $getOverride: GetOverrideFn, +): typeof acc { + return (node, a, b, c, editor) => { + const $next = () => acc(node, a, b, c, editor); + const $override = $getOverride(node); + return $override ? $override(node, a, b, c, $next, editor) : $next(); + }; +} + +function compilePrerenderKey( + prerender: PreEditorDOMRenderConfig, + k: K, + defaults: EditorDOMRenderConfig, + mergeFunction: ( + $acc: EditorDOMRenderConfig[K], + $getOverride: (node: LexicalNode) => AnyDOMRenderMatch[K], + ) => typeof $acc, + ignoreNextFunction: (fn: EditorDOMRenderConfig[K]) => AnyDOMRenderMatch[K], +): void { + let acc = defaults[k]; + for (const pair of prerender[k]) { + if (typeof pair[0] === 'function') { + const [$predicate, $override] = pair; + acc = mergeFunction( + acc, + (node) => ($predicate(node) && $override) || undefined, + ); + } else { + const typeOverrides = pair[1]; + const compiled: Record = {}; + for (const type in typeOverrides) { + const arr = typeOverrides[type]; + if (arr) { + compiled[type] = arr.reduce( + ($acc, $override) => mergeFunction($acc, () => $override), + acc, + ); + } + } + acc = mergeFunction(acc, (node) => { + const f = compiled[node.getType()]; + return f && ignoreNextFunction(f); + }); + } + } + defaults[k] = acc; +} + +function addOverride( + prerender: PreEditorDOMRenderConfig, + k: K, + predicateOrTypes: PredicateOrTypes, + override: AnyDOMRenderMatch[K], +): void { + if (!override) { + return; + } + const arr = prerender[k]; + if (typeof predicateOrTypes === 'function') { + arr.push([predicateOrTypes, override]); + } else { + const last = arr[arr.length - 1]; + let types: TypeRender; + if (last && last[0] === 'types') { + types = last[1]; + } else { + types = {}; + arr.push(['types', types]); + } + for (const type in predicateOrTypes) { + const typeArr = types[type] || []; + types[type] = typeArr; + typeArr.push(override); + } + } +} + +export function precompileDOMRenderConfigOverrides( + editorConfig: Pick, + overrides: DOMRenderConfig['overrides'], +): PreEditorDOMRenderConfig { + const typeTree = buildTypeTree(editorConfig); + const prerender = makePrerender(); + for (const override of overrides) { + const predicateOrTypes = getPredicate(typeTree, override); + for (const k_ in prerender) { + const k = k_ as keyof typeof prerender; + addOverride(prerender, k, predicateOrTypes, override[k]); + } + } + return prerender; +} + +export function compileDOMRenderConfigOverrides( + editorConfig: InitialEditorConfig, + {overrides}: DOMRenderConfig, +): EditorDOMRenderConfig { + const prerender = precompileDOMRenderConfigOverrides(editorConfig, overrides); + const dom = { + ...DEFAULT_EDITOR_DOM_CONFIG, + ...editorConfig.dom, + }; + compilePrerenderKey(prerender, '$createDOM', dom, merge2, ignoreNext2); + compilePrerenderKey(prerender, '$exportDOM', dom, merge2, ignoreNext2); + compilePrerenderKey(prerender, '$extractWithChild', dom, merge5, ignoreNext5); + compilePrerenderKey(prerender, '$getDOMSlot', dom, merge3, ignoreNext3); + compilePrerenderKey(prerender, '$shouldExclude', dom, merge3, ignoreNext3); + compilePrerenderKey(prerender, '$shouldInclude', dom, merge3, ignoreNext3); + compilePrerenderKey(prerender, '$updateDOM', dom, merge4, ignoreNext4); + return dom; +} diff --git a/packages/lexical-html/src/compileLegacyImportDOM.ts b/packages/lexical-html/src/compileLegacyImportDOM.ts new file mode 100644 index 00000000000..7cd63f7fd47 --- /dev/null +++ b/packages/lexical-html/src/compileLegacyImportDOM.ts @@ -0,0 +1,171 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + */ +import type {DOMImportExtensionOutput, DOMImportOutput} from './types'; + +import { + $createLineBreakNode, + $createParagraphNode, + $isBlockElementNode, + $isElementNode, + $isRootOrShadowRoot, + ArtificialNode__DO_NOT_USE, + type DOMConversionOutput, + isBlockDomNode, + type LexicalEditor, + type LexicalNode, +} from 'lexical'; +import invariant from 'shared/invariant'; + +import {$wrapContinuousInlinesInPlace} from './$wrapContinuousInlinesInPlace'; +import {EMPTY_ARRAY, IGNORE_TAGS} from './constants'; +import {contextUpdater} from './ContextRecord'; +import {getConversionFunction} from './getConversionFunction'; +import { + $addImportChildContext, + $addImportContextFinalizer, + $getImportContextValue, + ImportContextArtificialNodes, + ImportContextForChildMap, + ImportContextHasBlockAncestorLexicalNode, + ImportContextParentLexicalNode, +} from './ImportContext'; +import {isDomNodeBetweenTwoInlineNodes} from './isDomNodeBetweenTwoInlineNodes'; + +export function compileLegacyImportDOM( + editor: LexicalEditor, +): DOMImportExtensionOutput['$importNode'] { + return (node) => { + if (IGNORE_TAGS.has(node.nodeName)) { + return {childNodes: EMPTY_ARRAY, node: null}; + } + // If the DOM node doesn't have a transformer, we don't know what + // to do with it but we still need to process any childNodes. + let childLexicalNodes: LexicalNode[] = []; + let postTransform: DOMConversionOutput['after']; + const output: DOMImportOutput = { + $appendChild: (childNode) => childLexicalNodes.push(childNode), + node: null, + }; + $addImportContextFinalizer((nodeOrNodes) => { + const finalLexicalNodes = Array.isArray(nodeOrNodes) + ? nodeOrNodes + : nodeOrNodes + ? [nodeOrNodes] + : []; + const finalLexicalNode: null | LexicalNode = + finalLexicalNodes[finalLexicalNodes.length - 1] || null; + if (postTransform) { + childLexicalNodes = postTransform(childLexicalNodes); + } + if (isBlockDomNode(node)) { + const hasBlockAncestorLexicalNodeForChildren = + finalLexicalNode && $isRootOrShadowRoot(finalLexicalNode) + ? false + : (finalLexicalNode && $isBlockElementNode(finalLexicalNode)) || + $getImportContextValue(ImportContextHasBlockAncestorLexicalNode); + + if (!hasBlockAncestorLexicalNodeForChildren) { + $wrapContinuousInlinesInPlace( + node, + childLexicalNodes, + $createParagraphNode, + ); + } else { + const allArtificialNodes = $getImportContextValue( + ImportContextArtificialNodes, + ); + invariant( + allArtificialNodes !== null, + 'Missing ImportContextArtificialNodes', + ); + $wrapContinuousInlinesInPlace(node, childLexicalNodes, () => { + const artificialNode = new ArtificialNode__DO_NOT_USE(); + allArtificialNodes.push(artificialNode); + return artificialNode; + }); + } + } + + if (finalLexicalNode == null) { + if (childLexicalNodes.length > 0) { + // If it hasn't been converted to a LexicalNode, we hoist its children + // up to the same level as it. + finalLexicalNodes.push(...childLexicalNodes); + } else { + if (isBlockDomNode(node) && isDomNodeBetweenTwoInlineNodes(node)) { + // Empty block dom node that hasnt been converted, we replace it with a linebreak if its between inline nodes + finalLexicalNodes.push($createLineBreakNode()); + } + } + } else { + if ($isElementNode(finalLexicalNode)) { + // If the current node is a ElementNode after conversion, + // we can append all the children to it. + finalLexicalNode.append(...childLexicalNodes); + } + } + + return finalLexicalNodes; + }); + let currentLexicalNode: null | LexicalNode = null; + const transformFunction = getConversionFunction(node, editor); + const transformOutput = transformFunction + ? transformFunction(node as HTMLElement) + : null; + + if (transformOutput !== null) { + const forChildMap = $getImportContextValue( + ImportContextForChildMap, + editor, + ); + const parentLexicalNode = $getImportContextValue( + ImportContextParentLexicalNode, + editor, + ); + postTransform = transformOutput.after; + let transformNodeArray = Array.isArray(transformOutput.node) + ? transformOutput.node + : transformOutput.node + ? [transformOutput.node] + : []; + + if (transformNodeArray.length > 0 && forChildMap) { + const transformWithForChild = (initial: LexicalNode) => { + let current: null | undefined | LexicalNode = initial; + for (const forChildFunction of forChildMap.values()) { + current = forChildFunction(current, parentLexicalNode); + + if (!current) { + return []; + } + } + return [current]; + }; + transformNodeArray = transformNodeArray.flatMap(transformWithForChild); + } + currentLexicalNode = + transformNodeArray[transformNodeArray.length - 1] || null; + output.node = + transformNodeArray.length > 1 ? transformNodeArray : currentLexicalNode; + + if (transformOutput.forChild) { + const {forChild} = transformOutput; + $addImportChildContext( + contextUpdater(ImportContextForChildMap, (prev) => { + return new Map(prev || forChildMap || []).set( + node.nodeName, + forChild, + ); + }), + ); + } + } + + return output; + }; +} diff --git a/packages/lexical-html/src/constants.ts b/packages/lexical-html/src/constants.ts new file mode 100644 index 00000000000..954c990a064 --- /dev/null +++ b/packages/lexical-html/src/constants.ts @@ -0,0 +1,37 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + */ +export const DOMRenderExtensionName = '@lexical/html/DOM'; +export const DOMImportExtensionName = '@lexical/html/DOMImport'; +export const IGNORE_TAGS = new Set(['STYLE', 'SCRIPT']); +export const DOMImportContextSymbol = Symbol.for( + '@lexical/html/DOMImportContext', +); +export const DOMRenderContextSymbol = Symbol.for( + '@lexical/html/DOMExportContext', +); + +// https://drafts.csswg.org/css-text-4/#white-space-collapsing +export const DOMWhiteSpaceCollapseKeys = { + 'break-spaces': 'break-spaces', + collapse: 'collapse', + discard: 'discard', + preserve: 'preserve', + 'preserve-breaks': 'preserve-breaks', + 'preserve-spaces': 'preserve-spaces', +} as const; + +export const DOMTextWrapModeKeys = { + nowrap: 'nowrap', + wrap: 'wrap', +} as const; + +export const EMPTY_ARRAY = [] as const; + +export const ALWAYS_TRUE = () => true as const; + +export const ALWAYS_NULL = () => null; diff --git a/packages/lexical-html/src/domOverride.ts b/packages/lexical-html/src/domOverride.ts new file mode 100644 index 00000000000..d73a599f951 --- /dev/null +++ b/packages/lexical-html/src/domOverride.ts @@ -0,0 +1,31 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + */ +import type {AnyDOMRenderMatch, DOMRenderMatch, NodeMatch} from './types'; +import type {LexicalNode} from 'lexical'; + +/** + * A convenience function for type inference when constructing DOM overrides for + * use with {@link DOMRenderExtension}. + * + * @__NO_SIDE_EFFECTS__ + */ + +export function domOverride( + nodes: '*', + config: Omit, 'nodes'>, +): DOMRenderMatch; +export function domOverride( + nodes: readonly NodeMatch[], + config: Omit, 'nodes'>, +): DOMRenderMatch; +export function domOverride( + nodes: AnyDOMRenderMatch['nodes'], + config: Omit, +): AnyDOMRenderMatch { + return {...config, nodes}; +} diff --git a/packages/lexical-html/src/getConversionFunction.ts b/packages/lexical-html/src/getConversionFunction.ts new file mode 100644 index 00000000000..ef889b416f8 --- /dev/null +++ b/packages/lexical-html/src/getConversionFunction.ts @@ -0,0 +1,36 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + */ +import type {DOMConversion, DOMConversionFn, LexicalEditor} from 'lexical'; + +export function getConversionFunction( + domNode: Node, + editor: LexicalEditor, +): DOMConversionFn | null { + const {nodeName} = domNode; + + const cachedConversions = editor._htmlConversions.get(nodeName.toLowerCase()); + + let currentConversion: DOMConversion | null = null; + + if (cachedConversions !== undefined) { + for (const cachedConversion of cachedConversions) { + const domConversion = cachedConversion(domNode); + if ( + domConversion !== null && + (currentConversion === null || + // Given equal priority, prefer the last registered importer + // which is typically an application custom node or HTMLConfig['import'] + (currentConversion.priority || 0) <= (domConversion.priority || 0)) + ) { + currentConversion = domConversion; + } + } + } + + return currentConversion !== null ? currentConversion.conversion : null; +} diff --git a/packages/lexical-html/src/importOverride.ts b/packages/lexical-html/src/importOverride.ts new file mode 100644 index 00000000000..f7bcc5d6379 --- /dev/null +++ b/packages/lexical-html/src/importOverride.ts @@ -0,0 +1,31 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + */ +import type { + DOMImportConfigMatch, + DOMImportFunction, + NodeNameToType, +} from './types'; + +/** + * A convenience function for type inference when constructing DOM overrides for + * use with {@link DOMImportExtension}. + * + * @__NO_SIDE_EFFECTS__ + */ + +export function importOverride( + tag: T, + $import: DOMImportFunction>, + options: Omit = {}, +): DOMImportConfigMatch { + return { + ...options, + $import: $import as DOMImportFunction, + tag: tag.toLowerCase(), + }; +} diff --git a/packages/lexical-html/src/index.ts b/packages/lexical-html/src/index.ts index fc31fbb8021..4857502c12d 100644 --- a/packages/lexical-html/src/index.ts +++ b/packages/lexical-html/src/index.ts @@ -5,376 +5,54 @@ * LICENSE file in the root directory of this source tree. * */ - -import type { - BaseSelection, - DOMChildConversion, - DOMConversion, - DOMConversionFn, - ElementFormatType, - LexicalEditor, - LexicalNode, -} from 'lexical'; - -import {$sliceSelectedTextNodeContent} from '@lexical/selection'; -import {isBlockDomNode, isHTMLElement} from '@lexical/utils'; -import { - $createLineBreakNode, - $createParagraphNode, - $getRoot, - $isBlockElementNode, - $isElementNode, - $isRootOrShadowRoot, - $isTextNode, - ArtificialNode__DO_NOT_USE, - ElementNode, - getRegisteredNode, - isDocumentFragment, - isDOMDocumentNode, - isInlineDomNode, -} from 'lexical'; - -/** - * How you parse your html string to get a document is left up to you. In the browser you can use the native - * DOMParser API to generate a document (see clipboard.ts), but to use in a headless environment you can use JSDom - * or an equivalent library and pass in the document here. - */ -export function $generateNodesFromDOM( - editor: LexicalEditor, - dom: Document | ParentNode, -): Array { - const elements = isDOMDocumentNode(dom) - ? dom.body.childNodes - : dom.childNodes; - let lexicalNodes: Array = []; - const allArtificialNodes: Array = []; - for (const element of elements) { - if (!IGNORE_TAGS.has(element.nodeName)) { - const lexicalNode = $createNodesFromDOM( - element, - editor, - allArtificialNodes, - false, - ); - if (lexicalNode !== null) { - lexicalNodes = lexicalNodes.concat(lexicalNode); - } - } - } - $unwrapArtificialNodes(allArtificialNodes); - - return lexicalNodes; -} - -export function $generateHtmlFromNodes( - editor: LexicalEditor, - selection?: BaseSelection | null, -): string { - if ( - typeof document === 'undefined' || - (typeof window === 'undefined' && typeof global.window === 'undefined') - ) { - throw new Error( - 'To use $generateHtmlFromNodes in headless mode please initialize a headless browser implementation such as JSDom before calling this function.', - ); - } - - const container = document.createElement('div'); - const root = $getRoot(); - const topLevelChildren = root.getChildren(); - - for (let i = 0; i < topLevelChildren.length; i++) { - const topLevelNode = topLevelChildren[i]; - $appendNodesToHTML(editor, topLevelNode, container, selection); - } - - return container.innerHTML; -} - -function $appendNodesToHTML( - editor: LexicalEditor, - currentNode: LexicalNode, - parentElement: HTMLElement | DocumentFragment, - selection: BaseSelection | null = null, -): boolean { - let shouldInclude = - selection !== null ? currentNode.isSelected(selection) : true; - const shouldExclude = - $isElementNode(currentNode) && currentNode.excludeFromCopy('html'); - let target = currentNode; - - if (selection !== null && $isTextNode(currentNode)) { - target = $sliceSelectedTextNodeContent(selection, currentNode, 'clone'); - } - const children = $isElementNode(target) ? target.getChildren() : []; - const registeredNode = getRegisteredNode(editor, target.getType()); - let exportOutput; - - // Use HTMLConfig overrides, if available. - if (registeredNode && registeredNode.exportDOM !== undefined) { - exportOutput = registeredNode.exportDOM(editor, target); - } else { - exportOutput = target.exportDOM(editor); - } - - const {element, after} = exportOutput; - - if (!element) { - return false; - } - - const fragment = document.createDocumentFragment(); - - for (let i = 0; i < children.length; i++) { - const childNode = children[i]; - const shouldIncludeChild = $appendNodesToHTML( - editor, - childNode, - fragment, - selection, - ); - - if ( - !shouldInclude && - $isElementNode(currentNode) && - shouldIncludeChild && - currentNode.extractWithChild(childNode, selection, 'html') - ) { - shouldInclude = true; - } - } - - if (shouldInclude && !shouldExclude) { - if (isHTMLElement(element) || isDocumentFragment(element)) { - element.append(fragment); - } - parentElement.append(element); - - if (after) { - const newElement = after.call(target, element); - if (newElement) { - if (isDocumentFragment(element)) { - element.replaceChildren(newElement); - } else { - element.replaceWith(newElement); - } - } - } - } else { - parentElement.append(fragment); - } - - return shouldInclude; -} - -function getConversionFunction( - domNode: Node, - editor: LexicalEditor, -): DOMConversionFn | null { - const {nodeName} = domNode; - - const cachedConversions = editor._htmlConversions.get(nodeName.toLowerCase()); - - let currentConversion: DOMConversion | null = null; - - if (cachedConversions !== undefined) { - for (const cachedConversion of cachedConversions) { - const domConversion = cachedConversion(domNode); - if ( - domConversion !== null && - (currentConversion === null || - // Given equal priority, prefer the last registered importer - // which is typically an application custom node or HTMLConfig['import'] - (currentConversion.priority || 0) <= (domConversion.priority || 0)) - ) { - currentConversion = domConversion; - } - } - } - - return currentConversion !== null ? currentConversion.conversion : null; -} - -const IGNORE_TAGS = new Set(['STYLE', 'SCRIPT']); - -function $createNodesFromDOM( - node: Node, - editor: LexicalEditor, - allArtificialNodes: Array, - hasBlockAncestorLexicalNode: boolean, - forChildMap: Map = new Map(), - parentLexicalNode?: LexicalNode | null | undefined, -): Array { - let lexicalNodes: Array = []; - - if (IGNORE_TAGS.has(node.nodeName)) { - return lexicalNodes; - } - - let currentLexicalNode = null; - const transformFunction = getConversionFunction(node, editor); - const transformOutput = transformFunction - ? transformFunction(node as HTMLElement) - : null; - let postTransform = null; - - if (transformOutput !== null) { - postTransform = transformOutput.after; - const transformNodes = transformOutput.node; - currentLexicalNode = Array.isArray(transformNodes) - ? transformNodes[transformNodes.length - 1] - : transformNodes; - - if (currentLexicalNode !== null) { - for (const [, forChildFunction] of forChildMap) { - currentLexicalNode = forChildFunction( - currentLexicalNode, - parentLexicalNode, - ); - - if (!currentLexicalNode) { - break; - } - } - - if (currentLexicalNode) { - lexicalNodes.push( - ...(Array.isArray(transformNodes) - ? transformNodes - : [currentLexicalNode]), - ); - } - } - - if (transformOutput.forChild != null) { - forChildMap.set(node.nodeName, transformOutput.forChild); - } - } - - // If the DOM node doesn't have a transformer, we don't know what - // to do with it but we still need to process any childNodes. - const children = node.childNodes; - let childLexicalNodes = []; - - const hasBlockAncestorLexicalNodeForChildren = - currentLexicalNode != null && $isRootOrShadowRoot(currentLexicalNode) - ? false - : (currentLexicalNode != null && - $isBlockElementNode(currentLexicalNode)) || - hasBlockAncestorLexicalNode; - - for (let i = 0; i < children.length; i++) { - childLexicalNodes.push( - ...$createNodesFromDOM( - children[i], - editor, - allArtificialNodes, - hasBlockAncestorLexicalNodeForChildren, - new Map(forChildMap), - currentLexicalNode, - ), - ); - } - - if (postTransform != null) { - childLexicalNodes = postTransform(childLexicalNodes); - } - - if (isBlockDomNode(node)) { - if (!hasBlockAncestorLexicalNodeForChildren) { - childLexicalNodes = wrapContinuousInlines( - node, - childLexicalNodes, - $createParagraphNode, - ); - } else { - childLexicalNodes = wrapContinuousInlines(node, childLexicalNodes, () => { - const artificialNode = new ArtificialNode__DO_NOT_USE(); - allArtificialNodes.push(artificialNode); - return artificialNode; - }); - } - } - - if (currentLexicalNode == null) { - if (childLexicalNodes.length > 0) { - // If it hasn't been converted to a LexicalNode, we hoist its children - // up to the same level as it. - lexicalNodes = lexicalNodes.concat(childLexicalNodes); - } else { - if (isBlockDomNode(node) && isDomNodeBetweenTwoInlineNodes(node)) { - // Empty block dom node that hasnt been converted, we replace it with a linebreak if its between inline nodes - lexicalNodes = lexicalNodes.concat($createLineBreakNode()); - } - } - } else { - if ($isElementNode(currentLexicalNode)) { - // If the current node is a ElementNode after conversion, - // we can append all the children to it. - currentLexicalNode.append(...childLexicalNodes); - } - } - - return lexicalNodes; -} - -function wrapContinuousInlines( - domNode: Node, - nodes: Array, - createWrapperFn: () => ElementNode, -): Array { - const textAlign = (domNode as HTMLElement).style - .textAlign as ElementFormatType; - const out: Array = []; - let continuousInlines: Array = []; - // wrap contiguous inline child nodes in para - for (let i = 0; i < nodes.length; i++) { - const node = nodes[i]; - if ($isBlockElementNode(node)) { - if (textAlign && !node.getFormat()) { - node.setFormat(textAlign); - } - out.push(node); - } else { - continuousInlines.push(node); - if ( - i === nodes.length - 1 || - (i < nodes.length - 1 && $isBlockElementNode(nodes[i + 1])) - ) { - const wrapper = createWrapperFn(); - wrapper.setFormat(textAlign); - wrapper.append(...continuousInlines); - out.push(wrapper); - continuousInlines = []; - } - } - } - return out; -} - -function $unwrapArtificialNodes( - allArtificialNodes: Array, -) { - for (const node of allArtificialNodes) { - if (node.getNextSibling() instanceof ArtificialNode__DO_NOT_USE) { - node.insertAfter($createLineBreakNode()); - } - } - // Replace artificial node with it's children - for (const node of allArtificialNodes) { - const children = node.getChildren(); - for (const child of children) { - node.insertBefore(child); - } - node.remove(); - } -} - -function isDomNodeBetweenTwoInlineNodes(node: Node): boolean { - if (node.nextSibling == null || node.previousSibling == null) { - return false; - } - return ( - isInlineDomNode(node.nextSibling) && isInlineDomNode(node.previousSibling) - ); -} +export { + $generateDOMFromNodes, + $generateDOMFromRoot, + $generateHtmlFromNodes, +} from './$generateDOMFromNodes'; +export {$generateNodesFromDOM} from './$generateNodesFromDOM'; +export {contextUpdater, contextValue} from './ContextRecord'; +export {DOMImportExtension} from './DOMImportExtension'; +export {domOverride} from './domOverride'; +export {DOMRenderExtension} from './DOMRenderExtension'; +export { + $applyTextAlignToElement, + $applyTextFormatsFromContext, + $getImportContextValue, + $setImportContextValue, + $updateImportContextValue, + ImportContextHasBlockAncestorLexicalNode, + ImportContextParentLexicalNode, + ImportContextTextAlign, + ImportContextTextFormats, + ImportContextWhiteSpaceCollapse, +} from './ImportContext'; +export {importOverride} from './importOverride'; +export { + $getRenderContextValue, + $withRenderContext, + RenderContextExport, + RenderContextRoot, +} from './RenderContext'; +export type { + AnyDOMRenderMatch, + AnyImportStateConfig, + AnyImportStateConfigPairOrUpdater, + AnyRenderStateConfig, + AnyRenderStateConfigPairOrUpdater, + ContextPairOrUpdater, + DOMImportConfig, + DOMImportConfigMatch, + DOMImportContextFinalizer, + DOMImportExtensionOutput, + DOMImportFunction, + DOMImportOutput, + DOMRenderConfig, + DOMRenderExtensionOutput, + DOMRenderMatch, + DOMTextWrapMode, + DOMWhiteSpaceCollapse, + NodeMatch, + NodeNameMap, + NodeNameToType, +} from './types'; diff --git a/packages/lexical-html/src/isDomNodeBetweenTwoInlineNodes.ts b/packages/lexical-html/src/isDomNodeBetweenTwoInlineNodes.ts new file mode 100644 index 00000000000..1a0c8aec377 --- /dev/null +++ b/packages/lexical-html/src/isDomNodeBetweenTwoInlineNodes.ts @@ -0,0 +1,17 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + */ +import {isInlineDomNode} from 'lexical'; + +export function isDomNodeBetweenTwoInlineNodes(node: Node): boolean { + if (node.nextSibling == null || node.previousSibling == null) { + return false; + } + return ( + isInlineDomNode(node.nextSibling) && isInlineDomNode(node.previousSibling) + ); +} diff --git a/packages/lexical-html/src/parseStringEnum.ts b/packages/lexical-html/src/parseStringEnum.ts new file mode 100644 index 00000000000..9c6db7bd9c9 --- /dev/null +++ b/packages/lexical-html/src/parseStringEnum.ts @@ -0,0 +1,13 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + */ +export function parseStringEnum( + stringEnum: {[K in T]: K}, + value: string, +): T | undefined { + return (stringEnum as Record)[value]; +} diff --git a/packages/lexical-html/src/types.ts b/packages/lexical-html/src/types.ts new file mode 100644 index 00000000000..28093b63edd --- /dev/null +++ b/packages/lexical-html/src/types.ts @@ -0,0 +1,188 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + */ + +import type { + DOMImportContextSymbol, + DOMRenderContextSymbol, + DOMTextWrapModeKeys, + DOMWhiteSpaceCollapseKeys, +} from './constants'; +import type { + BaseSelection, + DOMExportOutput, + ElementDOMSlot, + Klass, + LexicalEditor, + LexicalNode, + StateConfig, +} from 'lexical'; + +export type AnyContextSymbol = + | typeof DOMImportContextSymbol + | typeof DOMRenderContextSymbol; + +export type ContextRecord<_K extends symbol> = Record; + +export type ContextConfig = StateConfig & { + readonly [K in Sym]?: true; +}; + +export type ContextConfigUpdater = { + readonly cfg: ContextConfig; + readonly updater: (prev: V) => V; +}; +export type ContextConfigPair = readonly [ + ContextConfig, + V, +]; + +export type ContextPairOrUpdater = + | ContextConfigPair + | ContextConfigUpdater; + +export type AnyContextConfigPairOrUpdater = + // eslint-disable-next-line @typescript-eslint/no-explicit-any + ContextPairOrUpdater; + +export interface DOMRenderExtensionOutput { + defaults: undefined | ContextRecord; +} + +export type ImportStateConfig = ContextConfig< + typeof DOMImportContextSymbol, + V +>; + +export type RenderStateConfig = ContextConfig< + typeof DOMRenderContextSymbol, + V +>; + +export type AnyImportStateConfigPairOrUpdater = AnyContextConfigPairOrUpdater< + typeof DOMImportContextSymbol +>; +export type AnyRenderStateConfigPairOrUpdater = AnyContextConfigPairOrUpdater< + typeof DOMRenderContextSymbol +>; + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export type AnyRenderStateConfig = RenderStateConfig; +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export type AnyImportStateConfig = ImportStateConfig; + +export interface DOMImportOutput { + node: null | LexicalNode | LexicalNode[]; + childNodes?: NodeListOf | readonly ChildNode[]; + $appendChild?: (node: LexicalNode, dom: ChildNode) => void; +} + +export type DOMImportFunction = ( + node: T, + $next: () => null | undefined | DOMImportOutput, + editor: LexicalEditor, +) => null | undefined | DOMImportOutput; + +export interface NodeNameMap extends HTMLElementTagNameMap { + '*': Node; + '#text': Text; + '#document': Document; + '#comment': Comment; + '#cdata-section': CDATASection; +} + +export type NodeNameToType = T extends keyof NodeNameMap + ? NodeNameMap[T] + : Node; + +/** @internal @experimental */ +export interface DOMRenderConfig { + overrides: AnyDOMRenderMatch[]; + contextDefaults: AnyRenderStateConfigPairOrUpdater[]; +} + +/** @internal @experimental */ +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export type AnyDOMRenderMatch = DOMRenderMatch; + +export type NodeMatch = + | Klass + | ((node: LexicalNode) => node is T); + +/** @internal @experimental */ +export interface DOMRenderMatch { + readonly nodes: '*' | readonly NodeMatch[]; + $getDOMSlot?: ( + node: N, + dom: HTMLElement, + $next: () => ElementDOMSlot, + editor: LexicalEditor, + ) => ElementDOMSlot; + $createDOM?: ( + node: T, + $next: () => HTMLElement, + editor: LexicalEditor, + ) => HTMLElement; + $updateDOM?: ( + nextNode: T, + prevNode: T, + dom: HTMLElement, + $next: () => boolean, + editor: LexicalEditor, + ) => boolean; + $exportDOM?: ( + node: T, + $next: () => DOMExportOutput, + editor: LexicalEditor, + ) => DOMExportOutput; + $shouldExclude?: ( + node: T, + selection: null | BaseSelection, + $next: () => boolean, + editor: LexicalEditor, + ) => boolean; + $shouldInclude?: ( + node: T, + selection: null | BaseSelection, + $next: () => boolean, + editor: LexicalEditor, + ) => boolean; + $extractWithChild?: ( + node: T, + childNode: LexicalNode, + selection: null | BaseSelection, + destination: 'clone' | 'html', + $next: () => boolean, + editor: LexicalEditor, + ) => boolean; +} + +/** @internal @experimental */ +export interface DOMImportConfig { + overrides: DOMImportConfigMatch[]; + compileLegacyImportNode: ( + editor: LexicalEditor, + ) => DOMImportExtensionOutput['$importNode']; +} +export interface DOMImportConfigMatch { + readonly tag: '*' | '#text' | '#cdata-section' | '#comment' | (string & {}); + readonly selector?: string; + readonly priority?: 0 | 1 | 2 | 3 | 4; + readonly $import: DOMImportFunction; +} + +export interface DOMImportExtensionOutput { + $importNode: (node: Node) => null | undefined | DOMImportOutput; + $importNodes: (root: ParentNode | Document) => LexicalNode[]; +} + +export type DOMWhiteSpaceCollapse = keyof typeof DOMWhiteSpaceCollapseKeys; +export type DOMTextWrapMode = keyof typeof DOMTextWrapModeKeys; + +export type DOMImportContextFinalizer = ( + node: null | LexicalNode | LexicalNode[], +) => null | LexicalNode | LexicalNode[]; diff --git a/packages/lexical-link/src/ClickableLinkExtension.ts b/packages/lexical-link/src/ClickableLinkExtension.ts index 5c6378bd4f8..88b4f95676e 100644 --- a/packages/lexical-link/src/ClickableLinkExtension.ts +++ b/packages/lexical-link/src/ClickableLinkExtension.ts @@ -86,7 +86,7 @@ export function registerClickableLink( } // Allow user to select link text without following url - const selection = editor.getEditorState().read($getSelection); + const selection = editor.getEditorState().read($getSelection, {editor}); if ($isRangeSelection(selection) && !selection.isCollapsed()) { event.preventDefault(); return; diff --git a/packages/lexical-playground/src/plugins/TableActionMenuPlugin/index.tsx b/packages/lexical-playground/src/plugins/TableActionMenuPlugin/index.tsx index fcd10a41e29..b4438d70927 100644 --- a/packages/lexical-playground/src/plugins/TableActionMenuPlugin/index.tsx +++ b/packages/lexical-playground/src/plugins/TableActionMenuPlugin/index.tsx @@ -849,7 +849,7 @@ function TableCellActionMenuContainer({ let timeoutId: ReturnType | undefined = undefined; const callback = () => { timeoutId = undefined; - editor.getEditorState().read($moveMenu); + editor.getEditorState().read($moveMenu, {editor}); }; const delayedCallback = () => { if (timeoutId === undefined) { diff --git a/packages/lexical-react/src/LexicalErrorBoundary.tsx b/packages/lexical-react/src/LexicalErrorBoundary.tsx index a10e26bcc33..59c9ad347be 100644 --- a/packages/lexical-react/src/LexicalErrorBoundary.tsx +++ b/packages/lexical-react/src/LexicalErrorBoundary.tsx @@ -8,7 +8,6 @@ import type {JSX} from 'react'; -import * as React from 'react'; import {ErrorBoundary as ReactErrorBoundary} from 'react-error-boundary'; export type LexicalErrorBoundaryProps = { diff --git a/packages/lexical-react/src/shared/useCharacterLimit.ts b/packages/lexical-react/src/shared/useCharacterLimit.ts index ce22b19a7ba..aa8fce62324 100644 --- a/packages/lexical-react/src/shared/useCharacterLimit.ts +++ b/packages/lexical-react/src/shared/useCharacterLimit.ts @@ -62,7 +62,7 @@ export function useCharacterLimit( }, [editor]); useEffect(() => { - let text = editor.getEditorState().read($rootTextContent); + let text = editor.getEditorState().read($rootTextContent, {editor}); let lastComputedTextLength = 0; return mergeRegister( diff --git a/packages/lexical-table/src/LexicalTableNode.ts b/packages/lexical-table/src/LexicalTableNode.ts index 41b4d23c915..1485fc36509 100644 --- a/packages/lexical-table/src/LexicalTableNode.ts +++ b/packages/lexical-table/src/LexicalTableNode.ts @@ -40,6 +40,7 @@ import {TableDOMCell, TableDOMTable} from './LexicalTableObserver'; import {$isTableRowNode, type TableRowNode} from './LexicalTableRowNode'; import { $getNearestTableCellInTableFromDOMNode, + $getTableElement, getTable, isHTMLTableElement, } from './LexicalTableSelectionHelpers'; @@ -338,8 +339,7 @@ export class TableNode extends ElementNode { } updateDOM(prevNode: this, dom: HTMLElement, config: EditorConfig): boolean { - const slot = this.getDOMSlot(dom); - const tableElement = slot.element; + const tableElement = $getTableElement(this, dom); if ((dom === tableElement) === $isScrollableTablesActive()) { return true; } diff --git a/packages/lexical-table/src/LexicalTableSelectionHelpers.ts b/packages/lexical-table/src/LexicalTableSelectionHelpers.ts index e672e074dfd..6b6dc6a3f2a 100644 --- a/packages/lexical-table/src/LexicalTableSelectionHelpers.ts +++ b/packages/lexical-table/src/LexicalTableSelectionHelpers.ts @@ -46,6 +46,8 @@ import { $extendCaretToRange, $getAdjacentChildCaret, $getChildCaret, + $getEditor, + $getEditorDOMRenderConfig, $getNearestNodeFromDOMNode, $getPreviousSelection, $getSelection, @@ -120,15 +122,19 @@ export function isHTMLTableElement(el: unknown): el is HTMLTableElement { return isHTMLElement(el) && el.nodeName === 'TABLE'; } -export function getTableElement( +export function $getTableElement( tableNode: TableNode, dom: T, ): HTMLTableElementWithWithTableSelectionState | (T & null) { if (!dom) { return dom as T & null; } + const editor = $getEditor(); const element = ( - isHTMLTableElement(dom) ? dom : tableNode.getDOMSlot(dom).element + isHTMLTableElement(dom) + ? dom + : $getEditorDOMRenderConfig(editor).$getDOMSlot(tableNode, dom, editor) + .element ) as HTMLTableElementWithWithTableSelectionState; invariant( element.nodeName === 'TABLE', @@ -137,6 +143,8 @@ export function getTableElement( ); return element; } +/** @deprecated renamed to {@link $getTableElement} by @lexical/eslint-plugin rules-of-lexical */ +export const getTableElement = $getTableElement; export function getEditorWindow(editor: LexicalEditor): Window | null { return editor._window; @@ -176,7 +184,7 @@ const DELETE_KEY_COMMANDS = [ KEY_DELETE_COMMAND, ] as const; -export function applyTableHandlers( +export function $applyTableHandlers( tableNode: TableNode, element: HTMLElement, editor: LexicalEditor, @@ -191,7 +199,7 @@ export function applyTableHandlers( const tableObserver = new TableObserver(editor, tableNode.getKey()); - const tableElement = getTableElement(tableNode, element); + const tableElement = $getTableElement(tableNode, element); attachTableObserverToTableElement(tableElement, tableObserver); tableObserver.listenersToRemove.add(() => detachTableObserverFromTableElement(tableElement, tableObserver), @@ -1242,6 +1250,8 @@ export function applyTableHandlers( return tableObserver; } +/** @deprecated renamed to {@link $applyTableHandlers} by @lexical/eslint-plugin rules-of-lexical */ +export const applyTableHandlers = $applyTableHandlers; export type HTMLTableElementWithWithTableSelectionState = HTMLTableElement & { [LEXICAL_ELEMENT_KEY]?: TableObserver | undefined; @@ -1334,11 +1344,11 @@ export function doesTargetContainText(node: Node): boolean { return false; } -export function getTable( +export function $getTable( tableNode: TableNode, dom: HTMLElement, ): TableDOMTable { - const tableElement = getTableElement(tableNode, dom); + const tableElement = $getTableElement(tableNode, dom); const domRows: TableDOMRows = []; const grid = { columns: 0, @@ -1409,6 +1419,8 @@ export function getTable( return grid; } +/** @deprecated renamed to {@link $getTable} by @lexical/eslint-plugin rules-of-lexical */ +export const getTable = $getTable; export function $updateDOMForSelection( editor: LexicalEditor, @@ -2192,12 +2204,12 @@ function $handleArrowKey( } const anchorCellTable = $findTableNode(anchorCellNode); if (anchorCellTable !== tableNode && anchorCellTable != null) { - const anchorCellTableElement = getTableElement( + const anchorCellTableElement = $getTableElement( anchorCellTable, editor.getElementByKey(anchorCellTable.getKey()), ); if (anchorCellTableElement != null) { - tableObserver.table = getTable( + tableObserver.table = $getTable( anchorCellTable, anchorCellTableElement, ); @@ -2296,7 +2308,7 @@ function $handleArrowKey( $isTableNode(tableNodeFromSelection), '$handleArrowKey: TableSelection.getNodes()[0] expected to be TableNode', ); - const tableElement = getTableElement( + const tableElement = $getTableElement( tableNodeFromSelection, editor.getElementByKey(tableNodeFromSelection.getKey()), ); @@ -2310,7 +2322,7 @@ function $handleArrowKey( } tableObserver.$updateTableTableSelection(selection); - const grid = getTable(tableNodeFromSelection, tableElement); + const grid = $getTable(tableNodeFromSelection, tableElement); const cordsAnchor = tableNode.getCordsFromCellNode(anchorCellNode, grid); const anchorCell = tableNode.getDOMCellFromCordsOrThrow( cordsAnchor.x, @@ -2395,7 +2407,7 @@ function $getTableEdgeCursorPosition( } const domAnchorNode = domSelection.anchorNode; const tableNodeParentDOM = editor.getElementByKey(tableNodeParent.getKey()); - const tableElement = getTableElement( + const tableElement = $getTableElement( tableNode, editor.getElementByKey(tableNode.getKey()), ); diff --git a/packages/lexical-utils/src/markSelection.ts b/packages/lexical-utils/src/markSelection.ts index f1b7242d4e5..3959f84c838 100644 --- a/packages/lexical-utils/src/markSelection.ts +++ b/packages/lexical-utils/src/markSelection.ts @@ -7,6 +7,8 @@ */ import { + $getEditor, + $getEditorDOMRenderConfig, $getSelection, $isElementNode, $isRangeSelection, @@ -28,7 +30,7 @@ function $getOrderedSelectionPoints(selection: RangeSelection): [Point, Point] { return selection.isBackward() ? [points[1], points[0]] : points; } -function rangeTargetFromPoint( +function $rangeTargetFromPoint( point: Point, node: ElementNode | TextNode, dom: HTMLElement, @@ -37,12 +39,17 @@ function rangeTargetFromPoint( const textDOM = getDOMTextNode(dom) || dom; return [textDOM, point.offset]; } else { - const slot = node.getDOMSlot(dom); + const editor = $getEditor(); + const slot = $getEditorDOMRenderConfig(editor).$getDOMSlot( + node, + dom, + editor, + ); return [slot.element, slot.getFirstChildOffset() + point.offset]; } } -function rangeFromPoints( +function $rangeFromPoints( editor: LexicalEditor, start: Point, startNode: ElementNode | TextNode, @@ -53,8 +60,8 @@ function rangeFromPoints( ): Range { const editorDocument = editor._window ? editor._window.document : document; const range = editorDocument.createRange(); - range.setStart(...rangeTargetFromPoint(start, startNode, startDOM)); - range.setEnd(...rangeTargetFromPoint(end, endNode, endDOM)); + range.setStart(...$rangeTargetFromPoint(start, startNode, startDOM)); + range.setEnd(...$rangeTargetFromPoint(end, endNode, endDOM)); return range; } /** @@ -113,7 +120,7 @@ export default function markSelection( currentStartNodeDOM !== null && currentEndNodeDOM !== null ) { - const range = rangeFromPoints( + const range = $rangeFromPoints( editor, start, currentStartNode, diff --git a/packages/lexical/flow/Lexical.js.flow b/packages/lexical/flow/Lexical.js.flow index f560067ec3a..22ec65304f0 100644 --- a/packages/lexical/flow/Lexical.js.flow +++ b/packages/lexical/flow/Lexical.js.flow @@ -146,7 +146,29 @@ type DOMConversionCache = Map< Array<(node: Node) => DOMConversion | null>, >; +export type EditorDOMRenderConfig = { + /** @internal @experimental */ + createDOM: ( + node: T, + editor: LexicalEditor, + ) => HTMLElement; + /** @internal @experimental */ + exportDOM: ( + node: T, + editor: LexicalEditor, + ) => DOMExportOutput; + /** @internal @experimental */ + updateDOM: ( + nextNode: T, + prevNode: T, + dom: HTMLElement, + editor: LexicalEditor, + ) => boolean; +} + export type CreateEditorArgs = { + /** @internal @experimental */ + dom?: Partial; disableEvents?: boolean; editorState?: EditorState; namespace?: string; @@ -316,6 +338,23 @@ export type EditorConfig = { theme: EditorThemeClasses, namespace: string, disableEvents?: boolean, + /** @internal @experimental */ + createDOM?: ( + editor: LexicalEditor, + node: T, + ) => HTMLElement; + /** @internal @experimental */ + exportDOM?: ( + editor: LexicalEditor, + node: T, + ) => DOMExportOutput; + /** @internal @experimental */ + updateDOM?: ( + editor: LexicalEditor, + nextNode: T, + prevNode: T, + dom: HTMLElement, + ) => boolean; }; export type CommandListenerPriority = 0 | 1 | 2 | 3 | 4; export const COMMAND_PRIORITY_EDITOR = 0; @@ -1568,6 +1607,7 @@ export interface ExtensionRegisterState getOutput: () => Output; } export interface InitialEditorConfig { + dom?: CreateEditorArgs['dom']; disableEvents?: CreateEditorArgs['disableEvents']; parentEditor?: CreateEditorArgs['parentEditor']; namespace?: CreateEditorArgs['namespace']; diff --git a/packages/lexical/src/LexicalEditor.ts b/packages/lexical/src/LexicalEditor.ts index 4cabfd88575..53d7041d74e 100644 --- a/packages/lexical/src/LexicalEditor.ts +++ b/packages/lexical/src/LexicalEditor.ts @@ -14,10 +14,17 @@ import type { DOMExportOutputMap, NodeKey, } from './LexicalNode'; +import type {ElementDOMSlot} from './nodes/LexicalElementNode'; import invariant from 'shared/invariant'; -import {$getRoot, $getSelection, TextNode} from '.'; +import { + $getRoot, + $getSelection, + $isElementNode, + BaseSelection, + TextNode, +} from '.'; import {FULL_RECONCILE, NO_DIRTY_NODES} from './LexicalConstants'; import {cloneEditorState, createEmptyEditorState} from './LexicalEditorState'; import {addRootElementEvents, removeRootElementEvents} from './LexicalEvents'; @@ -43,6 +50,7 @@ import { getCachedTypeToNodeMap, getDefaultView, getDOMSelection, + getRegisteredNode, getStaticNodeConfig, hasOwnExportDOM, hasOwnStaticMethod, @@ -188,11 +196,12 @@ export type EditorThemeClasses = { [key: string]: any; }; -export type EditorConfig = { +export interface EditorConfig { + dom?: EditorDOMRenderConfig; disableEvents?: boolean; namespace: string; theme: EditorThemeClasses; -}; +} export type LexicalNodeReplacement = { replace: Klass; @@ -213,7 +222,54 @@ export type HTMLConfig = { */ export type LexicalNodeConfig = Klass | LexicalNodeReplacement; -export type CreateEditorArgs = { +/** @internal @experimental */ +export interface EditorDOMRenderConfig { + /** @internal @experimental */ + $createDOM: ( + node: T, + editor: LexicalEditor, + ) => HTMLElement; + /** @internal @experimental */ + $getDOMSlot: ( + node: T, + dom: HTMLElement, + editor: LexicalEditor, + ) => ElementDOMSlot; + /** @internal @experimental */ + $exportDOM: ( + node: T, + editor: LexicalEditor, + ) => DOMExportOutput; + /** @internal @experimental */ + $extractWithChild: ( + node: T, + childNode: LexicalNode, + selection: null | BaseSelection, + destination: 'clone' | 'html', + editor: LexicalEditor, + ) => boolean; + /** @internal @experimental */ + $updateDOM: ( + nextNode: T, + prevNode: T, + dom: HTMLElement, + editor: LexicalEditor, + ) => boolean; + /** @internal @experimental */ + $shouldInclude: ( + node: T, + selection: null | BaseSelection, + editor: LexicalEditor, + ) => boolean; + /** @internal @experimental */ + $shouldExclude: ( + node: T, + selection: null | BaseSelection, + editor: LexicalEditor, + ) => boolean; +} + +export interface CreateEditorArgs { disableEvents?: boolean; editorState?: EditorState; namespace?: string; @@ -223,7 +279,8 @@ export type CreateEditorArgs = { editable?: boolean; theme?: EditorThemeClasses; html?: HTMLConfig; -}; + dom?: Partial; +} export type RegisteredNodes = Map; @@ -528,6 +585,36 @@ export function getTransformSetFromKlass( return transforms; } +/** @internal */ +export const DEFAULT_EDITOR_DOM_CONFIG: EditorDOMRenderConfig = { + $createDOM: (node, editor) => node.createDOM(editor._config, editor), + $exportDOM: (node, editor) => { + const registeredNode = getRegisteredNode(editor, node.getType()); + // Use HTMLConfig overrides, if available. + return registeredNode && registeredNode.exportDOM !== undefined + ? registeredNode.exportDOM(editor, node) + : node.exportDOM(editor); + }, + $extractWithChild: (node, childNode, selection, destination, _editor) => + $isElementNode(node) && + node.extractWithChild(childNode, selection, destination), + $getDOMSlot: (node, dom, _editor) => { + invariant( + $isElementNode(node), + '$getDOMSlot called on a non-ElementNode (key %s type %s)', + node.getKey(), + node.getType(), + ); + return node.getDOMSlot(dom); + }, + $shouldExclude: (node, _selection, _editor) => + $isElementNode(node) && node.excludeFromCopy('html'), + $shouldInclude: (node, selection, _editor) => + selection ? node.isSelected(selection) : true, + $updateDOM: (nextNode, prevNode, dom, editor) => + nextNode.updateDOM(prevNode, dom, editor._config), +}; + /** * Creates a new LexicalEditor attached to a single contentEditable (provided in the config). This is * the lowest-level initialization API for a LexicalEditor. If you're using React or another framework, @@ -644,6 +731,10 @@ export function createEditor(editorConfig?: CreateEditorArgs): LexicalEditor { registeredNodes, { disableEvents, + dom: { + ...DEFAULT_EDITOR_DOM_CONFIG, + ...(editorConfig && editorConfig.dom), + }, namespace, theme, }, diff --git a/packages/lexical/src/LexicalNode.ts b/packages/lexical/src/LexicalNode.ts index 8d5ea7e143f..c9b84504e0d 100644 --- a/packages/lexical/src/LexicalNode.ts +++ b/packages/lexical/src/LexicalNode.ts @@ -373,12 +373,14 @@ export type DOMExportOutputMap = Map< (editor: LexicalEditor, target: LexicalNode) => DOMExportOutput >; -export type DOMExportOutput = { +export interface DOMExportOutput { after?: ( generatedElement: HTMLElement | DocumentFragment | Text | null | undefined, ) => HTMLElement | DocumentFragment | Text | null | undefined; element: HTMLElement | DocumentFragment | Text | null; -}; + append?: (element: HTMLElement | DocumentFragment | Text) => void; + $getChildNodes?: () => Iterable; +} export type NodeKey = string; @@ -1534,3 +1536,9 @@ export function insertRangeAfter( currentNode = currentNode.insertAfter(nodeToInsert); } } + +export function $isLexicalNode( + node: null | undefined | LexicalNode, +): node is LexicalNode { + return node instanceof LexicalNode; +} diff --git a/packages/lexical/src/LexicalNodeState.ts b/packages/lexical/src/LexicalNodeState.ts index eca7bc6ca03..57466088884 100644 --- a/packages/lexical/src/LexicalNodeState.ts +++ b/packages/lexical/src/LexicalNodeState.ts @@ -229,7 +229,7 @@ export interface StateValueConfig { * The return value of {@link createState}, for use with * {@link $getState} and {@link $setState}. */ -export class StateConfig { +export class StateConfig { /** The string key used when serializing this state to JSON */ readonly key: K; /** The parse function from the StateValueConfig passed to createState */ @@ -251,6 +251,7 @@ export class StateConfig { * the `defaultValue`, it will not be serialized to JSON. */ readonly defaultValue: V; + constructor(key: K, stateValueConfig: StateValueConfig) { this.key = key; this.parse = stateValueConfig.parse.bind(stateValueConfig); @@ -295,7 +296,7 @@ export type AnyStateConfig = StateConfig; * * @__NO_SIDE_EFFECTS__ */ -export function createState( +export function createState( key: K, valueConfig: StateValueConfig, ): StateConfig { @@ -885,7 +886,7 @@ function computeSize( */ function undefinedIfEmpty(obj: undefined | T): undefined | T { if (obj) { - for (const key in obj) { + for (const _key in obj) { return obj; } } diff --git a/packages/lexical/src/LexicalReconciler.ts b/packages/lexical/src/LexicalReconciler.ts index 02277c4cd63..1348dbdf173 100644 --- a/packages/lexical/src/LexicalReconciler.ts +++ b/packages/lexical/src/LexicalReconciler.ts @@ -8,6 +8,7 @@ import type { EditorConfig, + EditorDOMRenderConfig, LexicalEditor, MutatedNodes, MutationListeners, @@ -24,6 +25,7 @@ import { $isLineBreakNode, $isRootNode, $isTextNode, + DEFAULT_EDITOR_DOM_CONFIG, } from '.'; import { DOUBLE_LINE_BREAK, @@ -62,6 +64,7 @@ let activePrevNodeMap: NodeMap; let activeNextNodeMap: NodeMap; let activePrevKeyToDOMMap: Map; let mutatedNodes: MutatedNodes; +let activeEditorDOMRenderConfig: EditorDOMRenderConfig; function destroyNode(key: NodeKey, parentDOM: null | HTMLElement): void { const node = activePrevNodeMap.get(key); @@ -193,7 +196,7 @@ function $createNode(key: NodeKey, slot: ElementDOMSlot | null): HTMLElement { if (node === undefined) { invariant(false, 'createNode: node does not exist in nodeMap'); } - const dom = node.createDOM(activeEditorConfig, activeEditor); + const dom = activeEditorDOMRenderConfig.$createDOM(node, activeEditor); storeDOMWithKey(key, dom, activeEditor); // This helps preserve the text, and stops spell check tools from @@ -215,7 +218,13 @@ function $createNode(key: NodeKey, slot: ElementDOMSlot | null): HTMLElement { if (childrenSize !== 0) { const endIndex = childrenSize - 1; const children = createChildrenArray(node, activeNextNodeMap); - $createChildren(children, node, 0, endIndex, node.getDOMSlot(dom)); + $createChildren( + children, + node, + 0, + endIndex, + activeEditorDOMRenderConfig.$getDOMSlot(node, dom, activeEditor), + ); } const format = node.__format; @@ -223,7 +232,7 @@ function $createNode(key: NodeKey, slot: ElementDOMSlot | null): HTMLElement { setElementFormat(dom, format); } if (!node.isInline()) { - reconcileElementTerminatingLineBreak(null, node, dom); + $reconcileElementTerminatingLineBreak(null, node, dom); } if ($textContentRequiresDoubleLinebreakAtEnd(node)) { subTreeTextContent += DOUBLE_LINE_BREAK; @@ -318,7 +327,7 @@ function isLastChildLineBreakOrDecorator( } // If we end an element with a LineBreakNode, then we need to add an additional
-function reconcileElementTerminatingLineBreak( +function $reconcileElementTerminatingLineBreak( prevElement: null | ElementNode, nextElement: ElementNode, dom: HTMLElement & LexicalPrivateDOM, @@ -332,7 +341,9 @@ function reconcileElementTerminatingLineBreak( activeNextNodeMap, ); if (prevLineBreak !== nextLineBreak) { - nextElement.getDOMSlot(dom).setManagedLineBreak(nextLineBreak); + activeEditorDOMRenderConfig + .$getDOMSlot(nextElement, dom, activeEditor) + .setManagedLineBreak(nextLineBreak); } } @@ -363,7 +374,11 @@ function $reconcileChildrenWithDirection( ): void { subTreeTextFormat = null; subTreeTextStyle = ''; - $reconcileChildren(prevElement, nextElement, nextElement.getDOMSlot(dom)); + $reconcileChildren( + prevElement, + nextElement, + activeEditorDOMRenderConfig.$getDOMSlot(nextElement, dom, activeEditor), + ); reconcileTextFormat(nextElement); reconcileTextStyle(nextElement); } @@ -547,7 +562,14 @@ function $reconcileNode( } // Update node. If it returns true, we need to unmount and re-create the node - if (nextNode.updateDOM(prevNode, dom, activeEditorConfig)) { + if ( + activeEditorDOMRenderConfig.$updateDOM( + nextNode, + prevNode, + dom, + activeEditor, + ) + ) { const replacementDOM = $createNode(key, null); if (parentDOM === null) { @@ -574,7 +596,7 @@ function $reconcileNode( if (isDirty) { $reconcileChildrenWithDirection(prevNode, nextNode, dom); if (!$isRootNode(nextNode) && !nextNode.isInline()) { - reconcileElementTerminatingLineBreak(prevNode, nextNode, dom); + $reconcileElementTerminatingLineBreak(prevNode, nextNode, dom); } } @@ -773,6 +795,7 @@ export function $reconcileRoot( treatAllNodesAsDirty = dirtyType === FULL_RECONCILE; activeEditor = editor; activeEditorConfig = editor._config; + activeEditorDOMRenderConfig = editor._config.dom || DEFAULT_EDITOR_DOM_CONFIG; activeEditorNodes = editor._nodes; activeMutationListeners = activeEditor._listeners.mutation; activeDirtyElements = dirtyElements; @@ -808,6 +831,7 @@ export function $reconcileRoot( activePrevKeyToDOMMap = undefined; // @ts-ignore mutatedNodes = undefined; + activeEditorDOMRenderConfig = DEFAULT_EDITOR_DOM_CONFIG; return currentMutatedNodes; } diff --git a/packages/lexical/src/LexicalSelection.ts b/packages/lexical/src/LexicalSelection.ts index 001a105408b..74a98e667ad 100644 --- a/packages/lexical/src/LexicalSelection.ts +++ b/packages/lexical/src/LexicalSelection.ts @@ -70,6 +70,7 @@ import {SKIP_SELECTION_FOCUS_TAG} from './LexicalUpdateTags'; import { $findMatchingParent, $getCompositionKey, + $getEditorDOMRenderConfig, $getNearestRootOrShadowRoot, $getNodeByKey, $getNodeFromDOM, @@ -2301,7 +2302,11 @@ function $internalResolveSelectionPoint( elementDOM !== null, '$internalResolveSelectionPoint: node in DOM but not keyToDOMMap', ); - const slot = resolvedElement.getDOMSlot(elementDOM); + const slot = $getEditorDOMRenderConfig(editor).$getDOMSlot( + resolvedElement, + elementDOM, + editor, + ); [resolvedElement, resolvedOffset] = slot.resolveChildIndex( resolvedElement, elementDOM, diff --git a/packages/lexical/src/LexicalUpdates.ts b/packages/lexical/src/LexicalUpdates.ts index 7901b9c3cea..d23235ac610 100644 --- a/packages/lexical/src/LexicalUpdates.ts +++ b/packages/lexical/src/LexicalUpdates.ts @@ -120,7 +120,8 @@ export function getActiveEditor(): LexicalEditor { 'Unable to find an active editor. ' + 'This method can only be used ' + 'synchronously during the callback of ' + - 'editor.update() or editor.read().%s', + 'editor.update(), editor.read(), or ' + + 'editor.getEditorState().read(..., {editor}).%s', collectBuildInformation(), ); } diff --git a/packages/lexical/src/LexicalUtils.ts b/packages/lexical/src/LexicalUtils.ts index 5e339c98814..5c28db7db76 100644 --- a/packages/lexical/src/LexicalUtils.ts +++ b/packages/lexical/src/LexicalUtils.ts @@ -9,6 +9,7 @@ import type { CommandPayloadType, EditorConfig, + EditorDOMRenderConfig, EditorThemeClasses, Klass, LexicalCommand, @@ -45,6 +46,7 @@ import { $isTabNode, $isTextNode, DecoratorNode, + DEFAULT_EDITOR_DOM_CONFIG, ElementNode, HISTORY_MERGE_TAG, LineBreakNode, @@ -1815,7 +1817,9 @@ export function isDocumentFragment(x: unknown): x is DocumentFragment { * @param node - the Dom Node to check * @returns if the Dom Node is an inline node */ -export function isInlineDomNode(node: Node) { +export function isInlineDomNode( + node: Node, +): node is (HTMLElement | Text) & {[InlineDOMBrand]: never} { const inlineNodes = new RegExp( /^(a|abbr|acronym|b|cite|code|del|em|i|ins|kbd|label|mark|output|q|ruby|s|samp|span|strong|sub|sup|time|u|tt|var|#text)$/, 'i', @@ -1823,12 +1827,17 @@ export function isInlineDomNode(node: Node) { return node.nodeName.match(inlineNodes) !== null; } +const BlockDOMBrand = Symbol.for('@lexical/BlockDOMBrand'); +const InlineDOMBrand = Symbol.for('@lexical/InlineDOMBrand'); + /** * * @param node - the Dom Node to check * @returns if the Dom Node is a block node */ -export function isBlockDomNode(node: Node) { +export function isBlockDomNode( + node: Node, +): node is HTMLElement & {[BlockDOMBrand]: never} { const blockNodes = new RegExp( /^(address|article|aside|blockquote|canvas|dd|div|dl|dt|fieldset|figcaption|figure|footer|form|h1|h2|h3|h4|h5|h6|header|hr|li|main|nav|noscript|ol|p|pre|section|table|td|tfoot|ul|video)$/, 'i', @@ -1877,6 +1886,15 @@ export function $getEditor(): LexicalEditor { return getActiveEditor(); } +/** + * @internal @experimental + */ +export function $getEditorDOMRenderConfig( + editor: LexicalEditor = $getEditor(), +): EditorDOMRenderConfig { + return editor._config.dom || DEFAULT_EDITOR_DOM_CONFIG; +} + /** @internal */ export type TypeToNodeMap = Map; /** diff --git a/packages/lexical/src/extension-core/types.ts b/packages/lexical/src/extension-core/types.ts index cf1cd9b7c03..cdb36b78ee1 100644 --- a/packages/lexical/src/extension-core/types.ts +++ b/packages/lexical/src/extension-core/types.ts @@ -352,6 +352,10 @@ export interface InitialEditorConfig { * @internal Disable root element events (for internal Meta use) */ disableEvents?: CreateEditorArgs['disableEvents']; + /** + * @internal @experimental + */ + dom?: CreateEditorArgs['dom']; /** * Used when this editor is nested inside of another editor */ diff --git a/packages/lexical/src/index.ts b/packages/lexical/src/index.ts index 3c751e64738..b8308fb8875 100644 --- a/packages/lexical/src/index.ts +++ b/packages/lexical/src/index.ts @@ -135,6 +135,7 @@ export type { CreateEditorArgs, EditableListener, EditorConfig, + EditorDOMRenderConfig, EditorSetOptions, EditorThemeClasses, EditorThemeClassName, @@ -162,6 +163,7 @@ export { COMMAND_PRIORITY_LOW, COMMAND_PRIORITY_NORMAL, createEditor, + DEFAULT_EDITOR_DOM_CONFIG, getTransformSetFromKlass, } from './LexicalEditor'; export type { @@ -193,7 +195,7 @@ export type { StaticNodeConfigRecord, StaticNodeConfigValue, } from './LexicalNode'; -export {buildImportMap} from './LexicalNode'; +export {$isLexicalNode, buildImportMap} from './LexicalNode'; export { $getState, $getStateChange, @@ -245,6 +247,7 @@ export { $findMatchingParent, $getAdjacentNode, $getEditor, + $getEditorDOMRenderConfig, $getNearestNodeFromDOMNode, $getNearestRootOrShadowRoot, $getNodeByKey, diff --git a/packages/lexical/src/nodes/LexicalElementNode.ts b/packages/lexical/src/nodes/LexicalElementNode.ts index 7f6d1e5eddd..a29edb55ee7 100644 --- a/packages/lexical/src/nodes/LexicalElementNode.ts +++ b/packages/lexical/src/nodes/LexicalElementNode.ts @@ -44,6 +44,7 @@ import { } from '../LexicalSelection'; import {errorOnReadOnly, getActiveEditor} from '../LexicalUpdates'; import { + $getEditorDOMRenderConfig, $getNodeByKey, $isRootOrShadowRoot, isHTMLElement, @@ -657,7 +658,7 @@ export class ElementNode extends LexicalNode { } setFormat(type: ElementFormatType): this { const self = this.getWritable(); - self.__format = type !== '' ? ELEMENT_TYPE_TO_FORMAT[type] : 0; + self.__format = type !== '' ? ELEMENT_TYPE_TO_FORMAT[type] || 0 : 0; return this; } setStyle(style: string): this { @@ -972,7 +973,11 @@ export class ElementNode extends LexicalNode { /** @internal */ reconcileObservedMutation(dom: HTMLElement, editor: LexicalEditor): void { - const slot = this.getDOMSlot(dom); + const slot = $getEditorDOMRenderConfig(editor).$getDOMSlot( + this, + dom, + editor, + ); let currentDOM = slot.getFirstChild(); for ( let currentNode = this.getFirstChild();