commit 30775d5a01fe08f26af5957aa82e83070927c7d0 Author: Giuliano Silvestro Date: Sun Feb 15 23:33:25 2026 +1000 feat: initial rich-text-elements-ui library implementation TipTap-powered rich text editing library with WYSIWYG editor, markdown editor, template editor, collaboration support (Yjs), mentions, track changes, comments, code blocks, and table insertion. Co-Authored-By: Claude Opus 4.6 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6006445 --- /dev/null +++ b/.gitignore @@ -0,0 +1,21 @@ +# Dependencies +node_modules/ + +# Build output +dist/ + +# Angular cache +.angular/ + +# IDE +.idea/ +.vscode/ +*.swp +*.swo + +# OS +.DS_Store +Thumbs.db + +# Misc +*.log diff --git a/build-for-dev.sh b/build-for-dev.sh new file mode 100755 index 0000000..9070855 --- /dev/null +++ b/build-for-dev.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Build the library +npm run build + +# Link it locally for development +cd dist +npm link + +echo "Library built and linked successfully" +echo "Run 'npm link @sda/rich-text-elements-ui' in your consuming app" diff --git a/ng-package.json b/ng-package.json new file mode 100644 index 0000000..a35fd6f --- /dev/null +++ b/ng-package.json @@ -0,0 +1,40 @@ +{ + "$schema": "node_modules/ng-packagr/ng-package.schema.json", + "lib": { + "entryFile": "src/index.ts", + "styleIncludePaths": ["src/styles"] + }, + "dest": "dist", + "deleteDestPath": true, + "assets": [ + "src/styles/**/*.scss" + ], + "allowedNonPeerDependencies": [ + "@tiptap/core", + "@tiptap/starter-kit", + "@tiptap/pm", + "@tiptap/extension-placeholder", + "@tiptap/extension-character-count", + "@tiptap/extension-color", + "@tiptap/extension-text-style", + "@tiptap/extension-highlight", + "@tiptap/extension-link", + "@tiptap/extension-image", + "@tiptap/extension-table", + "@tiptap/extension-table-row", + "@tiptap/extension-table-cell", + "@tiptap/extension-table-header", + "@tiptap/extension-task-list", + "@tiptap/extension-task-item", + "@tiptap/extension-underline", + "@tiptap/extension-text-align", + "@tiptap/extension-subscript", + "@tiptap/extension-superscript", + "@tiptap/extension-mention", + "@tiptap/extension-code-block-lowlight", + "@tiptap/extension-bubble-menu", + "@tiptap/extension-floating-menu", + "lowlight", + "marked" + ] +} diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..2cb802e --- /dev/null +++ b/package-lock.json @@ -0,0 +1,4941 @@ +{ + "name": "@sda/rich-text-elements-ui", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "@sda/rich-text-elements-ui", + "version": "0.1.0", + "license": "MIT", + "dependencies": { + "@tiptap/core": "^2.6.0", + "@tiptap/extension-bubble-menu": "^2.6.0", + "@tiptap/extension-character-count": "^2.6.0", + "@tiptap/extension-code-block-lowlight": "^2.6.0", + "@tiptap/extension-color": "^2.6.0", + "@tiptap/extension-floating-menu": "^2.6.0", + "@tiptap/extension-highlight": "^2.6.0", + "@tiptap/extension-image": "^2.6.0", + "@tiptap/extension-link": "^2.6.0", + "@tiptap/extension-mention": "^2.6.0", + "@tiptap/extension-placeholder": "^2.6.0", + "@tiptap/extension-subscript": "^2.6.0", + "@tiptap/extension-superscript": "^2.6.0", + "@tiptap/extension-table": "^2.6.0", + "@tiptap/extension-table-cell": "^2.6.0", + "@tiptap/extension-table-header": "^2.6.0", + "@tiptap/extension-table-row": "^2.6.0", + "@tiptap/extension-task-item": "^2.6.0", + "@tiptap/extension-task-list": "^2.6.0", + "@tiptap/extension-text-align": "^2.6.0", + "@tiptap/extension-text-style": "^2.6.0", + "@tiptap/extension-underline": "^2.6.0", + "@tiptap/pm": "^2.6.0", + "@tiptap/starter-kit": "^2.6.0", + "lowlight": "^3.1.0", + "marked": "^12.0.0" + }, + "devDependencies": { + "@angular/common": "^19.1.0", + "@angular/compiler": "^19.1.0", + "@angular/compiler-cli": "^19.1.0", + "@angular/core": "^19.1.0", + "@angular/forms": "^19.2.18", + "ng-packagr": "^19.1.0", + "typescript": "~5.7.2" + }, + "optionalDependencies": { + "@tiptap/extension-collaboration": "^2.6.0", + "@tiptap/extension-collaboration-cursor": "^2.6.0", + "y-prosemirror": "^1.2.0", + "yjs": "^13.6.0" + }, + "peerDependencies": { + "@angular/common": "^19.0.0", + "@angular/core": "^19.0.0", + "@sda/base-ui": "*" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@angular/common": { + "version": "19.2.18", + "resolved": "https://registry.npmjs.org/@angular/common/-/common-19.2.18.tgz", + "integrity": "sha512-CrV02Omzw/QtfjlEVXVPJVXipdx83NuA+qSASZYrxrhKFusUZyK3P/Zznqg+wiAeNDbedQwMUVqoAARHf0xQrw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0" + }, + "peerDependencies": { + "@angular/core": "19.2.18", + "rxjs": "^6.5.3 || ^7.4.0" + } + }, + "node_modules/@angular/compiler": { + "version": "19.2.18", + "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-19.2.18.tgz", + "integrity": "sha512-3MscvODxRVxc3Cs0ZlHI5Pk5rEvE80otfvxZTMksOZuPlv1B+S8MjWfc3X3jk9SbyUEzODBEH55iCaBHD48V3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0" + } + }, + "node_modules/@angular/compiler-cli": { + "version": "19.2.18", + "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-19.2.18.tgz", + "integrity": "sha512-N4TMtLfImJIoMaRL6mx7885UBeQidywptHH6ACZj71Ar6++DBc1mMlcwuvbeJCd3r3y8MQ5nLv5PZSN/tHr13w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "7.26.9", + "@jridgewell/sourcemap-codec": "^1.4.14", + "chokidar": "^4.0.0", + "convert-source-map": "^1.5.1", + "reflect-metadata": "^0.2.0", + "semver": "^7.0.0", + "tslib": "^2.3.0", + "yargs": "^17.2.1" + }, + "bin": { + "ng-xi18n": "bundles/src/bin/ng_xi18n.js", + "ngc": "bundles/src/bin/ngc.js", + "ngcc": "bundles/ngcc/index.js" + }, + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0" + }, + "peerDependencies": { + "@angular/compiler": "19.2.18", + "typescript": ">=5.5 <5.9" + } + }, + "node_modules/@angular/core": { + "version": "19.2.18", + "resolved": "https://registry.npmjs.org/@angular/core/-/core-19.2.18.tgz", + "integrity": "sha512-+QRrf0Igt8ccUWXHA+7doK5W6ODyhHdqVyblSlcQ8OciwkzIIGGEYNZom5OZyWMh+oI54lcSeyV2O3xaDepSrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0" + }, + "peerDependencies": { + "rxjs": "^6.5.3 || ^7.4.0", + "zone.js": "~0.15.0" + } + }, + "node_modules/@angular/forms": { + "version": "19.2.18", + "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-19.2.18.tgz", + "integrity": "sha512-pe40934jWhoS7DyGl7jyZdoj1gvBgur2t1zrJD+csEkTitYnW14+La2Pv6SW1pNX5nIzFsgsS9Nex1KcH5S6Tw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0" + }, + "peerDependencies": { + "@angular/common": "19.2.18", + "@angular/core": "19.2.18", + "@angular/platform-browser": "19.2.18", + "rxjs": "^6.5.3 || ^7.4.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", + "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.28.5", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.0.tgz", + "integrity": "sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.26.9", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.9.tgz", + "integrity": "sha512-lWBYIrF7qK5+GjY5Uy+/hEgp8OJWOD/rpy74GplYRhEauvbHDeFB8t5hPOZxCZ0Oxf4Cc36tK51/l3ymJysrKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.26.2", + "@babel/generator": "^7.26.9", + "@babel/helper-compilation-targets": "^7.26.5", + "@babel/helper-module-transforms": "^7.26.0", + "@babel/helpers": "^7.26.9", + "@babel/parser": "^7.26.9", + "@babel/template": "^7.26.9", + "@babel/traverse": "^7.26.9", + "@babel/types": "^7.26.9", + "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/core/node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/generator": { + "version": "7.29.1", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz", + "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.0", + "@babel/types": "^7.29.0", + "@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.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", + "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.28.6", + "@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-compilation-targets/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", + "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", + "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.28.6", + "@babel/helper-validator-identifier": "^7.28.5", + "@babel/traverse": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.6.tgz", + "integrity": "sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.0.tgz", + "integrity": "sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/template": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", + "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.28.6", + "@babel/parser": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz", + "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", + "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", + "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz", + "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz", + "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz", + "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz", + "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz", + "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz", + "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz", + "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz", + "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz", + "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz", + "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz", + "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz", + "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz", + "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz", + "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz", + "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz", + "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz", + "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz", + "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz", + "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz", + "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz", + "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz", + "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz", + "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz", + "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz", + "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@napi-rs/nice": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice/-/nice-1.1.1.tgz", + "integrity": "sha512-xJIPs+bYuc9ASBl+cvGsKbGrJmS6fAKaSZCnT0lhahT5rhA2VVy9/EcIgd2JhtEuFOJNx7UHNn/qiTPTY4nrQw==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "optionalDependencies": { + "@napi-rs/nice-android-arm-eabi": "1.1.1", + "@napi-rs/nice-android-arm64": "1.1.1", + "@napi-rs/nice-darwin-arm64": "1.1.1", + "@napi-rs/nice-darwin-x64": "1.1.1", + "@napi-rs/nice-freebsd-x64": "1.1.1", + "@napi-rs/nice-linux-arm-gnueabihf": "1.1.1", + "@napi-rs/nice-linux-arm64-gnu": "1.1.1", + "@napi-rs/nice-linux-arm64-musl": "1.1.1", + "@napi-rs/nice-linux-ppc64-gnu": "1.1.1", + "@napi-rs/nice-linux-riscv64-gnu": "1.1.1", + "@napi-rs/nice-linux-s390x-gnu": "1.1.1", + "@napi-rs/nice-linux-x64-gnu": "1.1.1", + "@napi-rs/nice-linux-x64-musl": "1.1.1", + "@napi-rs/nice-openharmony-arm64": "1.1.1", + "@napi-rs/nice-win32-arm64-msvc": "1.1.1", + "@napi-rs/nice-win32-ia32-msvc": "1.1.1", + "@napi-rs/nice-win32-x64-msvc": "1.1.1" + } + }, + "node_modules/@napi-rs/nice-android-arm-eabi": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-android-arm-eabi/-/nice-android-arm-eabi-1.1.1.tgz", + "integrity": "sha512-kjirL3N6TnRPv5iuHw36wnucNqXAO46dzK9oPb0wj076R5Xm8PfUVA9nAFB5ZNMmfJQJVKACAPd/Z2KYMppthw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-android-arm64": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-android-arm64/-/nice-android-arm64-1.1.1.tgz", + "integrity": "sha512-blG0i7dXgbInN5urONoUCNf+DUEAavRffrO7fZSeoRMJc5qD+BJeNcpr54msPF6qfDD6kzs9AQJogZvT2KD5nw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-darwin-arm64": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-darwin-arm64/-/nice-darwin-arm64-1.1.1.tgz", + "integrity": "sha512-s/E7w45NaLqTGuOjC2p96pct4jRfo61xb9bU1unM/MJ/RFkKlJyJDx7OJI/O0ll/hrfpqKopuAFDV8yo0hfT7A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-darwin-x64": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-darwin-x64/-/nice-darwin-x64-1.1.1.tgz", + "integrity": "sha512-dGoEBnVpsdcC+oHHmW1LRK5eiyzLwdgNQq3BmZIav+9/5WTZwBYX7r5ZkQC07Nxd3KHOCkgbHSh4wPkH1N1LiQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-freebsd-x64": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-freebsd-x64/-/nice-freebsd-x64-1.1.1.tgz", + "integrity": "sha512-kHv4kEHAylMYmlNwcQcDtXjklYp4FCf0b05E+0h6nDHsZ+F0bDe04U/tXNOqrx5CmIAth4vwfkjjUmp4c4JktQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-linux-arm-gnueabihf": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-arm-gnueabihf/-/nice-linux-arm-gnueabihf-1.1.1.tgz", + "integrity": "sha512-E1t7K0efyKXZDoZg1LzCOLxgolxV58HCkaEkEvIYQx12ht2pa8hoBo+4OB3qh7e+QiBlp1SRf+voWUZFxyhyqg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-linux-arm64-gnu": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-arm64-gnu/-/nice-linux-arm64-gnu-1.1.1.tgz", + "integrity": "sha512-CIKLA12DTIZlmTaaKhQP88R3Xao+gyJxNWEn04wZwC2wmRapNnxCUZkVwggInMJvtVElA+D4ZzOU5sX4jV+SmQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-linux-arm64-musl": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-arm64-musl/-/nice-linux-arm64-musl-1.1.1.tgz", + "integrity": "sha512-+2Rzdb3nTIYZ0YJF43qf2twhqOCkiSrHx2Pg6DJaCPYhhaxbLcdlV8hCRMHghQ+EtZQWGNcS2xF4KxBhSGeutg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-linux-ppc64-gnu": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-ppc64-gnu/-/nice-linux-ppc64-gnu-1.1.1.tgz", + "integrity": "sha512-4FS8oc0GeHpwvv4tKciKkw3Y4jKsL7FRhaOeiPei0X9T4Jd619wHNe4xCLmN2EMgZoeGg+Q7GY7BsvwKpL22Tg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-linux-riscv64-gnu": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-riscv64-gnu/-/nice-linux-riscv64-gnu-1.1.1.tgz", + "integrity": "sha512-HU0nw9uD4FO/oGCCk409tCi5IzIZpH2agE6nN4fqpwVlCn5BOq0MS1dXGjXaG17JaAvrlpV5ZeyZwSon10XOXw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-linux-s390x-gnu": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-s390x-gnu/-/nice-linux-s390x-gnu-1.1.1.tgz", + "integrity": "sha512-2YqKJWWl24EwrX0DzCQgPLKQBxYDdBxOHot1KWEq7aY2uYeX+Uvtv4I8xFVVygJDgf6/92h9N3Y43WPx8+PAgQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-linux-x64-gnu": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-x64-gnu/-/nice-linux-x64-gnu-1.1.1.tgz", + "integrity": "sha512-/gaNz3R92t+dcrfCw/96pDopcmec7oCcAQ3l/M+Zxr82KT4DljD37CpgrnXV+pJC263JkW572pdbP3hP+KjcIg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-linux-x64-musl": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-x64-musl/-/nice-linux-x64-musl-1.1.1.tgz", + "integrity": "sha512-xScCGnyj/oppsNPMnevsBe3pvNaoK7FGvMjT35riz9YdhB2WtTG47ZlbxtOLpjeO9SqqQ2J2igCmz6IJOD5JYw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-openharmony-arm64": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-openharmony-arm64/-/nice-openharmony-arm64-1.1.1.tgz", + "integrity": "sha512-6uJPRVwVCLDeoOaNyeiW0gp2kFIM4r7PL2MczdZQHkFi9gVlgm+Vn+V6nTWRcu856mJ2WjYJiumEajfSm7arPQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-win32-arm64-msvc": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-win32-arm64-msvc/-/nice-win32-arm64-msvc-1.1.1.tgz", + "integrity": "sha512-uoTb4eAvM5B2aj/z8j+Nv8OttPf2m+HVx3UjA5jcFxASvNhQriyCQF1OB1lHL43ZhW+VwZlgvjmP5qF3+59atA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-win32-ia32-msvc": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-win32-ia32-msvc/-/nice-win32-ia32-msvc-1.1.1.tgz", + "integrity": "sha512-CNQqlQT9MwuCsg1Vd/oKXiuH+TcsSPJmlAFc5frFyX/KkOh0UpBLEj7aoY656d5UKZQMQFP7vJNa1DNUNORvug==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-win32-x64-msvc": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-win32-x64-msvc/-/nice-win32-x64-msvc-1.1.1.tgz", + "integrity": "sha512-vB+4G/jBQCAh0jelMTY3+kgFy00Hlx2f2/1zjMoH821IbplbWZOkLiTYXQkygNTzQJTq5cvwBDgn2ppHD+bglQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@parcel/watcher": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.6.tgz", + "integrity": "sha512-tmmZ3lQxAe/k/+rNnXQRawJ4NjxO2hqiOLTHvWchtGZULp4RyFeh6aU4XdOYBFe2KE1oShQTv4AblOs2iOrNnQ==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "dependencies": { + "detect-libc": "^2.0.3", + "is-glob": "^4.0.3", + "node-addon-api": "^7.0.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "@parcel/watcher-android-arm64": "2.5.6", + "@parcel/watcher-darwin-arm64": "2.5.6", + "@parcel/watcher-darwin-x64": "2.5.6", + "@parcel/watcher-freebsd-x64": "2.5.6", + "@parcel/watcher-linux-arm-glibc": "2.5.6", + "@parcel/watcher-linux-arm-musl": "2.5.6", + "@parcel/watcher-linux-arm64-glibc": "2.5.6", + "@parcel/watcher-linux-arm64-musl": "2.5.6", + "@parcel/watcher-linux-x64-glibc": "2.5.6", + "@parcel/watcher-linux-x64-musl": "2.5.6", + "@parcel/watcher-win32-arm64": "2.5.6", + "@parcel/watcher-win32-ia32": "2.5.6", + "@parcel/watcher-win32-x64": "2.5.6" + } + }, + "node_modules/@parcel/watcher-android-arm64": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.6.tgz", + "integrity": "sha512-YQxSS34tPF/6ZG7r/Ih9xy+kP/WwediEUsqmtf0cuCV5TPPKw/PQHRhueUo6JdeFJaqV3pyjm0GdYjZotbRt/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-arm64": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.6.tgz", + "integrity": "sha512-Z2ZdrnwyXvvvdtRHLmM4knydIdU9adO3D4n/0cVipF3rRiwP+3/sfzpAwA/qKFL6i1ModaabkU7IbpeMBgiVEA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-x64": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.6.tgz", + "integrity": "sha512-HgvOf3W9dhithcwOWX9uDZyn1lW9R+7tPZ4sug+NGrGIo4Rk1hAXLEbcH1TQSqxts0NYXXlOWqVpvS1SFS4fRg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-freebsd-x64": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.6.tgz", + "integrity": "sha512-vJVi8yd/qzJxEKHkeemh7w3YAn6RJCtYlE4HPMoVnCpIXEzSrxErBW5SJBgKLbXU3WdIpkjBTeUNtyBVn8TRng==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-glibc": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.6.tgz", + "integrity": "sha512-9JiYfB6h6BgV50CCfasfLf/uvOcJskMSwcdH1PHH9rvS1IrNy8zad6IUVPVUfmXr+u+Km9IxcfMLzgdOudz9EQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-musl": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.6.tgz", + "integrity": "sha512-Ve3gUCG57nuUUSyjBq/MAM0CzArtuIOxsBdQ+ftz6ho8n7s1i9E1Nmk/xmP323r2YL0SONs1EuwqBp2u1k5fxg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-glibc": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.6.tgz", + "integrity": "sha512-f2g/DT3NhGPdBmMWYoxixqYr3v/UXcmLOYy16Bx0TM20Tchduwr4EaCbmxh1321TABqPGDpS8D/ggOTaljijOA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-musl": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.6.tgz", + "integrity": "sha512-qb6naMDGlbCwdhLj6hgoVKJl2odL34z2sqkC7Z6kzir8b5W65WYDpLB6R06KabvZdgoHI/zxke4b3zR0wAbDTA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-glibc": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.6.tgz", + "integrity": "sha512-kbT5wvNQlx7NaGjzPFu8nVIW1rWqV780O7ZtkjuWaPUgpv2NMFpjYERVi0UYj1msZNyCzGlaCWEtzc+exjMGbQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-musl": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.6.tgz", + "integrity": "sha512-1JRFeC+h7RdXwldHzTsmdtYR/Ku8SylLgTU/reMuqdVD7CtLwf0VR1FqeprZ0eHQkO0vqsbvFLXUmYm/uNKJBg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-arm64": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.6.tgz", + "integrity": "sha512-3ukyebjc6eGlw9yRt678DxVF7rjXatWiHvTXqphZLvo7aC5NdEgFufVwjFfY51ijYEWpXbqF5jtrK275z52D4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-ia32": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.6.tgz", + "integrity": "sha512-k35yLp1ZMwwee3Ez/pxBi5cf4AoBKYXj00CZ80jUz5h8prpiaQsiRPKQMxoLstNuqe2vR4RNPEAEcjEFzhEz/g==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-x64": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.6.tgz", + "integrity": "sha512-hbQlYcCq5dlAX9Qx+kFb0FHue6vbjlf0FrNzSKdYK2APUf7tGfGxQCk2ihEREmbR6ZMc0MVAD5RIX/41gpUzTw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@popperjs/core": { + "version": "2.11.8", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", + "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/popperjs" + } + }, + "node_modules/@remirror/core-constants": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@remirror/core-constants/-/core-constants-3.0.0.tgz", + "integrity": "sha512-42aWfPrimMfDKDi4YegyS7x+/0tlzaqwPQCULLanv3DMIlu96KTJR0fM5isWX2UViOqlGnX6YFgqWepcX+XMNg==", + "license": "MIT" + }, + "node_modules/@rollup/plugin-json": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@rollup/plugin-json/-/plugin-json-6.1.0.tgz", + "integrity": "sha512-EGI2te5ENk1coGeADSIwZ7G2Q8CJS2sF120T7jLw4xFw9n7wIOXHo+kIYRAoVpJAN+kmqZSoO3Fp4JtoNF4ReA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rollup/pluginutils": "^5.1.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/pluginutils": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.3.0.tgz", + "integrity": "sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "estree-walker": "^2.0.2", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.57.1.tgz", + "integrity": "sha512-A6ehUVSiSaaliTxai040ZpZ2zTevHYbvu/lDoeAteHI8QnaosIzm4qwtezfRg1jOYaUmnzLX1AOD6Z+UJjtifg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.57.1.tgz", + "integrity": "sha512-dQaAddCY9YgkFHZcFNS/606Exo8vcLHwArFZ7vxXq4rigo2bb494/xKMMwRRQW6ug7Js6yXmBZhSBRuBvCCQ3w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.57.1.tgz", + "integrity": "sha512-crNPrwJOrRxagUYeMn/DZwqN88SDmwaJ8Cvi/TN1HnWBU7GwknckyosC2gd0IqYRsHDEnXf328o9/HC6OkPgOg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.57.1.tgz", + "integrity": "sha512-Ji8g8ChVbKrhFtig5QBV7iMaJrGtpHelkB3lsaKzadFBe58gmjfGXAOfI5FV0lYMH8wiqsxKQ1C9B0YTRXVy4w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.57.1.tgz", + "integrity": "sha512-R+/WwhsjmwodAcz65guCGFRkMb4gKWTcIeLy60JJQbXrJ97BOXHxnkPFrP+YwFlaS0m+uWJTstrUA9o+UchFug==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.57.1.tgz", + "integrity": "sha512-IEQTCHeiTOnAUC3IDQdzRAGj3jOAYNr9kBguI7MQAAZK3caezRrg0GxAb6Hchg4lxdZEI5Oq3iov/w/hnFWY9Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.57.1.tgz", + "integrity": "sha512-F8sWbhZ7tyuEfsmOxwc2giKDQzN3+kuBLPwwZGyVkLlKGdV1nvnNwYD0fKQ8+XS6hp9nY7B+ZeK01EBUE7aHaw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.57.1.tgz", + "integrity": "sha512-rGfNUfn0GIeXtBP1wL5MnzSj98+PZe/AXaGBCRmT0ts80lU5CATYGxXukeTX39XBKsxzFpEeK+Mrp9faXOlmrw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.57.1.tgz", + "integrity": "sha512-MMtej3YHWeg/0klK2Qodf3yrNzz6CGjo2UntLvk2RSPlhzgLvYEB3frRvbEF2wRKh1Z2fDIg9KRPe1fawv7C+g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.57.1.tgz", + "integrity": "sha512-1a/qhaaOXhqXGpMFMET9VqwZakkljWHLmZOX48R0I/YLbhdxr1m4gtG1Hq7++VhVUmf+L3sTAf9op4JlhQ5u1Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.57.1.tgz", + "integrity": "sha512-QWO6RQTZ/cqYtJMtxhkRkidoNGXc7ERPbZN7dVW5SdURuLeVU7lwKMpo18XdcmpWYd0qsP1bwKPf7DNSUinhvA==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.57.1.tgz", + "integrity": "sha512-xpObYIf+8gprgWaPP32xiN5RVTi/s5FCR+XMXSKmhfoJjrpRAjCuuqQXyxUa/eJTdAE6eJ+KDKaoEqjZQxh3Gw==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.57.1.tgz", + "integrity": "sha512-4BrCgrpZo4hvzMDKRqEaW1zeecScDCR+2nZ86ATLhAoJ5FQ+lbHVD3ttKe74/c7tNT9c6F2viwB3ufwp01Oh2w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.57.1.tgz", + "integrity": "sha512-NOlUuzesGauESAyEYFSe3QTUguL+lvrN1HtwEEsU2rOwdUDeTMJdO5dUYl/2hKf9jWydJrO9OL/XSSf65R5+Xw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.57.1.tgz", + "integrity": "sha512-ptA88htVp0AwUUqhVghwDIKlvJMD/fmL/wrQj99PRHFRAG6Z5nbWoWG4o81Nt9FT+IuqUQi+L31ZKAFeJ5Is+A==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.57.1.tgz", + "integrity": "sha512-S51t7aMMTNdmAMPpBg7OOsTdn4tySRQvklmL3RpDRyknk87+Sp3xaumlatU+ppQ+5raY7sSTcC2beGgvhENfuw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.57.1.tgz", + "integrity": "sha512-Bl00OFnVFkL82FHbEqy3k5CUCKH6OEJL54KCyx2oqsmZnFTR8IoNqBF+mjQVcRCT5sB6yOvK8A37LNm/kPJiZg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.57.1.tgz", + "integrity": "sha512-ABca4ceT4N+Tv/GtotnWAeXZUZuM/9AQyCyKYyKnpk4yoA7QIAuBt6Hkgpw8kActYlew2mvckXkvx0FfoInnLg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.57.1.tgz", + "integrity": "sha512-HFps0JeGtuOR2convgRRkHCekD7j+gdAuXM+/i6kGzQtFhlCtQkpwtNzkNj6QhCDp7DRJ7+qC/1Vg2jt5iSOFw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.57.1.tgz", + "integrity": "sha512-H+hXEv9gdVQuDTgnqD+SQffoWoc0Of59AStSzTEj/feWTBAnSfSD3+Dql1ZruJQxmykT/JVY0dE8Ka7z0DH1hw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.57.1.tgz", + "integrity": "sha512-4wYoDpNg6o/oPximyc/NG+mYUejZrCU2q+2w6YZqrAs2UcNUChIZXjtafAiiZSUc7On8v5NyNj34Kzj/Ltk6dQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.57.1.tgz", + "integrity": "sha512-O54mtsV/6LW3P8qdTcamQmuC990HDfR71lo44oZMZlXU4tzLrbvTii87Ni9opq60ds0YzuAlEr/GNwuNluZyMQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.57.1.tgz", + "integrity": "sha512-P3dLS+IerxCT/7D2q2FYcRdWRl22dNbrbBEtxdWhXrfIMPP9lQhb5h4Du04mdl5Woq05jVCDPCMF7Ub0NAjIew==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.57.1.tgz", + "integrity": "sha512-VMBH2eOOaKGtIJYleXsi2B8CPVADrh+TyNxJ4mWPnKfLB/DBUmzW+5m1xUrcwWoMfSLagIRpjUFeW5CO5hyciQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.57.1.tgz", + "integrity": "sha512-mxRFDdHIWRxg3UfIIAwCm6NzvxG0jDX/wBN6KsQFTvKFqqg9vTrWUE68qEjHt19A5wwx5X5aUi2zuZT7YR0jrA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/wasm-node": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/wasm-node/-/wasm-node-4.57.1.tgz", + "integrity": "sha512-b0rcJH8ykEanfgTeDtlPubhphIUOx0oaAek+3hizTaFkoC1FBSTsY0GixwB4D5HZ5r3Gt2yI9c8M13OcW/kW5A==", + "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": { + "fsevents": "~2.3.2" + } + }, + "node_modules/@tiptap/core": { + "version": "2.27.2", + "resolved": "https://registry.npmjs.org/@tiptap/core/-/core-2.27.2.tgz", + "integrity": "sha512-ABL1N6eoxzDzC1bYvkMbvyexHacszsKdVPYqhl5GwHLOvpZcv9VE9QaKwDILTyz5voCA0lGcAAXZp+qnXOk5lQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/pm": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-blockquote": { + "version": "2.27.2", + "resolved": "https://registry.npmjs.org/@tiptap/extension-blockquote/-/extension-blockquote-2.27.2.tgz", + "integrity": "sha512-oIGZgiAeA4tG3YxbTDfrmENL4/CIwGuP3THtHsNhwRqwsl9SfMk58Ucopi2GXTQSdYXpRJ0ahE6nPqB5D6j/Zw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-bold": { + "version": "2.27.2", + "resolved": "https://registry.npmjs.org/@tiptap/extension-bold/-/extension-bold-2.27.2.tgz", + "integrity": "sha512-bR7J5IwjCGQ0s3CIxyMvOCnMFMzIvsc5OVZKscTN5UkXzFsaY6muUAIqtKxayBUucjtUskm5qZowJITCeCb1/A==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-bubble-menu": { + "version": "2.27.2", + "resolved": "https://registry.npmjs.org/@tiptap/extension-bubble-menu/-/extension-bubble-menu-2.27.2.tgz", + "integrity": "sha512-VkwlCOcr0abTBGzjPXklJ92FCowG7InU8+Od9FyApdLNmn0utRYGRhw0Zno6VgE9EYr1JY4BRnuSa5f9wlR72w==", + "license": "MIT", + "dependencies": { + "tippy.js": "^6.3.7" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0", + "@tiptap/pm": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-bullet-list": { + "version": "2.27.2", + "resolved": "https://registry.npmjs.org/@tiptap/extension-bullet-list/-/extension-bullet-list-2.27.2.tgz", + "integrity": "sha512-gmFuKi97u5f8uFc/GQs+zmezjiulZmFiDYTh3trVoLRoc2SAHOjGEB7qxdx7dsqmMN7gwiAWAEVurLKIi1lnnw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-character-count": { + "version": "2.27.2", + "resolved": "https://registry.npmjs.org/@tiptap/extension-character-count/-/extension-character-count-2.27.2.tgz", + "integrity": "sha512-EcQRIvbLbMDDzo7uFqXYgh1CfgedS9sYX4BllktY2OlXLPdNpwo9t8WMK/a7soESNv0Le3WZ5pNvnNhv7Z2YdA==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0", + "@tiptap/pm": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-code": { + "version": "2.27.2", + "resolved": "https://registry.npmjs.org/@tiptap/extension-code/-/extension-code-2.27.2.tgz", + "integrity": "sha512-7X9AgwqiIGXoZX7uvdHQsGsjILnN/JaEVtqfXZnPECzKGaWHeK/Ao4sYvIIIffsyZJA8k5DC7ny2/0sAgr2TuA==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-code-block": { + "version": "2.27.2", + "resolved": "https://registry.npmjs.org/@tiptap/extension-code-block/-/extension-code-block-2.27.2.tgz", + "integrity": "sha512-KgvdQHS4jXr79aU3wZOGBIZYYl9vCB7uDEuRFV4so2rYrfmiYMw3T8bTnlNEEGe4RUeAms1i4fdwwvQp9nR1Dw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0", + "@tiptap/pm": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-code-block-lowlight": { + "version": "2.27.2", + "resolved": "https://registry.npmjs.org/@tiptap/extension-code-block-lowlight/-/extension-code-block-lowlight-2.27.2.tgz", + "integrity": "sha512-v6NKStBbQ/XCc1NnCi3ObsL1DsxadSIBtUQNA/B+urkPgn5LEy72HAGlf0xwjRaNkAGSaTASLKmc84L5q5zlGQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0", + "@tiptap/extension-code-block": "^2.7.0", + "@tiptap/pm": "^2.7.0", + "highlight.js": "^11", + "lowlight": "^2 || ^3" + } + }, + "node_modules/@tiptap/extension-collaboration": { + "version": "2.27.2", + "resolved": "https://registry.npmjs.org/@tiptap/extension-collaboration/-/extension-collaboration-2.27.2.tgz", + "integrity": "sha512-Y61ItHxQ1uc/Ir27mBQRI/wY9JkOui194V+awNv+1YHeaKArTjC2cdSvNzj9+h8JIh5MyfvslSf8hBa7t7PzAg==", + "license": "MIT", + "optional": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0", + "@tiptap/pm": "^2.7.0", + "y-prosemirror": "^1.2.11" + } + }, + "node_modules/@tiptap/extension-collaboration-cursor": { + "version": "2.26.2", + "resolved": "https://registry.npmjs.org/@tiptap/extension-collaboration-cursor/-/extension-collaboration-cursor-2.26.2.tgz", + "integrity": "sha512-FdRb27mZ5Kr18hN6cbfBj1e9F0DOoHB1Gv3IYeic+g4h1C9BjDVMN0+JRBQc+4lamNA8TsHO0oKWRwaPe4sSlA==", + "license": "MIT", + "optional": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0", + "y-prosemirror": "^1.2.11" + } + }, + "node_modules/@tiptap/extension-color": { + "version": "2.27.2", + "resolved": "https://registry.npmjs.org/@tiptap/extension-color/-/extension-color-2.27.2.tgz", + "integrity": "sha512-sOKCP8/2V3sRM3FdWgMe1lFE5ewsWNCRafiVoujS1+TTHGCj4jw6W+LiumBUk7cRI8kXW/rqGWVC4RVdknYUCA==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0", + "@tiptap/extension-text-style": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-document": { + "version": "2.27.2", + "resolved": "https://registry.npmjs.org/@tiptap/extension-document/-/extension-document-2.27.2.tgz", + "integrity": "sha512-CFhAYsPnyYnosDC4639sCJnBUnYH4Cat9qH5NZWHVvdgtDwu8GZgZn2eSzaKSYXWH1vJ9DSlCK+7UyC3SNXIBA==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-dropcursor": { + "version": "2.27.2", + "resolved": "https://registry.npmjs.org/@tiptap/extension-dropcursor/-/extension-dropcursor-2.27.2.tgz", + "integrity": "sha512-oEu/OrktNoQXq1x29NnH/GOIzQZm8ieTQl3FK27nxfBPA89cNoH4mFEUmBL5/OFIENIjiYG3qWpg6voIqzswNw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0", + "@tiptap/pm": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-floating-menu": { + "version": "2.27.2", + "resolved": "https://registry.npmjs.org/@tiptap/extension-floating-menu/-/extension-floating-menu-2.27.2.tgz", + "integrity": "sha512-GUN6gPIGXS7ngRJOwdSmtBRBDt9Kt9CM/9pSwKebhLJ+honFoNA+Y6IpVyDvvDMdVNgBchiJLs6qA5H97gAePQ==", + "license": "MIT", + "dependencies": { + "tippy.js": "^6.3.7" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0", + "@tiptap/pm": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-gapcursor": { + "version": "2.27.2", + "resolved": "https://registry.npmjs.org/@tiptap/extension-gapcursor/-/extension-gapcursor-2.27.2.tgz", + "integrity": "sha512-/c9VF1HBxj+AP54XGVgCmD9bEGYc5w5OofYCFQgM7l7PB1J00A4vOke0oPkHJnqnOOyPlFaxO/7N6l3XwFcnKA==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0", + "@tiptap/pm": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-hard-break": { + "version": "2.27.2", + "resolved": "https://registry.npmjs.org/@tiptap/extension-hard-break/-/extension-hard-break-2.27.2.tgz", + "integrity": "sha512-kSRVGKlCYK6AGR0h8xRkk0WOFGXHIIndod3GKgWU49APuIGDiXd8sziXsSlniUsWmqgDmDXcNnSzPcV7AQ8YNg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-heading": { + "version": "2.27.2", + "resolved": "https://registry.npmjs.org/@tiptap/extension-heading/-/extension-heading-2.27.2.tgz", + "integrity": "sha512-iM3yeRWuuQR/IRQ1djwNooJGfn9Jts9zF43qZIUf+U2NY8IlvdNsk2wTOdBgh6E0CamrStPxYGuln3ZS4fuglw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-highlight": { + "version": "2.27.2", + "resolved": "https://registry.npmjs.org/@tiptap/extension-highlight/-/extension-highlight-2.27.2.tgz", + "integrity": "sha512-ZjlktDdMjruMJFAVz0TbQf0v92Jqkc7Ri1iZJqBXuLid+r+GxUzl2CVAV7qq5yagkGQgvAG+WGsMk880HgR3MA==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-history": { + "version": "2.27.2", + "resolved": "https://registry.npmjs.org/@tiptap/extension-history/-/extension-history-2.27.2.tgz", + "integrity": "sha512-+hSyqERoFNTWPiZx4/FCyZ/0eFqB9fuMdTB4AC/q9iwu3RNWAQtlsJg5230bf/qmyO6bZxRUc0k8p4hrV6ybAw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0", + "@tiptap/pm": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-horizontal-rule": { + "version": "2.27.2", + "resolved": "https://registry.npmjs.org/@tiptap/extension-horizontal-rule/-/extension-horizontal-rule-2.27.2.tgz", + "integrity": "sha512-WGWUSgX+jCsbtf9Y9OCUUgRZYuwjVoieW5n6mAUohJ9/6gc6sGIOrUpBShf+HHo6WD+gtQjRd+PssmX3NPWMpg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0", + "@tiptap/pm": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-image": { + "version": "2.27.2", + "resolved": "https://registry.npmjs.org/@tiptap/extension-image/-/extension-image-2.27.2.tgz", + "integrity": "sha512-5zL/BY41FIt72azVrCrv3n+2YJ/JyO8wxCcA4Dk1eXIobcgVyIdo4rG39gCqIOiqziAsqnqoj12QHTBtHsJ6mQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-italic": { + "version": "2.27.2", + "resolved": "https://registry.npmjs.org/@tiptap/extension-italic/-/extension-italic-2.27.2.tgz", + "integrity": "sha512-1OFsw2SZqfaqx5Fa5v90iNlPRcqyt+lVSjBwTDzuPxTPFY4Q0mL89mKgkq2gVHYNCiaRkXvFLDxaSvBWbmthgg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-link": { + "version": "2.27.2", + "resolved": "https://registry.npmjs.org/@tiptap/extension-link/-/extension-link-2.27.2.tgz", + "integrity": "sha512-bnP61qkr0Kj9Cgnop1hxn2zbOCBzNtmawxr92bVTOE31fJv6FhtCnQiD6tuPQVGMYhcmAj7eihtvuEMFfqEPcQ==", + "license": "MIT", + "dependencies": { + "linkifyjs": "^4.3.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0", + "@tiptap/pm": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-list-item": { + "version": "2.27.2", + "resolved": "https://registry.npmjs.org/@tiptap/extension-list-item/-/extension-list-item-2.27.2.tgz", + "integrity": "sha512-eJNee7IEGXMnmygM5SdMGDC8m/lMWmwNGf9fPCK6xk0NxuQRgmZHL6uApKcdH6gyNcRPHCqvTTkhEP7pbny/fg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-mention": { + "version": "2.27.2", + "resolved": "https://registry.npmjs.org/@tiptap/extension-mention/-/extension-mention-2.27.2.tgz", + "integrity": "sha512-uHxVf8RISscb4xgCEJmDSNcFQmzlBTKJh7fp2QAXWIF4Xtrg3zD08PIXUvvHapoluGD9OdBugW4YCu1PJ3xWNw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0", + "@tiptap/pm": "^2.7.0", + "@tiptap/suggestion": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-ordered-list": { + "version": "2.27.2", + "resolved": "https://registry.npmjs.org/@tiptap/extension-ordered-list/-/extension-ordered-list-2.27.2.tgz", + "integrity": "sha512-M7A4tLGJcLPYdLC4CI2Gwl8LOrENQW59u3cMVa+KkwG1hzSJyPsbDpa1DI6oXPC2WtYiTf22zrbq3gVvH+KA2w==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-paragraph": { + "version": "2.27.2", + "resolved": "https://registry.npmjs.org/@tiptap/extension-paragraph/-/extension-paragraph-2.27.2.tgz", + "integrity": "sha512-elYVn2wHJJ+zB9LESENWOAfI4TNT0jqEN34sMA/hCtA4im1ZG2DdLHwkHIshj/c4H0dzQhmsS/YmNC5Vbqab/A==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-placeholder": { + "version": "2.27.2", + "resolved": "https://registry.npmjs.org/@tiptap/extension-placeholder/-/extension-placeholder-2.27.2.tgz", + "integrity": "sha512-IjsgSVYJRjpAKmIoapU0E2R4E2FPY3kpvU7/1i7PUYisylqejSJxmtJPGYw0FOMQY9oxnEEvfZHMBA610tqKpg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0", + "@tiptap/pm": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-strike": { + "version": "2.27.2", + "resolved": "https://registry.npmjs.org/@tiptap/extension-strike/-/extension-strike-2.27.2.tgz", + "integrity": "sha512-HHIjhafLhS2lHgfAsCwC1okqMsQzR4/mkGDm4M583Yftyjri1TNA7lzhzXWRFWiiMfJxKtdjHjUAQaHuteRTZw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-subscript": { + "version": "2.27.2", + "resolved": "https://registry.npmjs.org/@tiptap/extension-subscript/-/extension-subscript-2.27.2.tgz", + "integrity": "sha512-x2Oz7hrI4KvzzB9pWChFRm6JnKdYAUQDyrlSROngtzXT7VpNQNoD5s8OlICzDeNsaRKzhR8omIz2z17S1VB48g==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-superscript": { + "version": "2.27.2", + "resolved": "https://registry.npmjs.org/@tiptap/extension-superscript/-/extension-superscript-2.27.2.tgz", + "integrity": "sha512-VTGJDuNqdesibSVW94Q71VaGVGr/bwBppdaNLn7k6beOegALfIH7ncArlkD/eihOlJ2qaWiT7FoWNLTb/Fdv1w==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-table": { + "version": "2.27.2", + "resolved": "https://registry.npmjs.org/@tiptap/extension-table/-/extension-table-2.27.2.tgz", + "integrity": "sha512-pDbhOpT5phZkcsyPjGBQlXv0+0hmdrvqHJ+dJjkGcCtlfy2pHiEIhmIItOFagc7wXy8G9iUFZ9Jie4zvDf+brg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0", + "@tiptap/pm": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-table-cell": { + "version": "2.27.2", + "resolved": "https://registry.npmjs.org/@tiptap/extension-table-cell/-/extension-table-cell-2.27.2.tgz", + "integrity": "sha512-9Lk46MjZMFzVZfOj9Kd7VgC6Odt6vmEhlCYVumErShUY7EkFqCw3b2IYoUtQkntfOEx/Afnhff/okNQwPsJeUA==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-table-header": { + "version": "2.27.2", + "resolved": "https://registry.npmjs.org/@tiptap/extension-table-header/-/extension-table-header-2.27.2.tgz", + "integrity": "sha512-ZEb6lbG0NbbodWLV0b4BS/QrDIPlUbCcuOsUxzqVvlMUY1Vg6Fj6fKwLaBcsIUDHi8sxZDBEgYEDw3BR/zcO6A==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-table-row": { + "version": "2.27.2", + "resolved": "https://registry.npmjs.org/@tiptap/extension-table-row/-/extension-table-row-2.27.2.tgz", + "integrity": "sha512-Nw9+tA56Y5HtLVP01NGCZSUuTQhJPtfK9OfmDgGgcxynn2cRVdEtj+9FNZqRhQ1iRVaAI+Rd4xRvX9qYePMOxw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-task-item": { + "version": "2.27.2", + "resolved": "https://registry.npmjs.org/@tiptap/extension-task-item/-/extension-task-item-2.27.2.tgz", + "integrity": "sha512-ZBSqj/dygB/Rp5K9qOxRVwASTZCmKVoTq8C59KvMgD/aFjJxhq/w2dZaWkCUEXEep+NmvJqo0kfeAEMY5UDnGg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0", + "@tiptap/pm": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-task-list": { + "version": "2.27.2", + "resolved": "https://registry.npmjs.org/@tiptap/extension-task-list/-/extension-task-list-2.27.2.tgz", + "integrity": "sha512-5nupAewdzZ9F3599oAcaK0WkDH04wdACAVBPM4zG7InlIpkbho3txB7zWmm64OxfhCMIMGKiXY1q0bw9i0QBGQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-text": { + "version": "2.27.2", + "resolved": "https://registry.npmjs.org/@tiptap/extension-text/-/extension-text-2.27.2.tgz", + "integrity": "sha512-Xk7nYcigljAY0GO9hAQpZ65ZCxqOqaAlTPDFcKerXmlkQZP/8ndx95OgUb1Xf63kmPOh3xypurGS2is3v0MXSA==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-text-align": { + "version": "2.27.2", + "resolved": "https://registry.npmjs.org/@tiptap/extension-text-align/-/extension-text-align-2.27.2.tgz", + "integrity": "sha512-0Pyks6Hu+Q/+9+5/osoSv0SP6jIerdWMYbi13aaZLsJoj3lBj5WNaE11JtAwSFN5sx0IbqhDSlp1zkvRnzgZ8g==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-text-style": { + "version": "2.27.2", + "resolved": "https://registry.npmjs.org/@tiptap/extension-text-style/-/extension-text-style-2.27.2.tgz", + "integrity": "sha512-Omk+uxjJLyEY69KStpCw5fA9asvV+MGcAX2HOxyISDFoLaL49TMrNjhGAuz09P1L1b0KGXo4ml7Q3v/Lfy4WPA==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-underline": { + "version": "2.27.2", + "resolved": "https://registry.npmjs.org/@tiptap/extension-underline/-/extension-underline-2.27.2.tgz", + "integrity": "sha512-gPOsbAcw1S07ezpAISwoO8f0RxpjcSH7VsHEFDVuXm4ODE32nhvSinvHQjv2icRLOXev+bnA7oIBu7Oy859gWQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0" + } + }, + "node_modules/@tiptap/pm": { + "version": "2.27.2", + "resolved": "https://registry.npmjs.org/@tiptap/pm/-/pm-2.27.2.tgz", + "integrity": "sha512-kaEg7BfiJPDQMKbjVIzEPO3wlcA+pZb2tlcK9gPrdDnEFaec2QTF1sXz2ak2IIb2curvnIrQ4yrfHgLlVA72wA==", + "license": "MIT", + "dependencies": { + "prosemirror-changeset": "^2.3.0", + "prosemirror-collab": "^1.3.1", + "prosemirror-commands": "^1.6.2", + "prosemirror-dropcursor": "^1.8.1", + "prosemirror-gapcursor": "^1.3.2", + "prosemirror-history": "^1.4.1", + "prosemirror-inputrules": "^1.4.0", + "prosemirror-keymap": "^1.2.2", + "prosemirror-markdown": "^1.13.1", + "prosemirror-menu": "^1.2.4", + "prosemirror-model": "^1.23.0", + "prosemirror-schema-basic": "^1.2.3", + "prosemirror-schema-list": "^1.4.1", + "prosemirror-state": "^1.4.3", + "prosemirror-tables": "^1.6.4", + "prosemirror-trailing-node": "^3.0.0", + "prosemirror-transform": "^1.10.2", + "prosemirror-view": "^1.37.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + } + }, + "node_modules/@tiptap/starter-kit": { + "version": "2.27.2", + "resolved": "https://registry.npmjs.org/@tiptap/starter-kit/-/starter-kit-2.27.2.tgz", + "integrity": "sha512-bb0gJvPoDuyRUQ/iuN52j1//EtWWttw+RXAv1uJxfR0uKf8X7uAqzaOOgwjknoCIDC97+1YHwpGdnRjpDkOBxw==", + "license": "MIT", + "dependencies": { + "@tiptap/core": "^2.27.2", + "@tiptap/extension-blockquote": "^2.27.2", + "@tiptap/extension-bold": "^2.27.2", + "@tiptap/extension-bullet-list": "^2.27.2", + "@tiptap/extension-code": "^2.27.2", + "@tiptap/extension-code-block": "^2.27.2", + "@tiptap/extension-document": "^2.27.2", + "@tiptap/extension-dropcursor": "^2.27.2", + "@tiptap/extension-gapcursor": "^2.27.2", + "@tiptap/extension-hard-break": "^2.27.2", + "@tiptap/extension-heading": "^2.27.2", + "@tiptap/extension-history": "^2.27.2", + "@tiptap/extension-horizontal-rule": "^2.27.2", + "@tiptap/extension-italic": "^2.27.2", + "@tiptap/extension-list-item": "^2.27.2", + "@tiptap/extension-ordered-list": "^2.27.2", + "@tiptap/extension-paragraph": "^2.27.2", + "@tiptap/extension-strike": "^2.27.2", + "@tiptap/extension-text": "^2.27.2", + "@tiptap/extension-text-style": "^2.27.2", + "@tiptap/pm": "^2.27.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + } + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/hast": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", + "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/linkify-it": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==", + "license": "MIT" + }, + "node_modules/@types/markdown-it": { + "version": "14.1.2", + "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-14.1.2.tgz", + "integrity": "sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==", + "license": "MIT", + "dependencies": { + "@types/linkify-it": "^5", + "@types/mdurl": "^2" + } + }, + "node_modules/@types/mdurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==", + "license": "MIT" + }, + "node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "license": "MIT" + }, + "node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-colors": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "license": "Python-2.0" + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/baseline-browser-mapping": { + "version": "2.9.19", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.19.tgz", + "integrity": "sha512-ipDqC8FrAl/76p2SSWKSI+H9tFwm7vYqXQrItCuiVPt26Km0jS+NzSsBWAaBusvSbQcfJG+JitdMm+wZAgTYqg==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.js" + } + }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", + "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", + "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.9.0", + "caniuse-lite": "^1.0.30001759", + "electron-to-chromium": "^1.5.263", + "node-releases": "^2.0.27", + "update-browserslist-db": "^1.2.0" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001769", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001769.tgz", + "integrity": "sha512-BCfFL1sHijQlBGWBMuJyhZUhzo7wer5sVj9hqekB/7xn0Ypy+pER/edCYQm4exbXj4WiySGp40P8UuTh6w1srg==", + "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/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "restore-cursor": "^3.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-spinners": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", + "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/commander": { + "version": "13.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-13.1.0.tgz", + "integrity": "sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", + "dev": true, + "license": "MIT" + }, + "node_modules/convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "dev": true, + "license": "MIT" + }, + "node_modules/copy-anything": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-2.0.6.tgz", + "integrity": "sha512-1j20GZTsvKNkc4BY3NpMOM8tt///wY3FpIzozTOFO2ffuZcV61nojHXVKIy3WM+7ADCy5FVhdZYHYDdgTU0yJw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-what": "^3.14.1" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } + }, + "node_modules/crelt": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.6.tgz", + "integrity": "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==", + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/defaults": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", + "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "clone": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/dependency-graph": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/dependency-graph/-/dependency-graph-1.0.0.tgz", + "integrity": "sha512-cW3gggJ28HZ/LExwxP2B++aiKxhJXMSIt9K48FOXQkm+vuG5gyatXnLsONRJdzO/7VfjDIiaOOa/bs4l464Lwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/devlop": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", + "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", + "license": "MIT", + "dependencies": { + "dequal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.286", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.286.tgz", + "integrity": "sha512-9tfDXhJ4RKFNerfjdCcZfufu49vg620741MNs26a9+bhLThdB+plgMeou98CAaHu/WATj2iHOOHTp1hWtABj2A==", + "dev": true, + "license": "ISC" + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/errno": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz", + "integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "prr": "~1.0.1" + }, + "bin": { + "errno": "cli.js" + } + }, + "node_modules/esbuild": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz", + "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.12", + "@esbuild/android-arm": "0.25.12", + "@esbuild/android-arm64": "0.25.12", + "@esbuild/android-x64": "0.25.12", + "@esbuild/darwin-arm64": "0.25.12", + "@esbuild/darwin-x64": "0.25.12", + "@esbuild/freebsd-arm64": "0.25.12", + "@esbuild/freebsd-x64": "0.25.12", + "@esbuild/linux-arm": "0.25.12", + "@esbuild/linux-arm64": "0.25.12", + "@esbuild/linux-ia32": "0.25.12", + "@esbuild/linux-loong64": "0.25.12", + "@esbuild/linux-mips64el": "0.25.12", + "@esbuild/linux-ppc64": "0.25.12", + "@esbuild/linux-riscv64": "0.25.12", + "@esbuild/linux-s390x": "0.25.12", + "@esbuild/linux-x64": "0.25.12", + "@esbuild/netbsd-arm64": "0.25.12", + "@esbuild/netbsd-x64": "0.25.12", + "@esbuild/openbsd-arm64": "0.25.12", + "@esbuild/openbsd-x64": "0.25.12", + "@esbuild/openharmony-arm64": "0.25.12", + "@esbuild/sunos-x64": "0.25.12", + "@esbuild/win32-arm64": "0.25.12", + "@esbuild/win32-ia32": "0.25.12", + "@esbuild/win32-x64": "0.25.12" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", + "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/fastq": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", + "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-cache-dir": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", + "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", + "dev": true, + "license": "MIT", + "dependencies": { + "commondir": "^1.0.1", + "make-dir": "^3.0.2", + "pkg-dir": "^4.1.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/avajs/find-cache-dir?sponsor=1" + } + }, + "node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC", + "optional": true + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/highlight.js": { + "version": "11.11.1", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.11.1.tgz", + "integrity": "sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/image-size": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz", + "integrity": "sha512-6TDAlDPZxUFCv+fuOkIoXT/V/f3Qbq8e37p+YOiYrUv3v9cc3/6x78VdfPgFVaB9dZYeLUfKgHRebpkm/oP2VQ==", + "dev": true, + "license": "MIT", + "optional": true, + "bin": { + "image-size": "bin/image-size.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/immutable": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.4.tgz", + "integrity": "sha512-p6u1bG3YSnINT5RQmx/yRZBpenIl30kVxkTLDyHLIMk0gict704Q9n+thfDI7lTRm9vXdDYutVzXhzcThxTnXA==", + "dev": true, + "license": "MIT" + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/injection-js": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/injection-js/-/injection-js-2.6.1.tgz", + "integrity": "sha512-dbR5bdhi7TWDoCye9cByZqeg/gAfamm8Vu3G1KZOTYkOif8WkuM8CD0oeDPtZYMzT5YH76JAFB7bkmyY9OJi2A==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.0.0" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-interactive": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", + "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-what": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/is-what/-/is-what-3.14.1.tgz", + "integrity": "sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA==", + "dev": true, + "license": "MIT" + }, + "node_modules/isomorphic.js": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/isomorphic.js/-/isomorphic.js-0.2.5.tgz", + "integrity": "sha512-PIeMbHqMt4DnUP3MA/Flc0HElYjMXArsw1qwJZcm9sqR8mq3l8NYizFMty0pWwE/tzIGH3EKK5+jes5mAr85yw==", + "license": "MIT", + "optional": true, + "funding": { + "type": "GitHub Sponsors ❤", + "url": "https://github.com/sponsors/dmonad" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonc-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", + "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/less": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/less/-/less-4.5.1.tgz", + "integrity": "sha512-UKgI3/KON4u6ngSsnDADsUERqhZknsVZbnuzlRZXLQCmfC/MDld42fTydUE9B+Mla1AL6SJ/Pp6SlEFi/AVGfw==", + "dev": true, + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "copy-anything": "^2.0.1", + "parse-node-version": "^1.0.1", + "tslib": "^2.3.0" + }, + "bin": { + "lessc": "bin/lessc" + }, + "engines": { + "node": ">=14" + }, + "optionalDependencies": { + "errno": "^0.1.1", + "graceful-fs": "^4.1.2", + "image-size": "~0.5.0", + "make-dir": "^2.1.0", + "mime": "^1.4.1", + "needle": "^3.1.0", + "source-map": "~0.6.0" + } + }, + "node_modules/less/node_modules/make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "pify": "^4.0.1", + "semver": "^5.6.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/less/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "license": "ISC", + "optional": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/lib0": { + "version": "0.2.117", + "resolved": "https://registry.npmjs.org/lib0/-/lib0-0.2.117.tgz", + "integrity": "sha512-DeXj9X5xDCjgKLU/7RR+/HQEVzuuEUiwldwOGsHK/sfAfELGWEyTcf0x+uOvCvK3O2zPmZePXWL85vtia6GyZw==", + "license": "MIT", + "optional": true, + "dependencies": { + "isomorphic.js": "^0.2.4" + }, + "bin": { + "0ecdsa-generate-keypair": "bin/0ecdsa-generate-keypair.js", + "0gentesthtml": "bin/gentesthtml.js", + "0serve": "bin/0serve.js" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "type": "GitHub Sponsors ❤", + "url": "https://github.com/sponsors/dmonad" + } + }, + "node_modules/linkify-it": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==", + "license": "MIT", + "dependencies": { + "uc.micro": "^2.0.0" + } + }, + "node_modules/linkifyjs": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/linkifyjs/-/linkifyjs-4.3.2.tgz", + "integrity": "sha512-NT1CJtq3hHIreOianA8aSXn6Cw0JzYOuDQbOrSPe7gqFnCpKP++MQe3ODgO3oh2GJFORkAAdqredOa60z63GbA==", + "license": "MIT" + }, + "node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lowlight": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/lowlight/-/lowlight-3.3.0.tgz", + "integrity": "sha512-0JNhgFoPvP6U6lE/UdVsSq99tn6DhjjpAj5MxG49ewd2mOBVtwWYIT8ClyABhq198aXXODMU6Ox8DrGy/CpTZQ==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "devlop": "^1.0.0", + "highlight.js": "~11.11.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/markdown-it": { + "version": "14.1.1", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.1.tgz", + "integrity": "sha512-BuU2qnTti9YKgK5N+IeMubp14ZUKUUw7yeJbkjtosvHiP0AZ5c8IAgEMk79D0eC8F23r4Ac/q8cAIFdm2FtyoA==", + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1", + "entities": "^4.4.0", + "linkify-it": "^5.0.0", + "mdurl": "^2.0.0", + "punycode.js": "^2.3.1", + "uc.micro": "^2.1.0" + }, + "bin": { + "markdown-it": "bin/markdown-it.mjs" + } + }, + "node_modules/marked": { + "version": "12.0.2", + "resolved": "https://registry.npmjs.org/marked/-/marked-12.0.2.tgz", + "integrity": "sha512-qXUm7e/YKFoqFPYPa3Ukg9xlI5cyAtGmyEIzMfW//m6kXwCy2Ps9DYf5ioijFKQ8qyuscrHoY04iJGctu2Kg0Q==", + "license": "MIT", + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/mdurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==", + "license": "MIT" + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "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/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true, + "license": "MIT", + "optional": true, + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "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/needle": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/needle/-/needle-3.3.1.tgz", + "integrity": "sha512-6k0YULvhpw+RoLNiQCRKOl09Rv1dPLr8hHnVjHqdolKwDrdNyk+Hmrthi4lIGPPz3r39dLx0hsF5s40sZ3Us4Q==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "iconv-lite": "^0.6.3", + "sax": "^1.2.4" + }, + "bin": { + "needle": "bin/needle" + }, + "engines": { + "node": ">= 4.4.x" + } + }, + "node_modules/ng-packagr": { + "version": "19.2.2", + "resolved": "https://registry.npmjs.org/ng-packagr/-/ng-packagr-19.2.2.tgz", + "integrity": "sha512-dFuwFsDJMBSd1YtmLLcX5bNNUCQUlRqgf34aXA+79PmkOP+0eF8GP2949wq3+jMjmFTNm80Oo8IUYiSLwklKCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rollup/plugin-json": "^6.1.0", + "@rollup/wasm-node": "^4.24.0", + "ajv": "^8.17.1", + "ansi-colors": "^4.1.3", + "browserslist": "^4.22.1", + "chokidar": "^4.0.1", + "commander": "^13.0.0", + "convert-source-map": "^2.0.0", + "dependency-graph": "^1.0.0", + "esbuild": "^0.25.0", + "fast-glob": "^3.3.2", + "find-cache-dir": "^3.3.2", + "injection-js": "^2.4.0", + "jsonc-parser": "^3.3.1", + "less": "^4.2.0", + "ora": "^5.1.0", + "piscina": "^4.7.0", + "postcss": "^8.4.47", + "rxjs": "^7.8.1", + "sass": "^1.81.0" + }, + "bin": { + "ng-packagr": "cli/main.js" + }, + "engines": { + "node": "^18.19.1 || >=20.11.1" + }, + "optionalDependencies": { + "rollup": "^4.24.0" + }, + "peerDependencies": { + "@angular/compiler-cli": "^19.0.0 || ^19.1.0-next.0 || ^19.2.0-next.0", + "tailwindcss": "^2.0.0 || ^3.0.0 || ^4.0.0", + "tslib": "^2.3.0", + "typescript": ">=5.5 <5.9" + }, + "peerDependenciesMeta": { + "tailwindcss": { + "optional": true + } + } + }, + "node_modules/ng-packagr/node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-addon-api": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", + "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/node-releases": { + "version": "2.0.27", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", + "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", + "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "bl": "^4.1.0", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.5.0", + "is-interactive": "^1.0.0", + "is-unicode-supported": "^0.1.0", + "log-symbols": "^4.1.0", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/orderedmap": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/orderedmap/-/orderedmap-2.1.1.tgz", + "integrity": "sha512-TvAWxi0nDe1j/rtMcWcIj94+Ffe6n7zhow33h40SKxmsmozs6dz/e+EajymfoFcHd7sxNn8yHM8839uixMOV6g==", + "license": "MIT" + }, + "node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-node-version": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parse-node-version/-/parse-node-version-1.0.1.tgz", + "integrity": "sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "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": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/piscina": { + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/piscina/-/piscina-4.9.2.tgz", + "integrity": "sha512-Fq0FERJWFEUpB4eSY59wSNwXD4RYqR+nR/WiEVcZW8IWfVBxJJafcgTEZDQo8k3w0sUarJ8RyVbbUF4GQ2LGbQ==", + "dev": true, + "license": "MIT", + "optionalDependencies": { + "@napi-rs/nice": "^1.0.1" + } + }, + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "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/prosemirror-changeset": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/prosemirror-changeset/-/prosemirror-changeset-2.3.1.tgz", + "integrity": "sha512-j0kORIBm8ayJNl3zQvD1TTPHJX3g042et6y/KQhZhnPrruO8exkTgG8X+NRpj7kIyMMEx74Xb3DyMIBtO0IKkQ==", + "license": "MIT", + "dependencies": { + "prosemirror-transform": "^1.0.0" + } + }, + "node_modules/prosemirror-collab": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/prosemirror-collab/-/prosemirror-collab-1.3.1.tgz", + "integrity": "sha512-4SnynYR9TTYaQVXd/ieUvsVV4PDMBzrq2xPUWutHivDuOshZXqQ5rGbZM84HEaXKbLdItse7weMGOUdDVcLKEQ==", + "license": "MIT", + "dependencies": { + "prosemirror-state": "^1.0.0" + } + }, + "node_modules/prosemirror-commands": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/prosemirror-commands/-/prosemirror-commands-1.7.1.tgz", + "integrity": "sha512-rT7qZnQtx5c0/y/KlYaGvtG411S97UaL6gdp6RIZ23DLHanMYLyfGBV5DtSnZdthQql7W+lEVbpSfwtO8T+L2w==", + "license": "MIT", + "dependencies": { + "prosemirror-model": "^1.0.0", + "prosemirror-state": "^1.0.0", + "prosemirror-transform": "^1.10.2" + } + }, + "node_modules/prosemirror-dropcursor": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/prosemirror-dropcursor/-/prosemirror-dropcursor-1.8.2.tgz", + "integrity": "sha512-CCk6Gyx9+Tt2sbYk5NK0nB1ukHi2ryaRgadV/LvyNuO3ena1payM2z6Cg0vO1ebK8cxbzo41ku2DE5Axj1Zuiw==", + "license": "MIT", + "dependencies": { + "prosemirror-state": "^1.0.0", + "prosemirror-transform": "^1.1.0", + "prosemirror-view": "^1.1.0" + } + }, + "node_modules/prosemirror-gapcursor": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/prosemirror-gapcursor/-/prosemirror-gapcursor-1.4.0.tgz", + "integrity": "sha512-z00qvurSdCEWUIulij/isHaqu4uLS8r/Fi61IbjdIPJEonQgggbJsLnstW7Lgdk4zQ68/yr6B6bf7sJXowIgdQ==", + "license": "MIT", + "dependencies": { + "prosemirror-keymap": "^1.0.0", + "prosemirror-model": "^1.0.0", + "prosemirror-state": "^1.0.0", + "prosemirror-view": "^1.0.0" + } + }, + "node_modules/prosemirror-history": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/prosemirror-history/-/prosemirror-history-1.5.0.tgz", + "integrity": "sha512-zlzTiH01eKA55UAf1MEjtssJeHnGxO0j4K4Dpx+gnmX9n+SHNlDqI2oO1Kv1iPN5B1dm5fsljCfqKF9nFL6HRg==", + "license": "MIT", + "dependencies": { + "prosemirror-state": "^1.2.2", + "prosemirror-transform": "^1.0.0", + "prosemirror-view": "^1.31.0", + "rope-sequence": "^1.3.0" + } + }, + "node_modules/prosemirror-inputrules": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/prosemirror-inputrules/-/prosemirror-inputrules-1.5.1.tgz", + "integrity": "sha512-7wj4uMjKaXWAQ1CDgxNzNtR9AlsuwzHfdFH1ygEHA2KHF2DOEaXl1CJfNPAKCg9qNEh4rum975QLaCiQPyY6Fw==", + "license": "MIT", + "dependencies": { + "prosemirror-state": "^1.0.0", + "prosemirror-transform": "^1.0.0" + } + }, + "node_modules/prosemirror-keymap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/prosemirror-keymap/-/prosemirror-keymap-1.2.3.tgz", + "integrity": "sha512-4HucRlpiLd1IPQQXNqeo81BGtkY8Ai5smHhKW9jjPKRc2wQIxksg7Hl1tTI2IfT2B/LgX6bfYvXxEpJl7aKYKw==", + "license": "MIT", + "dependencies": { + "prosemirror-state": "^1.0.0", + "w3c-keyname": "^2.2.0" + } + }, + "node_modules/prosemirror-markdown": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/prosemirror-markdown/-/prosemirror-markdown-1.13.4.tgz", + "integrity": "sha512-D98dm4cQ3Hs6EmjK500TdAOew4Z03EV71ajEFiWra3Upr7diytJsjF4mPV2dW+eK5uNectiRj0xFxYI9NLXDbw==", + "license": "MIT", + "dependencies": { + "@types/markdown-it": "^14.0.0", + "markdown-it": "^14.0.0", + "prosemirror-model": "^1.25.0" + } + }, + "node_modules/prosemirror-menu": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/prosemirror-menu/-/prosemirror-menu-1.2.5.tgz", + "integrity": "sha512-qwXzynnpBIeg1D7BAtjOusR+81xCp53j7iWu/IargiRZqRjGIlQuu1f3jFi+ehrHhWMLoyOQTSRx/IWZJqOYtQ==", + "license": "MIT", + "dependencies": { + "crelt": "^1.0.0", + "prosemirror-commands": "^1.0.0", + "prosemirror-history": "^1.0.0", + "prosemirror-state": "^1.0.0" + } + }, + "node_modules/prosemirror-model": { + "version": "1.25.4", + "resolved": "https://registry.npmjs.org/prosemirror-model/-/prosemirror-model-1.25.4.tgz", + "integrity": "sha512-PIM7E43PBxKce8OQeezAs9j4TP+5yDpZVbuurd1h5phUxEKIu+G2a+EUZzIC5nS1mJktDJWzbqS23n1tsAf5QA==", + "license": "MIT", + "dependencies": { + "orderedmap": "^2.0.0" + } + }, + "node_modules/prosemirror-schema-basic": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/prosemirror-schema-basic/-/prosemirror-schema-basic-1.2.4.tgz", + "integrity": "sha512-ELxP4TlX3yr2v5rM7Sb70SqStq5NvI15c0j9j/gjsrO5vaw+fnnpovCLEGIcpeGfifkuqJwl4fon6b+KdrODYQ==", + "license": "MIT", + "dependencies": { + "prosemirror-model": "^1.25.0" + } + }, + "node_modules/prosemirror-schema-list": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/prosemirror-schema-list/-/prosemirror-schema-list-1.5.1.tgz", + "integrity": "sha512-927lFx/uwyQaGwJxLWCZRkjXG0p48KpMj6ueoYiu4JX05GGuGcgzAy62dfiV8eFZftgyBUvLx76RsMe20fJl+Q==", + "license": "MIT", + "dependencies": { + "prosemirror-model": "^1.0.0", + "prosemirror-state": "^1.0.0", + "prosemirror-transform": "^1.7.3" + } + }, + "node_modules/prosemirror-state": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/prosemirror-state/-/prosemirror-state-1.4.4.tgz", + "integrity": "sha512-6jiYHH2CIGbCfnxdHbXZ12gySFY/fz/ulZE333G6bPqIZ4F+TXo9ifiR86nAHpWnfoNjOb3o5ESi7J8Uz1jXHw==", + "license": "MIT", + "dependencies": { + "prosemirror-model": "^1.0.0", + "prosemirror-transform": "^1.0.0", + "prosemirror-view": "^1.27.0" + } + }, + "node_modules/prosemirror-tables": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/prosemirror-tables/-/prosemirror-tables-1.8.5.tgz", + "integrity": "sha512-V/0cDCsHKHe/tfWkeCmthNUcEp1IVO3p6vwN8XtwE9PZQLAZJigbw3QoraAdfJPir4NKJtNvOB8oYGKRl+t0Dw==", + "license": "MIT", + "dependencies": { + "prosemirror-keymap": "^1.2.3", + "prosemirror-model": "^1.25.4", + "prosemirror-state": "^1.4.4", + "prosemirror-transform": "^1.10.5", + "prosemirror-view": "^1.41.4" + } + }, + "node_modules/prosemirror-trailing-node": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/prosemirror-trailing-node/-/prosemirror-trailing-node-3.0.0.tgz", + "integrity": "sha512-xiun5/3q0w5eRnGYfNlW1uU9W6x5MoFKWwq/0TIRgt09lv7Hcser2QYV8t4muXbEr+Fwo0geYn79Xs4GKywrRQ==", + "license": "MIT", + "dependencies": { + "@remirror/core-constants": "3.0.0", + "escape-string-regexp": "^4.0.0" + }, + "peerDependencies": { + "prosemirror-model": "^1.22.1", + "prosemirror-state": "^1.4.2", + "prosemirror-view": "^1.33.8" + } + }, + "node_modules/prosemirror-transform": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/prosemirror-transform/-/prosemirror-transform-1.11.0.tgz", + "integrity": "sha512-4I7Ce4KpygXb9bkiPS3hTEk4dSHorfRw8uI0pE8IhxlK2GXsqv5tIA7JUSxtSu7u8APVOTtbUBxTmnHIxVkIJw==", + "license": "MIT", + "dependencies": { + "prosemirror-model": "^1.21.0" + } + }, + "node_modules/prosemirror-view": { + "version": "1.41.6", + "resolved": "https://registry.npmjs.org/prosemirror-view/-/prosemirror-view-1.41.6.tgz", + "integrity": "sha512-mxpcDG4hNQa/CPtzxjdlir5bJFDlm0/x5nGBbStB2BWX+XOQ9M8ekEG+ojqB5BcVu2Rc80/jssCMZzSstJuSYg==", + "license": "MIT", + "dependencies": { + "prosemirror-model": "^1.20.0", + "prosemirror-state": "^1.0.0", + "prosemirror-transform": "^1.1.0" + } + }, + "node_modules/prr": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", + "integrity": "sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/punycode.js": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz", + "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/reflect-metadata": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz", + "integrity": "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rollup": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.57.1.tgz", + "integrity": "sha512-oQL6lgK3e2QZeQ7gcgIkS2YZPg5slw37hYufJ3edKlfQSGGm8ICoxswK15ntSzF/a8+h7ekRy7k7oWc3BQ7y8A==", + "dev": true, + "license": "MIT", + "optional": true, + "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.57.1", + "@rollup/rollup-android-arm64": "4.57.1", + "@rollup/rollup-darwin-arm64": "4.57.1", + "@rollup/rollup-darwin-x64": "4.57.1", + "@rollup/rollup-freebsd-arm64": "4.57.1", + "@rollup/rollup-freebsd-x64": "4.57.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.57.1", + "@rollup/rollup-linux-arm-musleabihf": "4.57.1", + "@rollup/rollup-linux-arm64-gnu": "4.57.1", + "@rollup/rollup-linux-arm64-musl": "4.57.1", + "@rollup/rollup-linux-loong64-gnu": "4.57.1", + "@rollup/rollup-linux-loong64-musl": "4.57.1", + "@rollup/rollup-linux-ppc64-gnu": "4.57.1", + "@rollup/rollup-linux-ppc64-musl": "4.57.1", + "@rollup/rollup-linux-riscv64-gnu": "4.57.1", + "@rollup/rollup-linux-riscv64-musl": "4.57.1", + "@rollup/rollup-linux-s390x-gnu": "4.57.1", + "@rollup/rollup-linux-x64-gnu": "4.57.1", + "@rollup/rollup-linux-x64-musl": "4.57.1", + "@rollup/rollup-openbsd-x64": "4.57.1", + "@rollup/rollup-openharmony-arm64": "4.57.1", + "@rollup/rollup-win32-arm64-msvc": "4.57.1", + "@rollup/rollup-win32-ia32-msvc": "4.57.1", + "@rollup/rollup-win32-x64-gnu": "4.57.1", + "@rollup/rollup-win32-x64-msvc": "4.57.1", + "fsevents": "~2.3.2" + } + }, + "node_modules/rope-sequence": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/rope-sequence/-/rope-sequence-1.3.4.tgz", + "integrity": "sha512-UT5EDe2cu2E/6O4igUr5PSFs23nvvukicWHx6GnOPlHAiiYbzNuCRQCuiUdHJQcqKalLKlrYJnjY0ySGsXNQXQ==", + "license": "MIT" + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/rxjs": { + "version": "7.8.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", + "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/sass": { + "version": "1.97.3", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.97.3.tgz", + "integrity": "sha512-fDz1zJpd5GycprAbu4Q2PV/RprsRtKC/0z82z0JLgdytmcq0+ujJbJ/09bPGDxCLkKY3Np5cRAOcWiVkLXJURg==", + "dev": true, + "license": "MIT", + "dependencies": { + "chokidar": "^4.0.0", + "immutable": "^5.0.2", + "source-map-js": ">=0.6.2 <2.0.0" + }, + "bin": { + "sass": "sass.js" + }, + "engines": { + "node": ">=14.0.0" + }, + "optionalDependencies": { + "@parcel/watcher": "^2.4.1" + } + }, + "node_modules/sax": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.4.tgz", + "integrity": "sha512-1n3r/tGXO6b6VXMdFT54SHzT9ytu9yr7TaELowdYpMqY/Ao7EnlQGmAQ1+RatX7Tkkdm6hONI2owqNx2aZj5Sw==", + "dev": true, + "license": "BlueOak-1.0.0", + "optional": true, + "engines": { + "node": ">=11.0.0" + } + }, + "node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tippy.js": { + "version": "6.3.7", + "resolved": "https://registry.npmjs.org/tippy.js/-/tippy.js-6.3.7.tgz", + "integrity": "sha512-E1d3oP2emgJ9dRQZdf3Kkn0qJgI6ZLpyS5z6ZkY1DF3kaQaBsGZsndEpHwx+eC+tYM41HaSNvNtLx8tU57FzTQ==", + "license": "MIT", + "dependencies": { + "@popperjs/core": "^2.9.0" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/typescript": { + "version": "5.7.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.3.tgz", + "integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/uc.micro": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz", + "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==", + "license": "MIT" + }, + "node_modules/update-browserslist-db": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", + "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/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true, + "license": "MIT" + }, + "node_modules/w3c-keyname": { + "version": "2.2.8", + "resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz", + "integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==", + "license": "MIT" + }, + "node_modules/wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", + "dev": true, + "license": "MIT", + "dependencies": { + "defaults": "^1.0.3" + } + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/y-prosemirror": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/y-prosemirror/-/y-prosemirror-1.3.7.tgz", + "integrity": "sha512-NpM99WSdD4Fx4if5xOMDpPtU3oAmTSjlzh5U4353ABbRHl1HtAFUx6HlebLZfyFxXN9jzKMDkVbcRjqOZVkYQg==", + "license": "MIT", + "optional": true, + "dependencies": { + "lib0": "^0.2.109" + }, + "engines": { + "node": ">=16.0.0", + "npm": ">=8.0.0" + }, + "funding": { + "type": "GitHub Sponsors ❤", + "url": "https://github.com/sponsors/dmonad" + }, + "peerDependencies": { + "prosemirror-model": "^1.7.1", + "prosemirror-state": "^1.2.3", + "prosemirror-view": "^1.9.10", + "y-protocols": "^1.0.1", + "yjs": "^13.5.38" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/yjs": { + "version": "13.6.29", + "resolved": "https://registry.npmjs.org/yjs/-/yjs-13.6.29.tgz", + "integrity": "sha512-kHqDPdltoXH+X4w1lVmMtddE3Oeqq48nM40FD5ojTd8xYhQpzIDcfE2keMSU5bAgRPJBe225WTUdyUgj1DtbiQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "lib0": "^0.2.99" + }, + "engines": { + "node": ">=16.0.0", + "npm": ">=8.0.0" + }, + "funding": { + "type": "GitHub Sponsors ❤", + "url": "https://github.com/sponsors/dmonad" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..76efa02 --- /dev/null +++ b/package.json @@ -0,0 +1,75 @@ +{ + "name": "@sda/rich-text-elements-ui", + "version": "0.1.0", + "description": "Angular components for rich text editing with WYSIWYG, markdown, templates, and collaboration powered by TipTap and @sda/base-ui", + "keywords": [ + "angular", + "rich-text", + "wysiwyg", + "editor", + "markdown", + "tiptap", + "collaboration", + "templates", + "components", + "ui" + ], + "repository": { + "type": "git", + "url": "https://git.sky-ai.com/ui-core-design/rich-text-elements-ui.git" + }, + "license": "MIT", + "sideEffects": false, + "scripts": { + "build": "ng-packagr -p ng-package.json", + "build:dev": "./build-for-dev.sh" + }, + "dependencies": { + "@tiptap/core": "^2.6.0", + "@tiptap/starter-kit": "^2.6.0", + "@tiptap/pm": "^2.6.0", + "@tiptap/extension-placeholder": "^2.6.0", + "@tiptap/extension-character-count": "^2.6.0", + "@tiptap/extension-color": "^2.6.0", + "@tiptap/extension-text-style": "^2.6.0", + "@tiptap/extension-highlight": "^2.6.0", + "@tiptap/extension-link": "^2.6.0", + "@tiptap/extension-image": "^2.6.0", + "@tiptap/extension-table": "^2.6.0", + "@tiptap/extension-table-row": "^2.6.0", + "@tiptap/extension-table-cell": "^2.6.0", + "@tiptap/extension-table-header": "^2.6.0", + "@tiptap/extension-task-list": "^2.6.0", + "@tiptap/extension-task-item": "^2.6.0", + "@tiptap/extension-underline": "^2.6.0", + "@tiptap/extension-text-align": "^2.6.0", + "@tiptap/extension-subscript": "^2.6.0", + "@tiptap/extension-superscript": "^2.6.0", + "@tiptap/extension-mention": "^2.6.0", + "@tiptap/extension-code-block-lowlight": "^2.6.0", + "@tiptap/extension-bubble-menu": "^2.6.0", + "@tiptap/extension-floating-menu": "^2.6.0", + "lowlight": "^3.1.0", + "marked": "^12.0.0" + }, + "optionalDependencies": { + "yjs": "^13.6.0", + "y-prosemirror": "^1.2.0", + "@tiptap/extension-collaboration": "^2.6.0", + "@tiptap/extension-collaboration-cursor": "^2.6.0" + }, + "peerDependencies": { + "@angular/common": "^19.0.0", + "@angular/core": "^19.0.0", + "@sda/base-ui": "*" + }, + "devDependencies": { + "@angular/common": "^19.1.0", + "@angular/compiler": "^19.1.0", + "@angular/compiler-cli": "^19.1.0", + "@angular/core": "^19.1.0", + "@angular/forms": "^19.2.18", + "ng-packagr": "^19.1.0", + "typescript": "~5.7.2" + } +} diff --git a/src/components/index.ts b/src/components/index.ts new file mode 100644 index 0000000..c480c32 --- /dev/null +++ b/src/components/index.ts @@ -0,0 +1,14 @@ +export * from './rt-editor'; +export * from './rt-toolbar'; +export * from './rt-bubble-menu'; +export * from './rt-markdown-editor'; +export * from './rt-template-editor'; +export * from './rt-variable-picker'; +export * from './rt-comment-sidebar'; +export * from './rt-track-changes'; +export * from './rt-mention'; +export * from './rt-link-dialog'; +export * from './rt-image-dialog'; +export * from './rt-table-inserter'; +export * from './rt-color-picker'; +export * from './rt-code-block'; diff --git a/src/components/rt-bubble-menu/index.ts b/src/components/rt-bubble-menu/index.ts new file mode 100644 index 0000000..5fe6c53 --- /dev/null +++ b/src/components/rt-bubble-menu/index.ts @@ -0,0 +1 @@ +export * from './rt-bubble-menu.component'; diff --git a/src/components/rt-bubble-menu/rt-bubble-menu.component.html b/src/components/rt-bubble-menu/rt-bubble-menu.component.html new file mode 100644 index 0000000..0eb0a73 --- /dev/null +++ b/src/components/rt-bubble-menu/rt-bubble-menu.component.html @@ -0,0 +1,15 @@ +
+ @for (item of actions; track item.action) { + + + + } +
diff --git a/src/components/rt-bubble-menu/rt-bubble-menu.component.scss b/src/components/rt-bubble-menu/rt-bubble-menu.component.scss new file mode 100644 index 0000000..540c9b2 --- /dev/null +++ b/src/components/rt-bubble-menu/rt-bubble-menu.component.scss @@ -0,0 +1,18 @@ +@use '../../styles/tokens'; + +.rt-bubble-menu { + display: flex; + align-items: center; + gap: 0.125rem; + padding: 0.25rem; + background: var(--rt-bubble-bg); + border: 1px solid var(--rt-bubble-border); + border-radius: var(--rt-bubble-radius); + box-shadow: var(--rt-bubble-shadow); + + // Active state override for base-ui ghost buttons + :host ::ng-deep ui-button.is-active .ui-button { + background: var(--rt-toolbar-btn-active); + color: var(--rt-toolbar-btn-active-text); + } +} diff --git a/src/components/rt-bubble-menu/rt-bubble-menu.component.ts b/src/components/rt-bubble-menu/rt-bubble-menu.component.ts new file mode 100644 index 0000000..28df552 --- /dev/null +++ b/src/components/rt-bubble-menu/rt-bubble-menu.component.ts @@ -0,0 +1,87 @@ +import { + Component, + ChangeDetectionStrategy, + OnInit, + OnDestroy, + input, + output, + signal, + inject, + ElementRef, + viewChild, +} from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { ButtonComponent, IconComponent, IconRegistry, TooltipDirective } from '@sda/base-ui'; +import { BubbleMenuPlugin } from '@tiptap/extension-bubble-menu'; +import type { Editor } from '@tiptap/core'; +import type { RtToolbarAction } from '../../types/toolbar.types'; +import { registerRtIcons } from '../../utils/icons.utils'; + +@Component({ + selector: 'rt-bubble-menu', + standalone: true, + imports: [CommonModule, ButtonComponent, IconComponent, TooltipDirective], + templateUrl: './rt-bubble-menu.component.html', + styleUrl: './rt-bubble-menu.component.scss', + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class RtBubbleMenuComponent implements OnInit, OnDestroy { + readonly editor = input.required(); + readonly action = output<{ action: RtToolbarAction; value?: string }>(); + + readonly menuElement = viewChild>('bubbleMenu'); + private pluginKey = 'rtBubbleMenu'; + + readonly visible = signal(false); + + readonly actions: { action: RtToolbarAction; label: string; icon: string }[] = [ + { action: 'bold', label: 'Bold', icon: 'bold' }, + { action: 'italic', label: 'Italic', icon: 'italic' }, + { action: 'underline', label: 'Underline', icon: 'underline' }, + { action: 'strikethrough', label: 'Strikethrough', icon: 'strikethrough' }, + { action: 'code', label: 'Code', icon: 'code' }, + { action: 'link', label: 'Link', icon: 'link' }, + ]; + + constructor() { + registerRtIcons(inject(IconRegistry)); + } + + ngOnInit(): void { + const el = this.menuElement()?.nativeElement; + if (!el) return; + + const editor = this.editor(); + editor.registerPlugin( + BubbleMenuPlugin({ + pluginKey: this.pluginKey, + editor, + element: el, + tippyOptions: { + duration: 150, + placement: 'top', + }, + shouldShow: ({ state }) => { + const { from, to } = state.selection; + const show = from !== to; + this.visible.set(show); + return show; + }, + }), + ); + } + + ngOnDestroy(): void { + this.editor().unregisterPlugin(this.pluginKey); + } + + isActive(actionName: RtToolbarAction): boolean { + return this.editor().isActive( + actionName === 'strikethrough' ? 'strike' : actionName, + ); + } + + onAction(actionName: RtToolbarAction): void { + this.action.emit({ action: actionName }); + } +} diff --git a/src/components/rt-code-block/index.ts b/src/components/rt-code-block/index.ts new file mode 100644 index 0000000..b048c96 --- /dev/null +++ b/src/components/rt-code-block/index.ts @@ -0,0 +1 @@ +export * from './rt-code-block.component'; diff --git a/src/components/rt-code-block/rt-code-block.component.html b/src/components/rt-code-block/rt-code-block.component.html new file mode 100644 index 0000000..bbf0b39 --- /dev/null +++ b/src/components/rt-code-block/rt-code-block.component.html @@ -0,0 +1,14 @@ +
+
+ +
+
+ +
+
diff --git a/src/components/rt-code-block/rt-code-block.component.scss b/src/components/rt-code-block/rt-code-block.component.scss new file mode 100644 index 0000000..38271a6 --- /dev/null +++ b/src/components/rt-code-block/rt-code-block.component.scss @@ -0,0 +1,52 @@ +@use '../../styles/tokens'; + +.rt-code-block { + background: var(--rt-code-bg); + border: 1px solid var(--rt-code-border); + border-radius: var(--rt-code-radius); + overflow: hidden; + + &__header { + display: flex; + align-items: center; + padding: 0.375rem 0.75rem; + background: var(--rt-toolbar-bg); + border-bottom: 1px solid var(--rt-code-border); + } + + &__select { + border: 1px solid var(--rt-code-border); + border-radius: var(--rt-toolbar-btn-radius); + background: transparent; + color: var(--rt-editor-text); + font-size: 0.6875rem; + padding: 0.125rem 0.375rem; + cursor: pointer; + outline: none; + + &:focus-visible { + border-color: var(--rt-editor-border-focus); + } + } + + &__content { + padding: 0.75rem 1rem; + font-family: var(--rt-code-font-family); + font-size: var(--rt-code-font-size); + line-height: 1.5; + overflow-x: auto; + + pre { + margin: 0; + background: none; + border: none; + padding: 0; + } + + code { + background: none; + padding: 0; + color: inherit; + } + } +} diff --git a/src/components/rt-code-block/rt-code-block.component.ts b/src/components/rt-code-block/rt-code-block.component.ts new file mode 100644 index 0000000..0759d57 --- /dev/null +++ b/src/components/rt-code-block/rt-code-block.component.ts @@ -0,0 +1,37 @@ +import { + Component, + ChangeDetectionStrategy, + input, + output, + inject, +} from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { FormsModule } from '@angular/forms'; +import { SelectComponent, type SelectOption } from '@sda/base-ui'; +import { RICH_TEXT_CONFIG } from '../../providers/rich-text-config.provider'; + +@Component({ + selector: 'rt-code-block', + standalone: true, + imports: [CommonModule, FormsModule, SelectComponent], + templateUrl: './rt-code-block.component.html', + styleUrl: './rt-code-block.component.scss', + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class RtCodeBlockComponent { + private config = inject(RICH_TEXT_CONFIG); + + readonly language = input(''); + readonly languageChange = output(); + + readonly languages = this.config.codeLanguages; + + readonly languageOptions: SelectOption[] = [ + { value: '', label: 'Plain text' }, + ...this.config.codeLanguages.map(lang => ({ value: lang, label: lang })), + ]; + + onLanguageChange(value: string): void { + this.languageChange.emit(value); + } +} diff --git a/src/components/rt-color-picker/index.ts b/src/components/rt-color-picker/index.ts new file mode 100644 index 0000000..c64ec39 --- /dev/null +++ b/src/components/rt-color-picker/index.ts @@ -0,0 +1 @@ +export * from './rt-color-picker.component'; diff --git a/src/components/rt-color-picker/rt-color-picker.component.html b/src/components/rt-color-picker/rt-color-picker.component.html new file mode 100644 index 0000000..ee6e3d8 --- /dev/null +++ b/src/components/rt-color-picker/rt-color-picker.component.html @@ -0,0 +1,30 @@ +@if (open()) { +
+
+ @for (color of presetColors; track color) { + + } +
+ +
+ + Apply +
+ +
+ Clear color +
+
+} diff --git a/src/components/rt-color-picker/rt-color-picker.component.scss b/src/components/rt-color-picker/rt-color-picker.component.scss new file mode 100644 index 0000000..b1b7b5b --- /dev/null +++ b/src/components/rt-color-picker/rt-color-picker.component.scss @@ -0,0 +1,85 @@ +@use '../../styles/tokens'; + +.rt-color-picker { + background: var(--rt-editor-bg); + border: 1px solid var(--rt-editor-border); + border-radius: var(--rt-editor-radius); + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); + padding: 0.75rem; + width: 200px; + + &__swatches { + display: grid; + grid-template-columns: repeat(6, 1fr); + gap: 3px; + margin-bottom: 0.5rem; + } + + &__swatch { + width: 24px; + height: 24px; + border: 1px solid var(--rt-editor-border); + border-radius: 3px; + cursor: pointer; + padding: 0; + transition: transform var(--rt-transition); + + &:hover { + transform: scale(1.15); + } + + &.is-active { + outline: 2px solid var(--rt-editor-border-focus); + outline-offset: 1px; + } + } + + &__custom { + display: flex; + align-items: center; + gap: 0.5rem; + margin-bottom: 0.5rem; + padding-top: 0.5rem; + border-top: 1px solid var(--rt-editor-border); + } + + &__input { + width: 2rem; + height: 2rem; + border: 1px solid var(--rt-editor-border); + border-radius: 3px; + cursor: pointer; + padding: 0; + background: none; + } + + &__apply { + flex: 1; + padding: 0.25rem 0.5rem; + border: 1px solid var(--rt-editor-border); + border-radius: var(--rt-toolbar-btn-radius); + background: transparent; + color: var(--rt-editor-text); + font-size: 0.75rem; + cursor: pointer; + + &:hover { background: var(--rt-toolbar-btn-hover); } + } + + &__actions { + padding-top: 0.5rem; + border-top: 1px solid var(--rt-editor-border); + } + + &__clear { + width: 100%; + padding: 0.25rem; + border: none; + background: transparent; + color: var(--color-error-500, #ef4444); + font-size: 0.75rem; + cursor: pointer; + + &:hover { text-decoration: underline; } + } +} diff --git a/src/components/rt-color-picker/rt-color-picker.component.ts b/src/components/rt-color-picker/rt-color-picker.component.ts new file mode 100644 index 0000000..61611f2 --- /dev/null +++ b/src/components/rt-color-picker/rt-color-picker.component.ts @@ -0,0 +1,54 @@ +import { + Component, + ChangeDetectionStrategy, + input, + output, + signal, +} from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { FormsModule } from '@angular/forms'; +import { ButtonComponent } from '@sda/base-ui'; +import { TooltipDirective } from '@sda/base-ui'; + +const PRESET_COLORS = [ + '#000000', '#434343', '#666666', '#999999', '#cccccc', '#ffffff', + '#ef4444', '#f97316', '#eab308', '#22c55e', '#3b82f6', '#8b5cf6', + '#dc2626', '#ea580c', '#ca8a04', '#16a34a', '#2563eb', '#7c3aed', + '#991b1b', '#9a3412', '#854d0e', '#166534', '#1d4ed8', '#5b21b6', +]; + +@Component({ + selector: 'rt-color-picker', + standalone: true, + imports: [CommonModule, FormsModule, ButtonComponent, TooltipDirective], + templateUrl: './rt-color-picker.component.html', + styleUrl: './rt-color-picker.component.scss', + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class RtColorPickerComponent { + readonly open = input(false); + readonly currentColor = input(null); + + readonly colorSelect = output(); + readonly colorClear = output(); + readonly close = output(); + + readonly customColor = signal('#000000'); + readonly presetColors = PRESET_COLORS; + + onPresetClick(color: string): void { + this.colorSelect.emit(color); + } + + onCustomApply(): void { + this.colorSelect.emit(this.customColor()); + } + + onClear(): void { + this.colorClear.emit(); + } + + onClose(): void { + this.close.emit(); + } +} diff --git a/src/components/rt-comment-sidebar/index.ts b/src/components/rt-comment-sidebar/index.ts new file mode 100644 index 0000000..fc1cbbe --- /dev/null +++ b/src/components/rt-comment-sidebar/index.ts @@ -0,0 +1 @@ +export * from './rt-comment-sidebar.component'; diff --git a/src/components/rt-comment-sidebar/rt-comment-sidebar.component.html b/src/components/rt-comment-sidebar/rt-comment-sidebar.component.html new file mode 100644 index 0000000..1edc301 --- /dev/null +++ b/src/components/rt-comment-sidebar/rt-comment-sidebar.component.html @@ -0,0 +1,106 @@ +@if (open()) { +
+
+

Comments

+
+ Active ({{ activeCount() }}) + Resolved ({{ resolvedCount() }}) + All +
+
+ +
+ @for (comment of filteredComments(); track comment.id) { + +
+
+ {{ comment.author.name[0] }} + {{ comment.author.name }} +
+ {{ formatDate(comment.createdAt) }} +
+ + @if (comment.quotedText) { +
{{ comment.quotedText }}
+ } + +
{{ comment.content }}
+ + + @for (reply of comment.replies; track reply.id) { +
+
+ {{ reply.author.name[0] }} + {{ reply.author.name }} + {{ formatDate(reply.createdAt) }} +
+
{{ reply.content }}
+
+ } + + + @if (replyingTo() === comment.id) { +
+ +
+ Cancel + Reply +
+
+ } + + +
+ @if (replyingTo() !== comment.id) { + Reply + } + @if (!comment.resolved) { + + Resolve + + } @else { + Reopen + } + + Delete + +
+
+ } + + @if (filteredComments().length === 0) { +
+ No {{ filter() === 'all' ? '' : filter() }} comments +
+ } +
+
+} diff --git a/src/components/rt-comment-sidebar/rt-comment-sidebar.component.scss b/src/components/rt-comment-sidebar/rt-comment-sidebar.component.scss new file mode 100644 index 0000000..cfeed8d --- /dev/null +++ b/src/components/rt-comment-sidebar/rt-comment-sidebar.component.scss @@ -0,0 +1,227 @@ +@use '../../styles/tokens'; + +.rt-comment-sidebar { + background: var(--rt-comment-bg); + border: 1px solid var(--rt-comment-border); + border-radius: var(--rt-editor-radius); + width: 320px; + max-height: 600px; + display: flex; + flex-direction: column; + + &__header { + padding: 0.75rem 1rem; + border-bottom: 1px solid var(--rt-comment-border); + } + + &__title { + font-size: 0.9375rem; + font-weight: 600; + color: var(--rt-editor-text); + margin: 0 0 0.5rem; + } + + &__filters { + display: flex; + gap: 0; + } + + &__filter-btn { + padding: 0.25rem 0.625rem; + border: 1px solid var(--rt-comment-border); + background: transparent; + color: var(--rt-status-text); + font-size: 0.6875rem; + cursor: pointer; + transition: all var(--rt-transition); + + &:first-child { border-radius: var(--rt-toolbar-btn-radius) 0 0 var(--rt-toolbar-btn-radius); } + &:last-child { border-radius: 0 var(--rt-toolbar-btn-radius) var(--rt-toolbar-btn-radius) 0; } + &:not(:first-child) { border-left: none; } + + &:hover { background: var(--rt-toolbar-btn-hover); } + + &.is-active { + background: var(--rt-toolbar-btn-active); + color: var(--rt-toolbar-btn-active-text); + border-color: var(--rt-toolbar-btn-active-text); + } + } + + &__list { + flex: 1; + overflow-y: auto; + padding: 0.5rem; + } + + &__comment { + padding: 0.75rem; + border: 1px solid var(--rt-comment-border); + border-radius: var(--rt-toolbar-btn-radius); + margin-bottom: 0.5rem; + transition: background var(--rt-transition); + + &.is-resolved { + background: var(--rt-comment-resolved-bg); + opacity: 0.8; + } + } + + &__comment-header { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 0.375rem; + } + + &__author { + display: flex; + align-items: center; + gap: 0.375rem; + } + + &__avatar { + width: 1.5rem; + height: 1.5rem; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + font-size: 0.625rem; + font-weight: 700; + color: #fff; + + &--sm { + width: 1.125rem; + height: 1.125rem; + font-size: 0.5rem; + } + } + + &__author-name { + font-size: 0.75rem; + font-weight: 600; + color: var(--rt-editor-text); + } + + &__time { + font-size: 0.625rem; + color: var(--rt-status-text); + } + + &__quote { + font-size: 0.75rem; + color: var(--rt-blockquote-text); + background: var(--rt-blockquote-bg); + border-left: 2px solid var(--rt-blockquote-border); + padding: 0.25rem 0.5rem; + margin-bottom: 0.375rem; + font-style: italic; + } + + &__content { + font-size: 0.8125rem; + color: var(--rt-editor-text); + line-height: 1.4; + margin-bottom: 0.375rem; + } + + &__reply { + padding: 0.375rem 0 0.375rem 1rem; + border-left: 2px solid var(--rt-comment-border); + margin: 0.375rem 0; + } + + &__reply-header { + display: flex; + align-items: center; + gap: 0.25rem; + margin-bottom: 0.125rem; + } + + &__reply-content { + font-size: 0.75rem; + color: var(--rt-editor-text); + line-height: 1.4; + } + + &__reply-form { + margin-top: 0.375rem; + } + + &__reply-input { + width: 100%; + padding: 0.375rem 0.5rem; + border: 1px solid var(--rt-comment-border); + border-radius: var(--rt-toolbar-btn-radius); + background: transparent; + color: var(--rt-editor-text); + font-size: 0.75rem; + resize: none; + outline: none; + + &:focus { border-color: var(--rt-editor-border-focus); } + &::placeholder { color: var(--rt-editor-placeholder); } + } + + &__reply-actions { + display: flex; + justify-content: flex-end; + gap: 0.375rem; + margin-top: 0.375rem; + } + + &__actions { + display: flex; + gap: 0.5rem; + padding-top: 0.375rem; + border-top: 1px solid var(--rt-comment-border); + margin-top: 0.375rem; + } + + &__action { + border: none; + background: transparent; + font-size: 0.6875rem; + color: var(--rt-toolbar-btn-active-text); + cursor: pointer; + padding: 0; + + &:hover { text-decoration: underline; } + + &--danger { + color: var(--color-error-500, #ef4444); + } + } + + &__btn { + padding: 0.25rem 0.625rem; + border-radius: var(--rt-toolbar-btn-radius); + font-size: 0.6875rem; + font-weight: 500; + cursor: pointer; + transition: all var(--rt-transition); + border: 1px solid transparent; + + &--primary { + background: var(--color-primary-500, #3b82f6); + color: #fff; + &:hover:not(:disabled) { opacity: 0.9; } + &:disabled { opacity: 0.5; cursor: not-allowed; } + } + + &--ghost { + background: transparent; + color: var(--rt-editor-text); + border-color: var(--rt-comment-border); + &:hover { background: var(--rt-toolbar-btn-hover); } + } + } + + &__empty { + padding: 2rem; + text-align: center; + font-size: 0.8125rem; + color: var(--rt-status-text); + } +} diff --git a/src/components/rt-comment-sidebar/rt-comment-sidebar.component.ts b/src/components/rt-comment-sidebar/rt-comment-sidebar.component.ts new file mode 100644 index 0000000..436a865 --- /dev/null +++ b/src/components/rt-comment-sidebar/rt-comment-sidebar.component.ts @@ -0,0 +1,105 @@ +import { + Component, + ChangeDetectionStrategy, + input, + output, + signal, + computed, + inject, +} from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { FormsModule } from '@angular/forms'; +import { ButtonComponent } from '@sda/base-ui'; +import { CardComponent } from '@sda/base-ui'; +import { TextareaComponent } from '@sda/base-ui'; +import { BadgeComponent } from '@sda/base-ui'; +import { IconComponent } from '@sda/base-ui'; +import { CollaborationService } from '../../services/collaboration.service'; +import type { RtComment, RtCollabUser } from '../../types/collaboration.types'; +import type { RtCommentEvent } from '../../types/event.types'; + +@Component({ + selector: 'rt-comment-sidebar', + standalone: true, + imports: [CommonModule, FormsModule, ButtonComponent, CardComponent, TextareaComponent, BadgeComponent, IconComponent], + providers: [CollaborationService], + templateUrl: './rt-comment-sidebar.component.html', + styleUrl: './rt-comment-sidebar.component.scss', + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class RtCommentSidebarComponent { + protected collabService = inject(CollaborationService); + + readonly comments = input([]); + readonly currentUser = input.required(); + readonly open = input(true); + + readonly commentEvent = output(); + + readonly filter = signal<'active' | 'resolved' | 'all'>('active'); + readonly replyingTo = signal(null); + readonly replyText = signal(''); + + readonly filteredComments = computed(() => { + const allComments = this.comments(); + switch (this.filter()) { + case 'active': return allComments.filter(c => !c.resolved); + case 'resolved': return allComments.filter(c => c.resolved); + default: return allComments; + } + }); + + readonly activeCount = computed(() => this.comments().filter(c => !c.resolved).length); + readonly resolvedCount = computed(() => this.comments().filter(c => c.resolved).length); + + setFilter(filter: 'active' | 'resolved' | 'all'): void { + this.filter.set(filter); + } + + onResolve(comment: RtComment): void { + this.commentEvent.emit({ type: 'resolve', comment }); + } + + onReopen(comment: RtComment): void { + this.commentEvent.emit({ type: 'reopen', comment }); + } + + onDelete(comment: RtComment): void { + this.commentEvent.emit({ type: 'delete', comment }); + } + + startReply(commentId: string): void { + this.replyingTo.set(commentId); + this.replyText.set(''); + } + + cancelReply(): void { + this.replyingTo.set(null); + this.replyText.set(''); + } + + submitReply(comment: RtComment): void { + const text = this.replyText().trim(); + if (!text) return; + + const reply = this.collabService.addReply(comment.id, this.currentUser(), text); + this.commentEvent.emit({ type: 'reply', comment, reply }); + this.replyingTo.set(null); + this.replyText.set(''); + } + + formatDate(isoString: string): string { + const date = new Date(isoString); + const now = new Date(); + const diff = now.getTime() - date.getTime(); + const minutes = Math.floor(diff / 60000); + + if (minutes < 1) return 'Just now'; + if (minutes < 60) return `${minutes}m ago`; + const hours = Math.floor(minutes / 60); + if (hours < 24) return `${hours}h ago`; + const days = Math.floor(hours / 24); + if (days < 7) return `${days}d ago`; + return date.toLocaleDateString(); + } +} diff --git a/src/components/rt-editor/index.ts b/src/components/rt-editor/index.ts new file mode 100644 index 0000000..2ec81f8 --- /dev/null +++ b/src/components/rt-editor/index.ts @@ -0,0 +1 @@ +export * from './rt-editor.component'; diff --git a/src/components/rt-editor/rt-editor.component.html b/src/components/rt-editor/rt-editor.component.html new file mode 100644 index 0000000..d832838 --- /dev/null +++ b/src/components/rt-editor/rt-editor.component.html @@ -0,0 +1,37 @@ +
+ @if (showToolbar()) { + + } + +
+ + @if (showBubbleMenu() && editor()) { + + } + + @if (showWordCount() || showCharacterCount()) { +
+ @if (showWordCount()) { + {{ editorState.wordCount() }} words + } + @if (showCharacterCount()) { + + {{ editorState.characterCount() }} characters + @if (maxLength()) { + / {{ maxLength() }} + } + + } +
+ } +
diff --git a/src/components/rt-editor/rt-editor.component.scss b/src/components/rt-editor/rt-editor.component.scss new file mode 100644 index 0000000..a6bd764 --- /dev/null +++ b/src/components/rt-editor/rt-editor.component.scss @@ -0,0 +1,41 @@ +@use '../../styles/tokens'; +@use '../../styles/mixins'; + +.rt-editor { + @include mixins.rt-container; + @include mixins.rt-card; + + &--focused { + border-color: var(--rt-editor-border-focus); + box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.15); + } + + &__content { + padding: var(--rt-editor-padding); + min-height: var(--rt-editor-min-height); + cursor: text; + } + + &__status-bar { + display: flex; + align-items: center; + justify-content: flex-end; + gap: 1rem; + padding: 0.375rem var(--rt-editor-padding); + background: var(--rt-status-bg); + border-top: 1px solid var(--rt-editor-border); + } + + &__count { + font-size: var(--rt-status-font-size); + color: var(--rt-status-text); + } +} + +// TipTap ProseMirror styling — :host must be outermost selector +// We use ::ng-deep because TipTap creates DOM outside Angular's template +:host ::ng-deep .rt-editor__content .tiptap { + @include mixins.rt-prose; + outline: none; + min-height: inherit; +} diff --git a/src/components/rt-editor/rt-editor.component.ts b/src/components/rt-editor/rt-editor.component.ts new file mode 100644 index 0000000..9238203 --- /dev/null +++ b/src/components/rt-editor/rt-editor.component.ts @@ -0,0 +1,296 @@ +import { + Component, + ChangeDetectionStrategy, + OnInit, + OnDestroy, + input, + output, + signal, + computed, + effect, + inject, + ElementRef, + viewChild, +} from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { Editor } from '@tiptap/core'; +import StarterKit from '@tiptap/starter-kit'; +import Placeholder from '@tiptap/extension-placeholder'; +import CharacterCount from '@tiptap/extension-character-count'; +import Underline from '@tiptap/extension-underline'; +import TextAlign from '@tiptap/extension-text-align'; +import TextStyle from '@tiptap/extension-text-style'; +import Color from '@tiptap/extension-color'; +import Highlight from '@tiptap/extension-highlight'; +import Link from '@tiptap/extension-link'; +import Image from '@tiptap/extension-image'; +import Table from '@tiptap/extension-table'; +import TableRow from '@tiptap/extension-table-row'; +import TableCell from '@tiptap/extension-table-cell'; +import TableHeader from '@tiptap/extension-table-header'; +import TaskList from '@tiptap/extension-task-list'; +import TaskItem from '@tiptap/extension-task-item'; +import Subscript from '@tiptap/extension-subscript'; +import Superscript from '@tiptap/extension-superscript'; +import CodeBlockLowlight from '@tiptap/extension-code-block-lowlight'; +import { common, createLowlight } from 'lowlight'; + +import { RICH_TEXT_CONFIG } from '../../providers/rich-text-config.provider'; +import { EditorStateService } from '../../services/editor-state.service'; +import { ToolbarService } from '../../services/toolbar.service'; +import { HistoryService } from '../../services/history.service'; +import { RtToolbarComponent } from '../rt-toolbar/rt-toolbar.component'; +import { RtBubbleMenuComponent } from '../rt-bubble-menu/rt-bubble-menu.component'; +import type { RtToolbarPreset, RtToolbarAction } from '../../types/toolbar.types'; +import type { RtContent } from '../../types/editor.types'; +import type { + RtContentChangeEvent, + RtSelectionChangeEvent, + RtFocusChangeEvent, + RtStateChangeEvent, + RtCountChangeEvent, +} from '../../types/event.types'; +import type { RtLinkData, RtImageData } from '../../types/editor.types'; + +@Component({ + selector: 'rt-editor', + standalone: true, + imports: [CommonModule, RtToolbarComponent, RtBubbleMenuComponent], + providers: [EditorStateService, ToolbarService, HistoryService], + templateUrl: './rt-editor.component.html', + styleUrl: './rt-editor.component.scss', + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class RtEditorComponent implements OnInit, OnDestroy { + private config = inject(RICH_TEXT_CONFIG); + protected editorState = inject(EditorStateService); + private toolbarService = inject(ToolbarService); + + // Inputs + readonly content = input(''); + readonly editable = input(this.config.editable); + readonly placeholder = input(this.config.placeholder); + readonly maxLength = input(this.config.maxLength); + readonly toolbarPreset = input(this.config.toolbarPreset); + readonly showToolbar = input(this.config.showToolbar); + readonly showBubbleMenu = input(this.config.showBubbleMenu); + readonly showWordCount = input(this.config.showWordCount); + readonly showCharacterCount = input(this.config.showCharacterCount); + + // Outputs + readonly contentChange = output(); + readonly selectionChange = output(); + readonly focusChange = output(); + readonly stateChange = output(); + readonly countChange = output(); + readonly linkRequest = output(); + readonly imageRequest = output(); + readonly tableRequest = output(); + readonly colorRequest = output(); + + // Template refs + readonly editorContainer = viewChild>('editorContainer'); + + // Editor instance + readonly editor = signal(null); + + // Toolbar groups + readonly toolbarGroups = computed(() => this.toolbarService.getPresetGroups(this.toolbarPreset())); + + constructor() { + // React to content input changes + effect(() => { + const content = this.content(); + const editor = this.editor(); + if (editor && content !== undefined) { + const currentHtml = editor.getHTML(); + if (typeof content === 'string' && content !== currentHtml) { + editor.commands.setContent(content, false); + } else if (typeof content === 'object') { + editor.commands.setContent(content, false); + } + } + }); + + // React to editable changes + effect(() => { + const editable = this.editable(); + const editor = this.editor(); + if (editor) { + editor.setEditable(editable); + } + }); + } + + ngOnInit(): void { + const lowlight = createLowlight(common); + + const extensions = [ + StarterKit.configure({ + codeBlock: false, // Use CodeBlockLowlight instead + }), + Placeholder.configure({ placeholder: this.placeholder() }), + CharacterCount.configure({ + limit: this.maxLength() || undefined, + }), + Underline, + TextAlign.configure({ types: ['heading', 'paragraph'] }), + TextStyle, + Color, + Highlight.configure({ multicolor: true }), + Link.configure({ + openOnClick: false, + autolink: this.config.autoLink, + HTMLAttributes: { rel: 'noopener noreferrer' }, + }), + Image.configure({ inline: false, allowBase64: true }), + Table.configure({ resizable: true }), + TableRow, + TableCell, + TableHeader, + TaskList, + TaskItem.configure({ nested: true }), + Subscript, + Superscript, + CodeBlockLowlight.configure({ lowlight }), + ]; + + const container = this.editorContainer()?.nativeElement; + if (!container) return; + + const editor = new Editor({ + element: container, + extensions, + content: this.content() || '', + editable: this.editable(), + onUpdate: ({ editor: e }) => this.onEditorUpdate(e), + onSelectionUpdate: ({ editor: e }) => this.onEditorSelectionUpdate(e), + onFocus: () => this.onEditorFocus(), + onBlur: () => this.onEditorBlur(), + }); + + this.editor.set(editor); + this.editorState.updateFromEditor(editor); + } + + ngOnDestroy(): void { + this.editor()?.destroy(); + } + + /** Handle toolbar action */ + onToolbarAction(action: RtToolbarAction, value?: string): void { + const editor = this.editor(); + if (!editor) return; + + if (action === 'link') { + this.linkRequest.emit(); + return; + } + if (action === 'image') { + this.imageRequest.emit(); + return; + } + if (action === 'table') { + this.tableRequest.emit(); + return; + } + if (action === 'text-color' || action === 'highlight-color') { + this.colorRequest.emit(action); + return; + } + + this.toolbarService.executeAction(editor, action, value); + } + + /** Set a link on the current selection */ + setLink(linkData: RtLinkData): void { + const editor = this.editor(); + if (!editor) return; + + if (linkData.href) { + editor.chain().focus() + .extendMarkRange('link') + .setLink({ href: linkData.href, target: linkData.target ?? '_blank' }) + .run(); + } else { + editor.chain().focus().unsetLink().run(); + } + } + + /** Insert an image */ + setImage(imageData: RtImageData): void { + const editor = this.editor(); + if (!editor) return; + editor.chain().focus().setImage(imageData).run(); + } + + /** Insert a table */ + insertTable(rows: number, cols: number): void { + const editor = this.editor(); + if (!editor) return; + editor.chain().focus().insertTable({ rows, cols, withHeaderRow: true }).run(); + } + + /** Set text or highlight color */ + setColor(action: RtToolbarAction, color: string): void { + const editor = this.editor(); + if (!editor) return; + this.toolbarService.executeAction(editor, action, color); + } + + private onEditorUpdate(editor: Editor): void { + this.editorState.updateFromEditor(editor); + + this.contentChange.emit({ + html: editor.getHTML(), + json: editor.getJSON(), + text: editor.getText(), + isEmpty: editor.isEmpty, + }); + + this.stateChange.emit({ + canUndo: editor.can().undo(), + canRedo: editor.can().redo(), + activeMarks: this.editorState.activeMarks(), + activeNodes: this.editorState.activeNodes(), + }); + + this.countChange.emit({ + words: this.editorState.wordCount(), + characters: this.editorState.characterCount(), + }); + } + + private onEditorSelectionUpdate(editor: Editor): void { + this.editorState.updateFromEditor(editor); + + const { from, to } = editor.state.selection; + const selectedText = editor.state.doc.textBetween(from, to, ' '); + + this.selectionChange.emit({ + from, + to, + hasSelection: from !== to, + selectedText, + activeMarks: this.editorState.activeMarks(), + activeNodes: this.editorState.activeNodes(), + }); + + this.stateChange.emit({ + canUndo: editor.can().undo(), + canRedo: editor.can().redo(), + activeMarks: this.editorState.activeMarks(), + activeNodes: this.editorState.activeNodes(), + }); + } + + private onEditorFocus(): void { + this.editorState.setFocused(true); + this.focusChange.emit({ focused: true }); + } + + private onEditorBlur(): void { + this.editorState.setFocused(false); + this.focusChange.emit({ focused: false }); + } +} diff --git a/src/components/rt-image-dialog/index.ts b/src/components/rt-image-dialog/index.ts new file mode 100644 index 0000000..f44c05f --- /dev/null +++ b/src/components/rt-image-dialog/index.ts @@ -0,0 +1 @@ +export * from './rt-image-dialog.component'; diff --git a/src/components/rt-image-dialog/rt-image-dialog.component.html b/src/components/rt-image-dialog/rt-image-dialog.component.html new file mode 100644 index 0000000..4a12bfa --- /dev/null +++ b/src/components/rt-image-dialog/rt-image-dialog.component.html @@ -0,0 +1,39 @@ + +
+ + +
+ + +
+
+ + +
diff --git a/src/components/rt-image-dialog/rt-image-dialog.component.scss b/src/components/rt-image-dialog/rt-image-dialog.component.scss new file mode 100644 index 0000000..0a5ee3e --- /dev/null +++ b/src/components/rt-image-dialog/rt-image-dialog.component.scss @@ -0,0 +1,24 @@ +.rt-image-dialog { + &__body { + display: flex; + flex-direction: column; + gap: 1rem; + padding: 1.25rem; + } + + &__row { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 0.75rem; + } + + &__footer { + display: flex; + align-items: center; + gap: 0.5rem; + padding: 0.75rem 1.25rem; + border-top: 1px solid var(--rt-editor-border); + } + + &__spacer { flex: 1; } +} diff --git a/src/components/rt-image-dialog/rt-image-dialog.component.ts b/src/components/rt-image-dialog/rt-image-dialog.component.ts new file mode 100644 index 0000000..39c62e1 --- /dev/null +++ b/src/components/rt-image-dialog/rt-image-dialog.component.ts @@ -0,0 +1,57 @@ +import { + Component, + ChangeDetectionStrategy, + input, + output, + signal, +} from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { FormsModule } from '@angular/forms'; +import { ModalComponent } from '@sda/base-ui'; +import { InputComponent } from '@sda/base-ui'; +import { ButtonComponent } from '@sda/base-ui'; +import type { RtImageData } from '../../types/editor.types'; + +@Component({ + selector: 'rt-image-dialog', + standalone: true, + imports: [CommonModule, FormsModule, ModalComponent, InputComponent, ButtonComponent], + templateUrl: './rt-image-dialog.component.html', + styleUrl: './rt-image-dialog.component.scss', + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class RtImageDialogComponent { + readonly open = input(false); + + readonly confirm = output(); + readonly cancel = output(); + + readonly src = signal(''); + readonly alt = signal(''); + readonly width = signal(undefined); + readonly height = signal(undefined); + + ngOnChanges(): void { + if (this.open()) { + this.src.set(''); + this.alt.set(''); + this.width.set(undefined); + this.height.set(undefined); + } + } + + onConfirm(): void { + const src = this.src().trim(); + if (!src) return; + this.confirm.emit({ + src, + alt: this.alt().trim() || undefined, + width: this.width(), + height: this.height(), + }); + } + + onCancel(): void { + this.cancel.emit(); + } +} diff --git a/src/components/rt-link-dialog/index.ts b/src/components/rt-link-dialog/index.ts new file mode 100644 index 0000000..19719c0 --- /dev/null +++ b/src/components/rt-link-dialog/index.ts @@ -0,0 +1 @@ +export * from './rt-link-dialog.component'; diff --git a/src/components/rt-link-dialog/rt-link-dialog.component.html b/src/components/rt-link-dialog/rt-link-dialog.component.html new file mode 100644 index 0000000..000f670 --- /dev/null +++ b/src/components/rt-link-dialog/rt-link-dialog.component.html @@ -0,0 +1,31 @@ + + + + + diff --git a/src/components/rt-link-dialog/rt-link-dialog.component.scss b/src/components/rt-link-dialog/rt-link-dialog.component.scss new file mode 100644 index 0000000..7032a1c --- /dev/null +++ b/src/components/rt-link-dialog/rt-link-dialog.component.scss @@ -0,0 +1,20 @@ +.rt-link-dialog { + &__body { + display: flex; + flex-direction: column; + gap: 1rem; + padding: 1.25rem; + } + + &__footer { + display: flex; + align-items: center; + gap: 0.5rem; + padding: 0.75rem 1.25rem; + border-top: 1px solid var(--rt-editor-border); + } + + &__spacer { + flex: 1; + } +} diff --git a/src/components/rt-link-dialog/rt-link-dialog.component.ts b/src/components/rt-link-dialog/rt-link-dialog.component.ts new file mode 100644 index 0000000..57b5a07 --- /dev/null +++ b/src/components/rt-link-dialog/rt-link-dialog.component.ts @@ -0,0 +1,61 @@ +import { + Component, + ChangeDetectionStrategy, + input, + output, + signal, +} from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { FormsModule } from '@angular/forms'; +import { ModalComponent } from '@sda/base-ui'; +import { InputComponent } from '@sda/base-ui'; +import { ButtonComponent } from '@sda/base-ui'; +import { ToggleComponent } from '@sda/base-ui'; +import type { RtLinkData } from '../../types/editor.types'; + +@Component({ + selector: 'rt-link-dialog', + standalone: true, + imports: [CommonModule, FormsModule, ModalComponent, InputComponent, ButtonComponent, ToggleComponent], + templateUrl: './rt-link-dialog.component.html', + styleUrl: './rt-link-dialog.component.scss', + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class RtLinkDialogComponent { + readonly open = input(false); + readonly initialUrl = input(''); + readonly initialText = input(''); + + readonly confirm = output(); + readonly remove = output(); + readonly cancel = output(); + + readonly url = signal(''); + readonly text = signal(''); + readonly openInNewTab = signal(true); + + ngOnChanges(): void { + if (this.open()) { + this.url.set(this.initialUrl()); + this.text.set(this.initialText()); + } + } + + onConfirm(): void { + const url = this.url().trim(); + if (!url) return; + this.confirm.emit({ + href: url, + text: this.text().trim() || undefined, + target: this.openInNewTab() ? '_blank' : '_self', + }); + } + + onRemove(): void { + this.remove.emit(); + } + + onCancel(): void { + this.cancel.emit(); + } +} diff --git a/src/components/rt-markdown-editor/index.ts b/src/components/rt-markdown-editor/index.ts new file mode 100644 index 0000000..b35b2b7 --- /dev/null +++ b/src/components/rt-markdown-editor/index.ts @@ -0,0 +1 @@ +export * from './rt-markdown-editor.component'; diff --git a/src/components/rt-markdown-editor/rt-markdown-editor.component.html b/src/components/rt-markdown-editor/rt-markdown-editor.component.html new file mode 100644 index 0000000..f0fd640 --- /dev/null +++ b/src/components/rt-markdown-editor/rt-markdown-editor.component.html @@ -0,0 +1,54 @@ +
+ + @if (showToolbar()) { +
+ +
+ Edit + Split + Preview +
+
+ } + + +
+ @if (viewMode() !== 'preview') { +
+ +
+ } + + @if (viewMode() !== 'edit') { +
+
+
+ } +
+
diff --git a/src/components/rt-markdown-editor/rt-markdown-editor.component.scss b/src/components/rt-markdown-editor/rt-markdown-editor.component.scss new file mode 100644 index 0000000..73af3f7 --- /dev/null +++ b/src/components/rt-markdown-editor/rt-markdown-editor.component.scss @@ -0,0 +1,101 @@ +@use '../../styles/tokens'; +@use '../../styles/mixins'; + +.rt-markdown-editor { + @include mixins.rt-container; + @include mixins.rt-card; + + &__header { + display: flex; + align-items: center; + border-bottom: 1px solid var(--rt-editor-border); + + rt-toolbar { + flex: 1; + } + } + + &__view-modes { + display: flex; + gap: 0; + padding: 0 0.5rem; + border-left: 1px solid var(--rt-editor-border); + } + + &__mode-btn { + padding: 0.375rem 0.625rem; + border: none; + background: transparent; + color: var(--rt-status-text); + font-size: 0.6875rem; + font-weight: 500; + cursor: pointer; + transition: all var(--rt-transition); + border-bottom: 2px solid transparent; + + &:hover { + color: var(--rt-editor-text); + } + + &.is-active { + color: var(--rt-toolbar-btn-active-text); + border-bottom-color: var(--rt-toolbar-btn-active-text); + } + } + + &__content { + display: flex; + min-height: 300px; + + &--edit { + .rt-markdown-editor__edit-pane { flex: 1; } + } + + &--split { + .rt-markdown-editor__edit-pane, + .rt-markdown-editor__preview-pane { + flex: 1; + width: 50%; + } + } + + &--preview { + .rt-markdown-editor__preview-pane { flex: 1; } + } + } + + &__edit-pane { + display: flex; + border-right: 1px solid var(--rt-editor-border); + } + + &__textarea { + flex: 1; + width: 100%; + border: none; + outline: none; + resize: none; + padding: var(--rt-editor-padding); + background: transparent; + color: var(--rt-editor-text); + font-family: var(--rt-code-font-family); + font-size: 0.875rem; + line-height: 1.6; + tab-size: 2; + + &::placeholder { + color: var(--rt-editor-placeholder); + } + } + + &__preview-pane { + overflow-y: auto; + } +} + +// Preview prose styles — :host must be outermost for ::ng-deep +// innerHTML content won't have Angular's _ngcontent attributes +:host ::ng-deep .rt-markdown-editor__preview { + @include mixins.rt-prose; + padding: var(--rt-editor-padding); +} diff --git a/src/components/rt-markdown-editor/rt-markdown-editor.component.ts b/src/components/rt-markdown-editor/rt-markdown-editor.component.ts new file mode 100644 index 0000000..41bda99 --- /dev/null +++ b/src/components/rt-markdown-editor/rt-markdown-editor.component.ts @@ -0,0 +1,169 @@ +import { + Component, + ChangeDetectionStrategy, + input, + output, + signal, + computed, + inject, + effect, + viewChild, + ElementRef, +} from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { FormsModule } from '@angular/forms'; +import { MarkdownService } from '../../services/markdown.service'; +import { ToolbarService } from '../../services/toolbar.service'; +import { ButtonComponent, IconComponent, IconRegistry } from '@sda/base-ui'; +import { RtToolbarComponent } from '../rt-toolbar/rt-toolbar.component'; +import { registerRtIcons } from '../../utils/icons.utils'; +import { wrapSelection, insertLinePrefix, insertBlock, toggleLinePrefix } from '../../utils/markdown.utils'; +import type { RtToolbarAction } from '../../types/toolbar.types'; +import type { RtActiveMarks, RtActiveNodes } from '../../types/editor.types'; + +type ViewMode = 'edit' | 'split' | 'preview'; + +const EMPTY_MARKS: RtActiveMarks = { + bold: false, italic: false, underline: false, strike: false, + code: false, subscript: false, superscript: false, link: false, + highlight: false, color: null, highlightColor: null, +}; + +const EMPTY_NODES: RtActiveNodes = { + paragraph: false, heading: null, bulletList: false, orderedList: false, + taskList: false, blockquote: false, codeBlock: false, table: false, + image: false, horizontalRule: false, textAlign: null, +}; + +@Component({ + selector: 'rt-markdown-editor', + standalone: true, + imports: [CommonModule, FormsModule, ButtonComponent, IconComponent, RtToolbarComponent], + providers: [MarkdownService, ToolbarService], + templateUrl: './rt-markdown-editor.component.html', + styleUrl: './rt-markdown-editor.component.scss', + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class RtMarkdownEditorComponent { + private markdownService = inject(MarkdownService); + private toolbarService = inject(ToolbarService); + + readonly content = input(''); + readonly placeholder = input('Write markdown here...'); + readonly showToolbar = input(true); + + readonly contentChange = output(); + readonly htmlChange = output(); + + readonly markdown = signal(''); + readonly viewMode = signal('split'); + + readonly toolbarGroups = computed(() => this.toolbarService.getPresetGroups('markdown')); + readonly activeMarks = signal(EMPTY_MARKS); + readonly activeNodes = signal(EMPTY_NODES); + + readonly preview = computed(() => { + const md = this.markdown(); + if (!md.trim()) return ''; + return this.markdownService.toHtml(md); + }); + + readonly textareaEl = viewChild>('markdownTextarea'); + + constructor() { + registerRtIcons(inject(IconRegistry)); + + effect(() => { + const content = this.content(); + if (content !== undefined) { + this.markdown.set(content); + } + }); + } + + onMarkdownInput(value: string): void { + this.markdown.set(value); + this.contentChange.emit(value); + this.htmlChange.emit(this.preview()); + } + + setViewMode(mode: ViewMode): void { + this.viewMode.set(mode); + } + + onToolbarAction(event: { action: RtToolbarAction; value?: string }): void { + const ta = this.textareaEl()?.nativeElement; + if (!ta) return; + + const { action } = event; + const start = ta.selectionStart; + const end = ta.selectionEnd; + const text = this.markdown(); + + let result: { text: string; selectionStart?: number; selectionEnd?: number; cursorPos?: number }; + + switch (action) { + case 'bold': + result = wrapSelection(text, start, end, '**', '**'); + break; + case 'italic': + result = wrapSelection(text, start, end, '*', '*'); + break; + case 'strikethrough': + result = wrapSelection(text, start, end, '~~', '~~'); + break; + case 'code': + result = wrapSelection(text, start, end, '`', '`'); + break; + case 'heading-1': + result = { ...toggleLinePrefix(text, start, '# '), selectionStart: undefined, selectionEnd: undefined }; + break; + case 'heading-2': + result = { ...toggleLinePrefix(text, start, '## '), selectionStart: undefined, selectionEnd: undefined }; + break; + case 'heading-3': + result = { ...toggleLinePrefix(text, start, '### '), selectionStart: undefined, selectionEnd: undefined }; + break; + case 'bullet-list': + result = { ...insertLinePrefix(text, start, '- '), selectionStart: undefined, selectionEnd: undefined }; + break; + case 'ordered-list': + result = { ...insertLinePrefix(text, start, '1. '), selectionStart: undefined, selectionEnd: undefined }; + break; + case 'task-list': + result = { ...insertLinePrefix(text, start, '- [ ] '), selectionStart: undefined, selectionEnd: undefined }; + break; + case 'blockquote': + result = { ...insertLinePrefix(text, start, '> '), selectionStart: undefined, selectionEnd: undefined }; + break; + case 'code-block': + result = insertBlock(text, start, '```', '```', 'code'); + break; + case 'link': + result = wrapSelection(text, start, end, '[', '](url)'); + break; + case 'image': + result = wrapSelection(text, start, end, '![', '](url)'); + break; + case 'horizontal-rule': + result = { ...insertLinePrefix(text, start, '\n---\n'), selectionStart: undefined, selectionEnd: undefined }; + break; + default: + return; + } + + this.markdown.set(result.text); + this.contentChange.emit(result.text); + this.htmlChange.emit(this.markdownService.toHtml(result.text)); + + // Restore cursor position + requestAnimationFrame(() => { + if (ta) { + ta.focus(); + const pos = result.selectionStart ?? result.cursorPos ?? start; + const endPos = result.selectionEnd ?? pos; + ta.setSelectionRange(pos, endPos); + } + }); + } +} diff --git a/src/components/rt-mention/index.ts b/src/components/rt-mention/index.ts new file mode 100644 index 0000000..90ddaa0 --- /dev/null +++ b/src/components/rt-mention/index.ts @@ -0,0 +1 @@ +export * from './rt-mention.component'; diff --git a/src/components/rt-mention/rt-mention.component.html b/src/components/rt-mention/rt-mention.component.html new file mode 100644 index 0000000..010d4c9 --- /dev/null +++ b/src/components/rt-mention/rt-mention.component.html @@ -0,0 +1,29 @@ +
+ @for (item of filteredItems(); track item.id; let i = $index) { + + } + + @if (filteredItems().length === 0) { +
No results
+ } +
diff --git a/src/components/rt-mention/rt-mention.component.scss b/src/components/rt-mention/rt-mention.component.scss new file mode 100644 index 0000000..b5e33ba --- /dev/null +++ b/src/components/rt-mention/rt-mention.component.scss @@ -0,0 +1,82 @@ +@use '../../styles/tokens'; + +.rt-mention { + background: var(--rt-bubble-bg); + border: 1px solid var(--rt-bubble-border); + border-radius: var(--rt-editor-radius); + box-shadow: var(--rt-bubble-shadow); + padding: 0.25rem; + min-width: 200px; + max-height: 240px; + overflow-y: auto; + + &__item { + display: flex; + align-items: center; + gap: 0.5rem; + width: 100%; + padding: 0.375rem 0.5rem; + border: none; + border-radius: var(--rt-toolbar-btn-radius); + background: transparent; + cursor: pointer; + text-align: left; + transition: background var(--rt-transition); + + &:hover, + &.is-selected { + background: var(--rt-toolbar-btn-hover); + } + } + + &__avatar { + width: 1.5rem; + height: 1.5rem; + border-radius: 50%; + background: var(--color-primary-500, #3b82f6); + color: #fff; + display: flex; + align-items: center; + justify-content: center; + font-size: 0.625rem; + font-weight: 700; + flex-shrink: 0; + overflow: hidden; + } + + &__avatar-img { + width: 100%; + height: 100%; + object-fit: cover; + } + + &__info { + display: flex; + flex-direction: column; + min-width: 0; + } + + &__name { + font-size: 0.8125rem; + font-weight: 500; + color: var(--rt-editor-text); + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } + + &__email { + font-size: 0.6875rem; + color: var(--rt-status-text); + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } + + &__empty { + padding: 0.75rem; + text-align: center; + font-size: 0.75rem; + color: var(--rt-status-text); + } +} diff --git a/src/components/rt-mention/rt-mention.component.ts b/src/components/rt-mention/rt-mention.component.ts new file mode 100644 index 0000000..74f2417 --- /dev/null +++ b/src/components/rt-mention/rt-mention.component.ts @@ -0,0 +1,60 @@ +import { + Component, + ChangeDetectionStrategy, + input, + output, + signal, + computed, +} from '@angular/core'; +import { CommonModule } from '@angular/common'; +import type { RtMentionItem } from '../../types/editor.types'; + +@Component({ + selector: 'rt-mention', + standalone: true, + imports: [CommonModule], + templateUrl: './rt-mention.component.html', + styleUrl: './rt-mention.component.scss', + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class RtMentionComponent { + readonly items = input([]); + readonly query = input(''); + + readonly mentionSelect = output(); + + readonly selectedIndex = signal(0); + + readonly filteredItems = computed(() => { + const q = this.query().toLowerCase(); + if (!q) return this.items(); + return this.items().filter(item => + item.label.toLowerCase().includes(q) || + (item.email && item.email.toLowerCase().includes(q)), + ); + }); + + onSelect(item: RtMentionItem): void { + this.mentionSelect.emit(item); + } + + onKeyDown(event: KeyboardEvent): boolean { + const items = this.filteredItems(); + if (!items.length) return false; + + if (event.key === 'ArrowUp') { + this.selectedIndex.update(i => (i + items.length - 1) % items.length); + return true; + } + if (event.key === 'ArrowDown') { + this.selectedIndex.update(i => (i + 1) % items.length); + return true; + } + if (event.key === 'Enter') { + const item = items[this.selectedIndex()]; + if (item) this.onSelect(item); + return true; + } + return false; + } +} diff --git a/src/components/rt-table-inserter/index.ts b/src/components/rt-table-inserter/index.ts new file mode 100644 index 0000000..8b5c081 --- /dev/null +++ b/src/components/rt-table-inserter/index.ts @@ -0,0 +1 @@ +export * from './rt-table-inserter.component'; diff --git a/src/components/rt-table-inserter/rt-table-inserter.component.html b/src/components/rt-table-inserter/rt-table-inserter.component.html new file mode 100644 index 0000000..e713fc9 --- /dev/null +++ b/src/components/rt-table-inserter/rt-table-inserter.component.html @@ -0,0 +1,26 @@ +@if (open()) { +
+
+ @for (row of rows; track row) { +
+ @for (col of cols; track col) { + + } +
+ } +
+
+ @if (hoverRow() > 0 && hoverCol() > 0) { + {{ hoverRow() }} x {{ hoverCol() }} + } @else { + Select size + } +
+
+} diff --git a/src/components/rt-table-inserter/rt-table-inserter.component.scss b/src/components/rt-table-inserter/rt-table-inserter.component.scss new file mode 100644 index 0000000..51b69d2 --- /dev/null +++ b/src/components/rt-table-inserter/rt-table-inserter.component.scss @@ -0,0 +1,47 @@ +@use '../../styles/tokens'; + +.rt-table-inserter { + background: var(--rt-editor-bg); + border: 1px solid var(--rt-editor-border); + border-radius: var(--rt-editor-radius); + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); + padding: 0.5rem; + + &__grid { + display: flex; + flex-direction: column; + gap: 2px; + } + + &__row { + display: flex; + gap: 2px; + } + + &__cell { + width: 1.25rem; + height: 1.25rem; + border: 1px solid var(--rt-editor-border); + border-radius: 2px; + background: transparent; + cursor: pointer; + padding: 0; + transition: all var(--rt-transition); + + &:hover { + border-color: var(--rt-editor-border-focus); + } + + &.is-highlighted { + background: var(--rt-toolbar-btn-active); + border-color: var(--rt-toolbar-btn-active-text); + } + } + + &__label { + text-align: center; + font-size: 0.6875rem; + color: var(--rt-status-text); + margin-top: 0.375rem; + } +} diff --git a/src/components/rt-table-inserter/rt-table-inserter.component.ts b/src/components/rt-table-inserter/rt-table-inserter.component.ts new file mode 100644 index 0000000..e164852 --- /dev/null +++ b/src/components/rt-table-inserter/rt-table-inserter.component.ts @@ -0,0 +1,49 @@ +import { + Component, + ChangeDetectionStrategy, + input, + output, + signal, +} from '@angular/core'; +import { CommonModule } from '@angular/common'; + +@Component({ + selector: 'rt-table-inserter', + standalone: true, + imports: [CommonModule], + templateUrl: './rt-table-inserter.component.html', + styleUrl: './rt-table-inserter.component.scss', + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class RtTableInserterComponent { + readonly open = input(false); + + readonly confirm = output<{ rows: number; cols: number }>(); + readonly cancel = output(); + + readonly maxRows = 8; + readonly maxCols = 6; + readonly hoverRow = signal(0); + readonly hoverCol = signal(0); + + readonly rows = Array.from({ length: 8 }, (_, i) => i + 1); + readonly cols = Array.from({ length: 6 }, (_, i) => i + 1); + + onCellHover(row: number, col: number): void { + this.hoverRow.set(row); + this.hoverCol.set(col); + } + + onCellClick(row: number, col: number): void { + this.confirm.emit({ rows: row, cols: col }); + } + + onMouseLeave(): void { + this.hoverRow.set(0); + this.hoverCol.set(0); + } + + isHighlighted(row: number, col: number): boolean { + return row <= this.hoverRow() && col <= this.hoverCol(); + } +} diff --git a/src/components/rt-template-editor/index.ts b/src/components/rt-template-editor/index.ts new file mode 100644 index 0000000..a03b3b3 --- /dev/null +++ b/src/components/rt-template-editor/index.ts @@ -0,0 +1 @@ +export * from './rt-template-editor.component'; diff --git a/src/components/rt-template-editor/rt-template-editor.component.html b/src/components/rt-template-editor/rt-template-editor.component.html new file mode 100644 index 0000000..2f7d0ff --- /dev/null +++ b/src/components/rt-template-editor/rt-template-editor.component.html @@ -0,0 +1,32 @@ +
+
+ + {{ '{' }}{{ '{' }} {{ '}' }}{{ '}' }} Variables + + + @if (variablePickerOpen()) { +
+ +
+ } +
+ + +
diff --git a/src/components/rt-template-editor/rt-template-editor.component.scss b/src/components/rt-template-editor/rt-template-editor.component.scss new file mode 100644 index 0000000..d9a3a46 --- /dev/null +++ b/src/components/rt-template-editor/rt-template-editor.component.scss @@ -0,0 +1,54 @@ +@use '../../styles/tokens'; + +.rt-template-editor { + position: relative; + + &__toolbar-extra { + position: relative; + display: flex; + align-items: center; + padding: 0.375rem 0.5rem; + background: var(--rt-toolbar-bg); + border: 1px solid var(--rt-editor-border); + border-bottom: none; + border-radius: var(--rt-editor-radius) var(--rt-editor-radius) 0 0; + } + + &__var-btn { + padding: 0.375rem 0.75rem; + border: 1px solid var(--rt-var-border); + border-radius: var(--rt-var-radius); + background: var(--rt-var-bg); + color: var(--rt-var-text); + font-size: 0.75rem; + font-weight: 500; + cursor: pointer; + transition: all var(--rt-transition); + + &:hover { + opacity: 0.85; + } + + &.is-active { + background: var(--rt-toolbar-btn-active); + } + } + + &__picker-dropdown { + position: absolute; + top: 100%; + left: 0.5rem; + z-index: 100; + margin-top: 0.25rem; + } + + rt-editor { + display: block; + + // Remove top border-radius since we have the toolbar-extra above + ::ng-deep .rt-editor { + border-top-left-radius: 0; + border-top-right-radius: 0; + } + } +} diff --git a/src/components/rt-template-editor/rt-template-editor.component.ts b/src/components/rt-template-editor/rt-template-editor.component.ts new file mode 100644 index 0000000..769a3af --- /dev/null +++ b/src/components/rt-template-editor/rt-template-editor.component.ts @@ -0,0 +1,80 @@ +import { + Component, + ChangeDetectionStrategy, + input, + output, + signal, + inject, + effect, +} from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { ButtonComponent } from '@sda/base-ui'; +import { RtEditorComponent } from '../rt-editor/rt-editor.component'; +import { RtVariablePickerComponent } from '../rt-variable-picker/rt-variable-picker.component'; +import { TemplateService } from '../../services/template.service'; +import type { RtTemplateVariable, RtTemplateContext } from '../../types/template.types'; +import type { RtContentChangeEvent } from '../../types/event.types'; +import type { RtToolbarPreset } from '../../types/toolbar.types'; +import { buildVariablePlaceholder } from '../../utils/template.utils'; + +@Component({ + selector: 'rt-template-editor', + standalone: true, + imports: [CommonModule, ButtonComponent, RtEditorComponent, RtVariablePickerComponent], + providers: [TemplateService], + templateUrl: './rt-template-editor.component.html', + styleUrl: './rt-template-editor.component.scss', + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class RtTemplateEditorComponent { + private templateService = inject(TemplateService); + + readonly content = input(''); + readonly variables = input([]); + readonly toolbarPreset = input('standard'); + readonly placeholder = input('Write your template...'); + + readonly contentChange = output(); + readonly variableInsert = output(); + + readonly variablePickerOpen = signal(false); + + private editorRef: RtEditorComponent | null = null; + + constructor() { + effect(() => { + this.templateService.setVariables(this.variables()); + }); + } + + onEditorRef(editor: RtEditorComponent): void { + this.editorRef = editor; + } + + onContentChange(event: RtContentChangeEvent): void { + this.contentChange.emit(event); + } + + toggleVariablePicker(): void { + this.variablePickerOpen.update(v => !v); + } + + onVariableSelect(variable: RtTemplateVariable): void { + const placeholder = buildVariablePlaceholder(variable.key); + const editor = this.editorRef?.editor(); + if (editor) { + editor.chain().focus().insertContent( + `${placeholder} ` + ).run(); + } + this.variableInsert.emit(variable); + this.variablePickerOpen.set(false); + } + + /** Resolve all variables and return the final content */ + resolveTemplate(context: RtTemplateContext): string { + const editor = this.editorRef?.editor(); + if (!editor) return ''; + return this.templateService.resolve(editor.getHTML(), context); + } +} diff --git a/src/components/rt-toolbar/index.ts b/src/components/rt-toolbar/index.ts new file mode 100644 index 0000000..2a9d9e2 --- /dev/null +++ b/src/components/rt-toolbar/index.ts @@ -0,0 +1 @@ +export * from './rt-toolbar.component'; diff --git a/src/components/rt-toolbar/rt-toolbar.component.html b/src/components/rt-toolbar/rt-toolbar.component.html new file mode 100644 index 0000000..6bd75a8 --- /dev/null +++ b/src/components/rt-toolbar/rt-toolbar.component.html @@ -0,0 +1,34 @@ + diff --git a/src/components/rt-toolbar/rt-toolbar.component.scss b/src/components/rt-toolbar/rt-toolbar.component.scss new file mode 100644 index 0000000..2718e57 --- /dev/null +++ b/src/components/rt-toolbar/rt-toolbar.component.scss @@ -0,0 +1,34 @@ +@use '../../styles/tokens'; + +.rt-toolbar { + display: flex; + align-items: center; + flex-wrap: wrap; + gap: var(--rt-toolbar-gap); + padding: var(--rt-toolbar-padding); + background: var(--rt-toolbar-bg); + border-bottom: 1px solid var(--rt-toolbar-border); + + &__group { + display: flex; + align-items: center; + gap: var(--rt-toolbar-gap); + } + + &__separator { + width: 1px; + height: 1.25rem; + background: var(--rt-toolbar-separator); + margin: 0 0.125rem; + } + + &__select { + min-width: 120px; + } + + // Active state override for base-ui ghost buttons + :host ::ng-deep ui-button.is-active .btn { + background: var(--rt-toolbar-btn-active); + color: var(--rt-toolbar-btn-active-text); + } +} diff --git a/src/components/rt-toolbar/rt-toolbar.component.ts b/src/components/rt-toolbar/rt-toolbar.component.ts new file mode 100644 index 0000000..671adca --- /dev/null +++ b/src/components/rt-toolbar/rt-toolbar.component.ts @@ -0,0 +1,96 @@ +import { + Component, + ChangeDetectionStrategy, + input, + output, + inject, +} from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { FormsModule } from '@angular/forms'; +import { ButtonComponent, IconComponent, IconRegistry, SelectComponent, type SelectOption, TooltipDirective } from '@sda/base-ui'; +import type { RtToolbarGroup, RtToolbarAction } from '../../types/toolbar.types'; +import type { RtActiveMarks, RtActiveNodes } from '../../types/editor.types'; +import { registerRtIcons } from '../../utils/icons.utils'; + +@Component({ + selector: 'rt-toolbar', + standalone: true, + imports: [CommonModule, FormsModule, ButtonComponent, IconComponent, SelectComponent, TooltipDirective], + templateUrl: './rt-toolbar.component.html', + styleUrl: './rt-toolbar.component.scss', + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class RtToolbarComponent { + readonly groups = input.required(); + readonly activeMarks = input.required(); + readonly activeNodes = input.required(); + readonly canUndo = input(false); + readonly canRedo = input(false); + + readonly action = output<{ action: RtToolbarAction; value?: string }>(); + + readonly headingOptions: SelectOption[] = [ + { value: '0', label: 'Normal text' }, + { value: '1', label: 'Heading 1' }, + { value: '2', label: 'Heading 2' }, + { value: '3', label: 'Heading 3' }, + { value: '4', label: 'Heading 4' }, + ]; + + constructor() { + registerRtIcons(inject(IconRegistry)); + } + + /** Check if a toolbar action is currently active */ + isActive(actionName: RtToolbarAction): boolean { + const marks = this.activeMarks(); + const nodes = this.activeNodes(); + + switch (actionName) { + case 'bold': return marks.bold; + case 'italic': return marks.italic; + case 'underline': return marks.underline; + case 'strikethrough': return marks.strike; + case 'code': return marks.code; + case 'subscript': return marks.subscript; + case 'superscript': return marks.superscript; + case 'link': return marks.link; + case 'highlight-color': return marks.highlight; + case 'bullet-list': return nodes.bulletList; + case 'ordered-list': return nodes.orderedList; + case 'task-list': return nodes.taskList; + case 'blockquote': return nodes.blockquote; + case 'code-block': return nodes.codeBlock; + case 'align-left': return nodes.textAlign === 'left'; + case 'align-center': return nodes.textAlign === 'center'; + case 'align-right': return nodes.textAlign === 'right'; + case 'align-justify': return nodes.textAlign === 'justify'; + default: return false; + } + } + + /** Check if a toolbar action is disabled */ + isDisabled(actionName: RtToolbarAction): boolean { + switch (actionName) { + case 'undo': return !this.canUndo(); + case 'redo': return !this.canRedo(); + default: return false; + } + } + + /** Get the current heading level as a string value */ + getCurrentHeadingValue(): string { + const heading = this.activeNodes().heading; + return heading ? String(heading) : '0'; + } + + /** Emit action event */ + onAction(actionName: RtToolbarAction, value?: string): void { + this.action.emit({ action: actionName, value }); + } + + /** Handle heading select change */ + onHeadingChange(value: string | number): void { + this.action.emit({ action: 'heading', value: String(value) }); + } +} diff --git a/src/components/rt-track-changes/index.ts b/src/components/rt-track-changes/index.ts new file mode 100644 index 0000000..bd57aa9 --- /dev/null +++ b/src/components/rt-track-changes/index.ts @@ -0,0 +1 @@ +export * from './rt-track-changes.component'; diff --git a/src/components/rt-track-changes/rt-track-changes.component.html b/src/components/rt-track-changes/rt-track-changes.component.html new file mode 100644 index 0000000..866db95 --- /dev/null +++ b/src/components/rt-track-changes/rt-track-changes.component.html @@ -0,0 +1,52 @@ +
+ @if (enabled() && changes().length > 0) { +
+ {{ changes().length }} changes +
+ + Accept All + + + Reject All + +
+
+ +
+ @for (change of changes(); track change.id) { +
+
+ {{ change.author.name[0] }} + {{ change.author.name }} + {{ change.type }} +
+
+ + {{ change.content }} + +
+
+ + Accept + + + Reject + +
+
+ } +
+ } + + @if (enabled() && changes().length === 0) { +
+ No tracked changes +
+ } +
diff --git a/src/components/rt-track-changes/rt-track-changes.component.scss b/src/components/rt-track-changes/rt-track-changes.component.scss new file mode 100644 index 0000000..0eef403 --- /dev/null +++ b/src/components/rt-track-changes/rt-track-changes.component.scss @@ -0,0 +1,142 @@ +@use '../../styles/tokens'; + +.rt-track-changes { + &__header { + display: flex; + align-items: center; + justify-content: space-between; + padding: 0.5rem 0.75rem; + background: var(--rt-toolbar-bg); + border: 1px solid var(--rt-editor-border); + border-radius: var(--rt-editor-radius) var(--rt-editor-radius) 0 0; + } + + &__count { + font-size: 0.75rem; + font-weight: 600; + color: var(--rt-editor-text); + } + + &__bulk-actions { + display: flex; + gap: 0.375rem; + } + + &__list { + border: 1px solid var(--rt-editor-border); + border-top: none; + border-radius: 0 0 var(--rt-editor-radius) var(--rt-editor-radius); + overflow: hidden; + } + + &__change { + padding: 0.5rem 0.75rem; + border-bottom: 1px solid var(--rt-editor-border); + + &:last-child { border-bottom: none; } + + &--insertion { + background: var(--rt-insertion-bg); + } + + &--deletion { + background: var(--rt-deletion-bg); + } + } + + &__change-header { + display: flex; + align-items: center; + gap: 0.375rem; + margin-bottom: 0.25rem; + } + + &__avatar { + width: 1.125rem; + height: 1.125rem; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + font-size: 0.5rem; + font-weight: 700; + color: #fff; + } + + &__author { + font-size: 0.6875rem; + font-weight: 600; + color: var(--rt-editor-text); + } + + &__type-badge { + font-size: 0.5625rem; + font-weight: 600; + text-transform: uppercase; + padding: 0.0625rem 0.25rem; + border-radius: 2px; + letter-spacing: 0.025em; + } + + &__change--insertion &__type-badge { + background: var(--rt-insertion-border); + color: var(--rt-insertion-text); + } + + &__change--deletion &__type-badge { + background: var(--rt-deletion-border); + color: var(--rt-deletion-text); + } + + &__change-content { + font-size: 0.8125rem; + line-height: 1.4; + margin-bottom: 0.25rem; + } + + &__inserted { + color: var(--rt-insertion-text); + text-decoration: underline; + } + + &__deleted { + color: var(--rt-deletion-text); + text-decoration: line-through; + } + + &__change-actions { + display: flex; + gap: 0.25rem; + } + + &__btn { + padding: 0.1875rem 0.5rem; + border: none; + border-radius: var(--rt-toolbar-btn-radius); + font-size: 0.625rem; + font-weight: 500; + cursor: pointer; + transition: all var(--rt-transition); + + &--accept, &--accept-sm { + background: rgba(34, 197, 94, 0.2); + color: #166534; + &:hover { background: rgba(34, 197, 94, 0.3); } + } + + &--reject, &--reject-sm { + background: rgba(239, 68, 68, 0.2); + color: #991b1b; + &:hover { background: rgba(239, 68, 68, 0.3); } + } + } + + &__empty { + padding: 1.5rem; + text-align: center; + font-size: 0.8125rem; + color: var(--rt-status-text); + border: 1px solid var(--rt-editor-border); + border-radius: var(--rt-editor-radius); + } +} diff --git a/src/components/rt-track-changes/rt-track-changes.component.ts b/src/components/rt-track-changes/rt-track-changes.component.ts new file mode 100644 index 0000000..1f350ba --- /dev/null +++ b/src/components/rt-track-changes/rt-track-changes.component.ts @@ -0,0 +1,55 @@ +import { + Component, + ChangeDetectionStrategy, + input, + output, + signal, +} from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { ButtonComponent } from '@sda/base-ui'; +import { BadgeComponent } from '@sda/base-ui'; +import { IconComponent } from '@sda/base-ui'; +import type { RtTrackChange } from '../../types/collaboration.types'; +import type { RtTrackChangeEvent } from '../../types/event.types'; + +@Component({ + selector: 'rt-track-changes', + standalone: true, + imports: [CommonModule, ButtonComponent, BadgeComponent, IconComponent], + templateUrl: './rt-track-changes.component.html', + styleUrl: './rt-track-changes.component.scss', + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class RtTrackChangesComponent { + readonly changes = input([]); + readonly enabled = input(false); + + readonly trackChangeEvent = output(); + + readonly showPanel = signal(false); + + togglePanel(): void { + this.showPanel.update(v => !v); + } + + onAccept(change: RtTrackChange): void { + this.trackChangeEvent.emit({ type: 'accept', change }); + } + + onReject(change: RtTrackChange): void { + this.trackChangeEvent.emit({ type: 'reject', change }); + } + + onAcceptAll(): void { + this.trackChangeEvent.emit({ type: 'accept-all' }); + } + + onRejectAll(): void { + this.trackChangeEvent.emit({ type: 'reject-all' }); + } + + formatDate(isoString: string): string { + const date = new Date(isoString); + return date.toLocaleString(); + } +} diff --git a/src/components/rt-variable-picker/index.ts b/src/components/rt-variable-picker/index.ts new file mode 100644 index 0000000..344a98c --- /dev/null +++ b/src/components/rt-variable-picker/index.ts @@ -0,0 +1 @@ +export * from './rt-variable-picker.component'; diff --git a/src/components/rt-variable-picker/rt-variable-picker.component.html b/src/components/rt-variable-picker/rt-variable-picker.component.html new file mode 100644 index 0000000..b985b85 --- /dev/null +++ b/src/components/rt-variable-picker/rt-variable-picker.component.html @@ -0,0 +1,43 @@ +
+
+ + + + +
+ +
+ @for (group of groupedVariables(); track group[0]) { +
+
{{ group[0] }}
+ @for (variable of group[1]; track variable.key) { + + } +
+ } + + @if (filteredVariables().length === 0) { +
+ No variables found +
+ } +
+
diff --git a/src/components/rt-variable-picker/rt-variable-picker.component.scss b/src/components/rt-variable-picker/rt-variable-picker.component.scss new file mode 100644 index 0000000..ee625c3 --- /dev/null +++ b/src/components/rt-variable-picker/rt-variable-picker.component.scss @@ -0,0 +1,121 @@ +@use '../../styles/tokens'; + +.rt-variable-picker { + background: var(--rt-editor-bg); + border: 1px solid var(--rt-editor-border); + border-radius: var(--rt-editor-radius); + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); + width: 280px; + max-height: 360px; + display: flex; + flex-direction: column; + + &__header { + display: flex; + align-items: center; + gap: 0.5rem; + padding: 0.5rem; + border-bottom: 1px solid var(--rt-editor-border); + } + + &__search { + flex: 1; + padding: 0.375rem 0.625rem; + border: 1px solid var(--rt-editor-border); + border-radius: var(--rt-toolbar-btn-radius); + background: transparent; + color: var(--rt-editor-text); + font-size: 0.8125rem; + outline: none; + + &:focus { border-color: var(--rt-editor-border-focus); } + &::placeholder { color: var(--rt-editor-placeholder); } + } + + &__close-btn { + background: none; + border: none; + font-size: 1.125rem; + color: var(--rt-editor-text); + cursor: pointer; + padding: 0.125rem 0.25rem; + line-height: 1; + opacity: 0.6; + &:hover { opacity: 1; } + } + + &__list { + overflow-y: auto; + flex: 1; + } + + &__category { + padding: 0.25rem 0; + } + + &__category-label { + padding: 0.375rem 0.75rem; + font-size: 0.6875rem; + font-weight: 600; + color: var(--rt-status-text); + text-transform: uppercase; + letter-spacing: 0.05em; + } + + &__item { + display: flex; + align-items: center; + justify-content: space-between; + width: 100%; + padding: 0.375rem 0.75rem; + border: none; + background: transparent; + cursor: pointer; + text-align: left; + transition: background var(--rt-transition); + + &:hover { + background: var(--rt-toolbar-btn-hover); + } + } + + &__item-content { + display: flex; + flex-direction: column; + gap: 0.0625rem; + } + + &__item-label { + font-size: 0.8125rem; + color: var(--rt-editor-text); + font-weight: 500; + } + + &__item-key { + font-size: 0.6875rem; + color: var(--rt-status-text); + font-family: var(--rt-code-font-family); + } + + &__badge { + font-size: 0.625rem; + font-weight: 600; + padding: 0.125rem 0.375rem; + border-radius: var(--rt-var-radius); + text-transform: uppercase; + letter-spacing: 0.025em; + + &--text { background: #dbeafe; color: #1d4ed8; } + &--number { background: #dcfce7; color: #166534; } + &--date { background: #fef3c7; color: #92400e; } + &--boolean { background: #f3e8ff; color: #7c3aed; } + &--list { background: #ffe4e6; color: #9f1239; } + } + + &__empty { + padding: 1.5rem; + text-align: center; + font-size: 0.8125rem; + color: var(--rt-status-text); + } +} diff --git a/src/components/rt-variable-picker/rt-variable-picker.component.ts b/src/components/rt-variable-picker/rt-variable-picker.component.ts new file mode 100644 index 0000000..9c21a78 --- /dev/null +++ b/src/components/rt-variable-picker/rt-variable-picker.component.ts @@ -0,0 +1,67 @@ +import { + Component, + ChangeDetectionStrategy, + input, + output, + signal, + computed, +} from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { FormsModule } from '@angular/forms'; +import { InputComponent } from '@sda/base-ui'; +import { BadgeComponent } from '@sda/base-ui'; +import { ButtonComponent } from '@sda/base-ui'; +import { IconComponent } from '@sda/base-ui'; +import type { RtTemplateVariable } from '../../types/template.types'; +import { filterVariables } from '../../utils/template.utils'; + +@Component({ + selector: 'rt-variable-picker', + standalone: true, + imports: [CommonModule, FormsModule, InputComponent, BadgeComponent, ButtonComponent, IconComponent], + templateUrl: './rt-variable-picker.component.html', + styleUrl: './rt-variable-picker.component.scss', + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class RtVariablePickerComponent { + readonly variables = input([]); + + readonly variableSelect = output(); + readonly close = output(); + + readonly searchQuery = signal(''); + + readonly filteredVariables = computed(() => + filterVariables(this.variables(), this.searchQuery()), + ); + + readonly groupedVariables = computed(() => { + const filtered = this.filteredVariables(); + const groups = new Map(); + for (const v of filtered) { + const list = groups.get(v.category) ?? []; + list.push(v); + groups.set(v.category, list); + } + return Array.from(groups.entries()); + }); + + onSelect(variable: RtTemplateVariable): void { + this.variableSelect.emit(variable); + } + + onClose(): void { + this.close.emit(); + } + + getTypeBadgeVariant(type: string): 'default' | 'primary' | 'success' | 'warning' | 'error' | 'info' { + switch (type) { + case 'text': return 'primary'; + case 'number': return 'success'; + case 'date': return 'warning'; + case 'boolean': return 'info'; + case 'list': return 'error'; + default: return 'default'; + } + } +} diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..a897724 --- /dev/null +++ b/src/index.ts @@ -0,0 +1,47 @@ +/** + * Rich Text Elements UI + * Main library entry point + * + * Angular components for rich text editing with WYSIWYG, markdown, templates, + * and collaboration powered by TipTap and @sda/base-ui + * + * @example + * ```typescript + * import { Component } from '@angular/core'; + * import { RtEditorComponent, type RtContentChangeEvent } from '@sda/rich-text-elements-ui'; + * + * @Component({ + * standalone: true, + * imports: [RtEditorComponent], + * template: ` + * + * ` + * }) + * export class AppComponent { + * content = '

Hello world

'; + * onContentChange(event: RtContentChangeEvent) { + * console.log(event.html); + * } + * } + * ``` + */ + +// Types +export * from './types'; + +// Utils +export * from './utils'; + +// Providers +export * from './providers'; + +// Services +export * from './services'; + +// Components +export * from './components'; diff --git a/src/providers/index.ts b/src/providers/index.ts new file mode 100644 index 0000000..e45c835 --- /dev/null +++ b/src/providers/index.ts @@ -0,0 +1 @@ +export * from './rich-text-config.provider'; diff --git a/src/providers/rich-text-config.provider.ts b/src/providers/rich-text-config.provider.ts new file mode 100644 index 0000000..8dc538c --- /dev/null +++ b/src/providers/rich-text-config.provider.ts @@ -0,0 +1,14 @@ +import { InjectionToken, makeEnvironmentProviders, type EnvironmentProviders } from '@angular/core'; +import type { RtConfig } from '../types/config.types'; +import { DEFAULT_RICH_TEXT_CONFIG } from '../types/config.types'; + +export const RICH_TEXT_CONFIG = new InjectionToken('RICH_TEXT_CONFIG', { + providedIn: 'root', + factory: () => DEFAULT_RICH_TEXT_CONFIG, +}); + +export function provideRichTextConfig(config: Partial = {}): EnvironmentProviders { + return makeEnvironmentProviders([ + { provide: RICH_TEXT_CONFIG, useValue: { ...DEFAULT_RICH_TEXT_CONFIG, ...config } }, + ]); +} diff --git a/src/services/collaboration.service.ts b/src/services/collaboration.service.ts new file mode 100644 index 0000000..1719367 --- /dev/null +++ b/src/services/collaboration.service.ts @@ -0,0 +1,144 @@ +import { Injectable, signal, computed } from '@angular/core'; +import type { RtComment, RtCommentReply, RtSuggestion, RtTrackChange, RtCollabUser } from '../types/collaboration.types'; +import { generateId } from '../utils/editor.utils'; + +@Injectable() +export class CollaborationService { + readonly comments = signal([]); + readonly suggestions = signal([]); + readonly trackChanges = signal([]); + readonly trackChangesEnabled = signal(false); + + readonly activeComments = computed(() => this.comments().filter(c => !c.resolved)); + readonly resolvedComments = computed(() => this.comments().filter(c => c.resolved)); + readonly pendingSuggestions = computed(() => this.suggestions().filter(s => s.status === 'pending')); + + /** Add a new comment */ + addComment(author: RtCollabUser, content: string, from: number, to: number, quotedText?: string): RtComment { + const comment: RtComment = { + id: generateId('comment'), + author, + content, + quotedText, + from, + to, + resolved: false, + replies: [], + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + }; + this.comments.update(list => [...list, comment]); + return comment; + } + + /** Resolve a comment */ + resolveComment(commentId: string): void { + this.comments.update(list => + list.map(c => c.id === commentId ? { ...c, resolved: true, updatedAt: new Date().toISOString() } : c), + ); + } + + /** Reopen a resolved comment */ + reopenComment(commentId: string): void { + this.comments.update(list => + list.map(c => c.id === commentId ? { ...c, resolved: false, updatedAt: new Date().toISOString() } : c), + ); + } + + /** Delete a comment */ + deleteComment(commentId: string): void { + this.comments.update(list => list.filter(c => c.id !== commentId)); + } + + /** Add a reply to a comment */ + addReply(commentId: string, author: RtCollabUser, content: string): RtCommentReply { + const reply: RtCommentReply = { + id: generateId('reply'), + author, + content, + createdAt: new Date().toISOString(), + }; + this.comments.update(list => + list.map(c => c.id === commentId + ? { ...c, replies: [...c.replies, reply], updatedAt: new Date().toISOString() } + : c, + ), + ); + return reply; + } + + /** Set comments (e.g., loading from external source) */ + setComments(comments: RtComment[]): void { + this.comments.set(comments); + } + + /** Add a suggestion */ + addSuggestion(author: RtCollabUser, originalText: string, replacementText: string, from: number, to: number): RtSuggestion { + const suggestion: RtSuggestion = { + id: generateId('suggestion'), + author, + originalText, + replacementText, + from, + to, + status: 'pending', + createdAt: new Date().toISOString(), + }; + this.suggestions.update(list => [...list, suggestion]); + return suggestion; + } + + /** Accept a suggestion */ + acceptSuggestion(suggestionId: string): void { + this.suggestions.update(list => + list.map(s => s.id === suggestionId ? { ...s, status: 'accepted' as const } : s), + ); + } + + /** Reject a suggestion */ + rejectSuggestion(suggestionId: string): void { + this.suggestions.update(list => + list.map(s => s.id === suggestionId ? { ...s, status: 'rejected' as const } : s), + ); + } + + /** Toggle track changes mode */ + toggleTrackChanges(): void { + this.trackChangesEnabled.update(v => !v); + } + + /** Add a track change entry */ + addTrackChange(author: RtCollabUser, type: 'insertion' | 'deletion', content: string, from: number, to: number): RtTrackChange { + const change: RtTrackChange = { + id: generateId('change'), + author, + type, + content, + from, + to, + createdAt: new Date().toISOString(), + }; + this.trackChanges.update(list => [...list, change]); + return change; + } + + /** Accept a track change */ + acceptChange(changeId: string): void { + this.trackChanges.update(list => list.filter(c => c.id !== changeId)); + } + + /** Reject a track change */ + rejectChange(changeId: string): void { + this.trackChanges.update(list => list.filter(c => c.id !== changeId)); + } + + /** Accept all track changes */ + acceptAllChanges(): void { + this.trackChanges.set([]); + } + + /** Reject all track changes */ + rejectAllChanges(): void { + this.trackChanges.set([]); + } +} diff --git a/src/services/editor-state.service.ts b/src/services/editor-state.service.ts new file mode 100644 index 0000000..b47ab46 --- /dev/null +++ b/src/services/editor-state.service.ts @@ -0,0 +1,93 @@ +import { Injectable, signal, computed } from '@angular/core'; +import type { Editor } from '@tiptap/core'; +import type { RtActiveMarks, RtActiveNodes } from '../types/editor.types'; + +const EMPTY_MARKS: RtActiveMarks = { + bold: false, + italic: false, + underline: false, + strike: false, + code: false, + subscript: false, + superscript: false, + link: false, + highlight: false, + color: null, + highlightColor: null, +}; + +const EMPTY_NODES: RtActiveNodes = { + paragraph: false, + heading: null, + bulletList: false, + orderedList: false, + taskList: false, + blockquote: false, + codeBlock: false, + table: false, + image: false, + horizontalRule: false, + textAlign: null, +}; + +@Injectable() +export class EditorStateService { + readonly focused = signal(false); + readonly wordCount = signal(0); + readonly characterCount = signal(0); + readonly isEmpty = signal(true); + readonly canUndo = signal(false); + readonly canRedo = signal(false); + readonly activeMarks = signal(EMPTY_MARKS); + readonly activeNodes = signal(EMPTY_NODES); + readonly selectionFrom = signal(0); + readonly selectionTo = signal(0); + readonly hasSelection = computed(() => this.selectionFrom() !== this.selectionTo()); + + /** Sync TipTap editor state into signals */ + updateFromEditor(editor: Editor): void { + this.isEmpty.set(editor.isEmpty); + this.canUndo.set(editor.can().undo()); + this.canRedo.set(editor.can().redo()); + + const text = editor.getText(); + this.wordCount.set(text.trim() ? text.trim().split(/\s+/).length : 0); + this.characterCount.set(text.length); + + const { from, to } = editor.state.selection; + this.selectionFrom.set(from); + this.selectionTo.set(to); + + this.activeMarks.set({ + bold: editor.isActive('bold'), + italic: editor.isActive('italic'), + underline: editor.isActive('underline'), + strike: editor.isActive('strike'), + code: editor.isActive('code'), + subscript: editor.isActive('subscript'), + superscript: editor.isActive('superscript'), + link: editor.isActive('link'), + highlight: editor.isActive('highlight'), + color: editor.getAttributes('textStyle')['color'] ?? null, + highlightColor: editor.getAttributes('highlight')['color'] ?? null, + }); + + this.activeNodes.set({ + paragraph: editor.isActive('paragraph'), + heading: editor.isActive('heading') ? (editor.getAttributes('heading')['level'] as number) : null, + bulletList: editor.isActive('bulletList'), + orderedList: editor.isActive('orderedList'), + taskList: editor.isActive('taskList'), + blockquote: editor.isActive('blockquote'), + codeBlock: editor.isActive('codeBlock'), + table: editor.isActive('table'), + image: editor.isActive('image'), + horizontalRule: editor.isActive('horizontalRule'), + textAlign: (editor.getAttributes('paragraph')['textAlign'] as 'left' | 'center' | 'right' | 'justify') ?? null, + }); + } + + setFocused(focused: boolean): void { + this.focused.set(focused); + } +} diff --git a/src/services/history.service.ts b/src/services/history.service.ts new file mode 100644 index 0000000..d74e2ba --- /dev/null +++ b/src/services/history.service.ts @@ -0,0 +1,25 @@ +import { Injectable } from '@angular/core'; +import type { Editor } from '@tiptap/core'; + +@Injectable() +export class HistoryService { + /** Undo the last action */ + undo(editor: Editor): void { + editor.chain().focus().undo().run(); + } + + /** Redo the last undone action */ + redo(editor: Editor): void { + editor.chain().focus().redo().run(); + } + + /** Check if undo is available */ + canUndo(editor: Editor): boolean { + return editor.can().undo(); + } + + /** Check if redo is available */ + canRedo(editor: Editor): boolean { + return editor.can().redo(); + } +} diff --git a/src/services/index.ts b/src/services/index.ts new file mode 100644 index 0000000..465cfe9 --- /dev/null +++ b/src/services/index.ts @@ -0,0 +1,6 @@ +export * from './editor-state.service'; +export * from './toolbar.service'; +export * from './template.service'; +export * from './collaboration.service'; +export * from './markdown.service'; +export * from './history.service'; diff --git a/src/services/markdown.service.ts b/src/services/markdown.service.ts new file mode 100644 index 0000000..7406b09 --- /dev/null +++ b/src/services/markdown.service.ts @@ -0,0 +1,83 @@ +import { Injectable } from '@angular/core'; +import { marked } from 'marked'; + +@Injectable() +export class MarkdownService { + constructor() { + marked.setOptions({ + breaks: true, + gfm: true, + }); + } + + /** Convert markdown to HTML */ + toHtml(markdown: string): string { + return marked.parse(markdown, { async: false }) as string; + } + + /** Convert HTML to markdown (simplified rule-based conversion) */ + toMarkdown(html: string): string { + let md = html; + + // Headings + md = md.replace(/]*>(.*?)<\/h1>/gi, '# $1\n\n'); + md = md.replace(/]*>(.*?)<\/h2>/gi, '## $1\n\n'); + md = md.replace(/]*>(.*?)<\/h3>/gi, '### $1\n\n'); + md = md.replace(/]*>(.*?)<\/h4>/gi, '#### $1\n\n'); + md = md.replace(/]*>(.*?)<\/h5>/gi, '##### $1\n\n'); + md = md.replace(/]*>(.*?)<\/h6>/gi, '###### $1\n\n'); + + // Bold / italic / strikethrough / code + md = md.replace(/]*>(.*?)<\/strong>/gi, '**$1**'); + md = md.replace(/]*>(.*?)<\/b>/gi, '**$1**'); + md = md.replace(/]*>(.*?)<\/em>/gi, '*$1*'); + md = md.replace(/]*>(.*?)<\/i>/gi, '*$1*'); + md = md.replace(/]*>(.*?)<\/s>/gi, '~~$1~~'); + md = md.replace(/]*>(.*?)<\/del>/gi, '~~$1~~'); + md = md.replace(/]*>(.*?)<\/code>/gi, '`$1`'); + + // Links and images + md = md.replace(/]*href="([^"]*)"[^>]*>(.*?)<\/a>/gi, '[$2]($1)'); + md = md.replace(/]*src="([^"]*)"[^>]*alt="([^"]*)"[^>]*\/?>/gi, '![$2]($1)'); + md = md.replace(/]*src="([^"]*)"[^>]*\/?>/gi, '![]($1)'); + + // Lists + md = md.replace(/]*>([\s\S]*?)<\/ul>/gi, (_, content: string) => { + return content.replace(/]*>(.*?)<\/li>/gi, '- $1\n') + '\n'; + }); + md = md.replace(/]*>([\s\S]*?)<\/ol>/gi, (_, content: string) => { + let i = 1; + return content.replace(/]*>(.*?)<\/li>/gi, () => `${i++}. $1\n`) + '\n'; + }); + + // Blockquote + md = md.replace(/]*>([\s\S]*?)<\/blockquote>/gi, (_, content: string) => { + return content.trim().split('\n').map((line: string) => `> ${line}`).join('\n') + '\n\n'; + }); + + // Code blocks + md = md.replace(/]*>]*>([\s\S]*?)<\/code><\/pre>/gi, '```\n$1\n```\n\n'); + + // Horizontal rule + md = md.replace(/]*\/?>/gi, '---\n\n'); + + // Paragraphs and line breaks + md = md.replace(/]*\/?>/gi, '\n'); + md = md.replace(/]*>(.*?)<\/p>/gi, '$1\n\n'); + + // Strip remaining HTML tags + md = md.replace(/<[^>]+>/g, ''); + + // Decode common HTML entities + md = md.replace(/&/g, '&'); + md = md.replace(/</g, '<'); + md = md.replace(/>/g, '>'); + md = md.replace(/"/g, '"'); + md = md.replace(/'/g, "'"); + md = md.replace(/ /g, ' '); + + // Clean up excessive whitespace + md = md.replace(/\n{3,}/g, '\n\n'); + return md.trim(); + } +} diff --git a/src/services/template.service.ts b/src/services/template.service.ts new file mode 100644 index 0000000..d12cfe2 --- /dev/null +++ b/src/services/template.service.ts @@ -0,0 +1,47 @@ +import { Injectable, signal, computed } from '@angular/core'; +import type { RtTemplateVariable, RtVariableCategory, RtTemplateContext } from '../types/template.types'; +import { filterVariables, resolveTemplate, validateTemplateValues } from '../utils/template.utils'; + +@Injectable() +export class TemplateService { + readonly variables = signal([]); + readonly categories = computed(() => { + const vars = this.variables(); + const categoryMap = new Map(); + for (const v of vars) { + const list = categoryMap.get(v.category) ?? []; + list.push(v); + categoryMap.set(v.category, list); + } + return Array.from(categoryMap.entries()).map(([key, variables]) => ({ + key, + label: key, + variables, + })); + }); + + readonly searchQuery = signal(''); + readonly filteredVariables = computed(() => + filterVariables(this.variables(), this.searchQuery()), + ); + + /** Set the available variables */ + setVariables(variables: RtTemplateVariable[]): void { + this.variables.set(variables); + } + + /** Update search query for variable filtering */ + setSearchQuery(query: string): void { + this.searchQuery.set(query); + } + + /** Resolve all variables in a template string */ + resolve(content: string, context: RtTemplateContext): string { + return resolveTemplate(content, context); + } + + /** Validate all required variables have values */ + validate(context: RtTemplateContext): { valid: boolean; missing: string[] } { + return validateTemplateValues(this.variables(), context); + } +} diff --git a/src/services/toolbar.service.ts b/src/services/toolbar.service.ts new file mode 100644 index 0000000..3a86e12 --- /dev/null +++ b/src/services/toolbar.service.ts @@ -0,0 +1,71 @@ +import { Injectable, inject } from '@angular/core'; +import type { Editor } from '@tiptap/core'; +import type { RtToolbarAction, RtToolbarGroup, RtToolbarPreset } from '../types/toolbar.types'; +import { RICH_TEXT_CONFIG } from '../providers/rich-text-config.provider'; +import { TOOLBAR_PRESETS } from '../utils/toolbar.utils'; + +@Injectable() +export class ToolbarService { + private config = inject(RICH_TEXT_CONFIG); + + /** Get toolbar groups for a preset */ + getPresetGroups(preset?: RtToolbarPreset): RtToolbarGroup[] { + return TOOLBAR_PRESETS[preset ?? this.config.toolbarPreset] ?? TOOLBAR_PRESETS['standard']; + } + + /** Execute a toolbar action on the editor */ + executeAction(editor: Editor, action: RtToolbarAction, value?: string): void { + const chain = editor.chain().focus(); + + switch (action) { + case 'bold': chain.toggleBold().run(); break; + case 'italic': chain.toggleItalic().run(); break; + case 'underline': chain.toggleUnderline().run(); break; + case 'strikethrough': chain.toggleStrike().run(); break; + case 'code': chain.toggleCode().run(); break; + case 'subscript': chain.toggleSubscript().run(); break; + case 'superscript': chain.toggleSuperscript().run(); break; + + case 'heading': + if (value && value !== '0') { + chain.toggleHeading({ level: Number(value) as 1 | 2 | 3 | 4 }).run(); + } else { + chain.setParagraph().run(); + } + break; + case 'heading-1': chain.toggleHeading({ level: 1 }).run(); break; + case 'heading-2': chain.toggleHeading({ level: 2 }).run(); break; + case 'heading-3': chain.toggleHeading({ level: 3 }).run(); break; + case 'heading-4': chain.toggleHeading({ level: 4 }).run(); break; + + case 'bullet-list': chain.toggleBulletList().run(); break; + case 'ordered-list': chain.toggleOrderedList().run(); break; + case 'task-list': chain.toggleTaskList().run(); break; + + case 'blockquote': chain.toggleBlockquote().run(); break; + case 'code-block': chain.toggleCodeBlock().run(); break; + case 'horizontal-rule': chain.setHorizontalRule().run(); break; + + case 'align-left': chain.setTextAlign('left').run(); break; + case 'align-center': chain.setTextAlign('center').run(); break; + case 'align-right': chain.setTextAlign('right').run(); break; + case 'align-justify': chain.setTextAlign('justify').run(); break; + + case 'text-color': + if (value) { chain.setColor(value).run(); } + else { chain.unsetColor().run(); } + break; + case 'highlight-color': + if (value) { chain.toggleHighlight({ color: value }).run(); } + else { chain.unsetHighlight().run(); } + break; + + case 'undo': chain.undo().run(); break; + case 'redo': chain.redo().run(); break; + case 'clear-formatting': chain.unsetAllMarks().clearNodes().run(); break; + + // link, image, table handled by their dialog components + default: break; + } + } +} diff --git a/src/styles/_index.scss b/src/styles/_index.scss new file mode 100644 index 0000000..ec2276a --- /dev/null +++ b/src/styles/_index.scss @@ -0,0 +1,2 @@ +@forward 'tokens'; +@forward 'mixins'; diff --git a/src/styles/_mixins.scss b/src/styles/_mixins.scss new file mode 100644 index 0000000..cde7b46 --- /dev/null +++ b/src/styles/_mixins.scss @@ -0,0 +1,224 @@ +@mixin rt-container { + display: block; + position: relative; + width: 100%; + font-family: var(--rt-editor-font-family, inherit); +} + +@mixin rt-card { + background: var(--rt-editor-bg); + border: 1px solid var(--rt-editor-border); + border-radius: var(--rt-editor-radius); + overflow: hidden; +} + +@mixin rt-toolbar-button { + display: inline-flex; + align-items: center; + justify-content: center; + width: var(--rt-toolbar-btn-size); + height: var(--rt-toolbar-btn-size); + border: none; + border-radius: var(--rt-toolbar-btn-radius); + background: transparent; + color: var(--rt-editor-text); + cursor: pointer; + transition: all var(--rt-transition); + padding: 0; + + &:hover { + background: var(--rt-toolbar-btn-hover); + } + + &.is-active { + background: var(--rt-toolbar-btn-active); + color: var(--rt-toolbar-btn-active-text); + } + + &:disabled { + opacity: 0.4; + cursor: not-allowed; + } +} + +@mixin rt-prose { + color: var(--rt-editor-text); + font-size: var(--rt-editor-font-size); + line-height: var(--rt-editor-line-height); + + > * + * { + margin-top: 0.75em; + } + + h1, h2, h3, h4, h5, h6 { + line-height: 1.3; + font-weight: 600; + margin-top: 1.5em; + margin-bottom: 0.5em; + } + + h1 { font-size: 2em; } + h2 { font-size: 1.5em; } + h3 { font-size: 1.25em; } + h4 { font-size: 1.125em; } + + p { + margin: 0; + } + + a { + color: var(--color-primary-500, #3b82f6); + text-decoration: underline; + cursor: pointer; + } + + strong { font-weight: 700; } + + em { font-style: italic; } + + u { text-decoration: underline; } + + s { text-decoration: line-through; } + + sub { vertical-align: sub; font-size: 0.75em; } + + sup { vertical-align: super; font-size: 0.75em; } + + code { + background: var(--rt-inline-code-bg); + color: var(--rt-inline-code-text); + border-radius: var(--rt-inline-code-radius); + padding: 0.125em 0.25em; + font-family: var(--rt-code-font-family); + font-size: 0.9em; + } + + pre { + background: var(--rt-code-bg); + border: 1px solid var(--rt-code-border); + border-radius: var(--rt-code-radius); + padding: 1rem; + overflow-x: auto; + + code { + background: none; + color: inherit; + padding: 0; + border-radius: 0; + font-size: var(--rt-code-font-size); + } + } + + blockquote { + border-left: var(--rt-blockquote-border-width) solid var(--rt-blockquote-border); + background: var(--rt-blockquote-bg); + padding: var(--rt-blockquote-padding); + color: var(--rt-blockquote-text); + margin: 0; + } + + ul, ol { + padding-left: 1.5em; + } + + ul { list-style-type: disc; } + ol { list-style-type: decimal; } + + li { + margin: 0.25em 0; + + > p { margin: 0; } + } + + ul[data-type="taskList"] { + list-style: none; + padding-left: 0; + + li { + display: flex; + align-items: center; + gap: 0.5em; + + > label { + flex-shrink: 0; + display: flex; + align-items: center; + + input[type="checkbox"] { + cursor: pointer; + margin: 0; + } + } + + > div { + flex: 1; + } + } + } + + hr { + border: none; + border-top: 2px solid var(--rt-editor-border); + margin: 1.5em 0; + } + + table { + border-collapse: collapse; + width: 100%; + margin: 1em 0; + overflow: auto; + + th, td { + border: 1px solid var(--rt-table-border); + padding: var(--rt-table-cell-padding); + text-align: left; + vertical-align: top; + } + + th { + background: var(--rt-table-header-bg); + font-weight: 600; + } + + .selectedCell { + background: var(--rt-table-selected-bg); + } + } + + img { + max-width: 100%; + height: auto; + border-radius: var(--radius-sm, 0.25rem); + + &.ProseMirror-selectednode { + outline: 2px solid var(--rt-editor-border-focus); + } + } + + // Placeholder + p.is-editor-empty:first-child::before { + content: attr(data-placeholder); + float: left; + color: var(--rt-editor-placeholder); + pointer-events: none; + height: 0; + } + + // Template variables + span[data-type="variable"] { + background: var(--rt-var-bg); + color: var(--rt-var-text); + border: 1px solid var(--rt-var-border); + border-radius: var(--rt-var-radius); + padding: var(--rt-var-padding); + font-size: 0.875em; + font-weight: 500; + white-space: nowrap; + cursor: default; + } +} + +@mixin rt-flex-center { + display: flex; + align-items: center; +} diff --git a/src/styles/_tokens.scss b/src/styles/_tokens.scss new file mode 100644 index 0000000..41d9ed8 --- /dev/null +++ b/src/styles/_tokens.scss @@ -0,0 +1,103 @@ +:root { + // Editor + --rt-editor-bg: var(--color-bg-secondary, #ffffff); + --rt-editor-text: var(--color-text-primary, #111827); + --rt-editor-border: var(--color-border-primary, #e5e7eb); + --rt-editor-border-focus: var(--color-primary-500, #3b82f6); + --rt-editor-placeholder: var(--color-text-muted, #9ca3af); + --rt-editor-padding: 1rem; + --rt-editor-radius: var(--radius-md, 0.5rem); + --rt-editor-min-height: 200px; + --rt-editor-font-size: 1rem; + --rt-editor-line-height: 1.75; + + // Toolbar + --rt-toolbar-bg: var(--color-bg-secondary, #ffffff); + --rt-toolbar-border: var(--color-border-primary, #e5e7eb); + --rt-toolbar-btn-size: 2rem; + --rt-toolbar-btn-radius: var(--radius-sm, 0.25rem); + --rt-toolbar-btn-hover: var(--color-bg-hover, #f3f4f6); + --rt-toolbar-btn-active: var(--color-primary-50, #eff6ff); + --rt-toolbar-btn-active-text: var(--color-primary-500, #3b82f6); + --rt-toolbar-separator: var(--color-border-primary, #e5e7eb); + --rt-toolbar-gap: 0.25rem; + --rt-toolbar-padding: 0.5rem; + + // Code blocks + --rt-code-bg: var(--color-bg-primary, #f9fafb); + --rt-code-text: var(--color-text-primary, #111827); + --rt-code-border: var(--color-border-primary, #e5e7eb); + --rt-code-radius: var(--radius-md, 0.5rem); + --rt-code-font-family: 'SF Mono', 'Fira Code', 'Fira Mono', Menlo, Consolas, monospace; + --rt-code-font-size: 0.875rem; + --rt-code-line-number: var(--color-text-muted, #9ca3af); + + // Inline code + --rt-inline-code-bg: var(--color-bg-primary, #f3f4f6); + --rt-inline-code-text: var(--color-error-500, #ef4444); + --rt-inline-code-radius: var(--radius-sm, 0.25rem); + + // Blockquote + --rt-blockquote-border: var(--color-primary-500, #3b82f6); + --rt-blockquote-bg: var(--color-primary-50, #eff6ff); + --rt-blockquote-text: var(--color-text-secondary, #6b7280); + --rt-blockquote-border-width: 3px; + --rt-blockquote-padding: 0.75rem 1rem; + + // Table + --rt-table-border: var(--color-border-primary, #e5e7eb); + --rt-table-header-bg: var(--color-bg-primary, #f9fafb); + --rt-table-cell-padding: 0.5rem 0.75rem; + --rt-table-selected-bg: var(--color-primary-50, #eff6ff); + + // Template variables + --rt-var-bg: var(--color-primary-50, #eff6ff); + --rt-var-text: var(--color-primary-700, #1d4ed8); + --rt-var-border: var(--color-primary-200, #bfdbfe); + --rt-var-radius: var(--radius-sm, 0.25rem); + --rt-var-padding: 0.125rem 0.375rem; + + // Comments + --rt-comment-bg: var(--color-bg-secondary, #ffffff); + --rt-comment-border: var(--color-border-primary, #e5e7eb); + --rt-comment-highlight: rgba(255, 212, 0, 0.3); + --rt-comment-active-highlight: rgba(255, 212, 0, 0.5); + --rt-comment-resolved-bg: var(--color-bg-primary, #f9fafb); + + // Track changes + --rt-insertion-bg: rgba(34, 197, 94, 0.15); + --rt-insertion-text: #166534; + --rt-insertion-border: rgba(34, 197, 94, 0.4); + --rt-deletion-bg: rgba(239, 68, 68, 0.15); + --rt-deletion-text: #991b1b; + --rt-deletion-border: rgba(239, 68, 68, 0.4); + + // Bubble menu + --rt-bubble-bg: var(--color-bg-secondary, #ffffff); + --rt-bubble-border: var(--color-border-primary, #e5e7eb); + --rt-bubble-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); + --rt-bubble-radius: var(--radius-md, 0.5rem); + + // Status bar + --rt-status-bg: var(--color-bg-primary, #f9fafb); + --rt-status-text: var(--color-text-muted, #9ca3af); + --rt-status-font-size: 0.75rem; + + // Transition + --rt-transition: 150ms ease-in-out; +} + +@media (prefers-color-scheme: dark) { + :root { + --rt-editor-bg: var(--color-bg-secondary, #1f2937); + --rt-editor-text: var(--color-text-primary, #f9fafb); + --rt-code-bg: var(--color-bg-primary, #111827); + --rt-inline-code-bg: var(--color-bg-primary, #374151); + --rt-inline-code-text: #fca5a5; + --rt-comment-highlight: rgba(255, 212, 0, 0.2); + --rt-insertion-bg: rgba(34, 197, 94, 0.1); + --rt-insertion-text: #86efac; + --rt-deletion-bg: rgba(239, 68, 68, 0.1); + --rt-deletion-text: #fca5a5; + } +} diff --git a/src/types/collaboration.types.ts b/src/types/collaboration.types.ts new file mode 100644 index 0000000..19d6f75 --- /dev/null +++ b/src/types/collaboration.types.ts @@ -0,0 +1,85 @@ +/** A comment on the document */ +export interface RtComment { + /** Unique comment ID */ + id: string; + /** Author of the comment */ + author: RtCollabUser; + /** Comment text content */ + content: string; + /** Quoted text from the document */ + quotedText?: string; + /** Position range in the document */ + from: number; + /** End position range in the document */ + to: number; + /** Whether the comment is resolved */ + resolved: boolean; + /** Replies to this comment */ + replies: RtCommentReply[]; + /** Creation timestamp */ + createdAt: string; + /** Last update timestamp */ + updatedAt: string; +} + +/** A reply to a comment */ +export interface RtCommentReply { + /** Unique reply ID */ + id: string; + /** Author of the reply */ + author: RtCollabUser; + /** Reply text content */ + content: string; + /** Creation timestamp */ + createdAt: string; +} + +/** A suggestion (inline edit proposal) */ +export interface RtSuggestion { + /** Unique suggestion ID */ + id: string; + /** Author of the suggestion */ + author: RtCollabUser; + /** Original text */ + originalText: string; + /** Suggested replacement */ + replacementText: string; + /** Position range in the document */ + from: number; + /** End position range in the document */ + to: number; + /** Status */ + status: 'pending' | 'accepted' | 'rejected'; + /** Creation timestamp */ + createdAt: string; +} + +/** Track change entry */ +export interface RtTrackChange { + /** Unique change ID */ + id: string; + /** Author of the change */ + author: RtCollabUser; + /** Type of change */ + type: 'insertion' | 'deletion'; + /** Changed text content */ + content: string; + /** Position in the document */ + from: number; + /** End position in the document */ + to: number; + /** Creation timestamp */ + createdAt: string; +} + +/** A collaborative user */ +export interface RtCollabUser { + /** Unique user ID */ + id: string; + /** Display name */ + name: string; + /** User color (for cursors, highlights) */ + color: string; + /** Avatar URL */ + avatar?: string; +} diff --git a/src/types/config.types.ts b/src/types/config.types.ts new file mode 100644 index 0000000..3c4f3b9 --- /dev/null +++ b/src/types/config.types.ts @@ -0,0 +1,48 @@ +import type { RtToolbarPreset } from './toolbar.types'; + +/** Rich text editor configuration */ +export interface RtConfig { + /** Default toolbar preset */ + toolbarPreset: RtToolbarPreset; + /** Whether to show the toolbar by default */ + showToolbar: boolean; + /** Whether to show the bubble menu on selection */ + showBubbleMenu: boolean; + /** Whether to show word count */ + showWordCount: boolean; + /** Whether to show character count */ + showCharacterCount: boolean; + /** Default placeholder text */ + placeholder: string; + /** Maximum content length (0 = unlimited) */ + maxLength: number; + /** Whether the editor is editable by default */ + editable: boolean; + /** Whether to enable spell check */ + spellcheck: boolean; + /** Code block languages available */ + codeLanguages: string[]; + /** Whether to auto-link URLs */ + autoLink: boolean; + /** Image upload handler (if provided, enables image upload) */ + imageUploadHandler?: (file: File) => Promise; +} + +/** Default configuration values */ +export const DEFAULT_RICH_TEXT_CONFIG: RtConfig = { + toolbarPreset: 'standard', + showToolbar: true, + showBubbleMenu: true, + showWordCount: false, + showCharacterCount: false, + placeholder: 'Start writing...', + maxLength: 0, + editable: true, + spellcheck: true, + codeLanguages: [ + 'javascript', 'typescript', 'html', 'css', 'scss', + 'json', 'python', 'java', 'go', 'rust', + 'sql', 'bash', 'yaml', 'xml', 'markdown', + ], + autoLink: true, +}; diff --git a/src/types/editor.types.ts b/src/types/editor.types.ts new file mode 100644 index 0000000..538ea3a --- /dev/null +++ b/src/types/editor.types.ts @@ -0,0 +1,61 @@ +import type { JSONContent } from '@tiptap/core'; + +/** Content format for the editor */ +export type RtContent = string | JSONContent; + +/** Editor mode */ +export type RtEditorMode = 'wysiwyg' | 'markdown' | 'template'; + +/** Currently active marks on the selection */ +export interface RtActiveMarks { + bold: boolean; + italic: boolean; + underline: boolean; + strike: boolean; + code: boolean; + subscript: boolean; + superscript: boolean; + link: boolean; + highlight: boolean; + color: string | null; + highlightColor: string | null; +} + +/** Currently active nodes at the cursor position */ +export interface RtActiveNodes { + paragraph: boolean; + heading: number | null; + bulletList: boolean; + orderedList: boolean; + taskList: boolean; + blockquote: boolean; + codeBlock: boolean; + table: boolean; + image: boolean; + horizontalRule: boolean; + textAlign: 'left' | 'center' | 'right' | 'justify' | null; +} + +/** Image data for insertion */ +export interface RtImageData { + src: string; + alt?: string; + title?: string; + width?: number; + height?: number; +} + +/** Link data for insertion */ +export interface RtLinkData { + href: string; + target?: '_blank' | '_self'; + text?: string; +} + +/** Mention item */ +export interface RtMentionItem { + id: string; + label: string; + avatar?: string; + email?: string; +} diff --git a/src/types/event.types.ts b/src/types/event.types.ts new file mode 100644 index 0000000..8d7e5b4 --- /dev/null +++ b/src/types/event.types.ts @@ -0,0 +1,75 @@ +import type { RtActiveMarks, RtActiveNodes, RtContent } from './editor.types'; +import type { RtComment, RtCommentReply, RtSuggestion, RtTrackChange } from './collaboration.types'; + +/** Emitted when editor content changes */ +export interface RtContentChangeEvent { + /** HTML content */ + html: string; + /** JSON content */ + json: RtContent; + /** Plain text content */ + text: string; + /** Whether the content is empty */ + isEmpty: boolean; +} + +/** Emitted when selection changes */ +export interface RtSelectionChangeEvent { + /** Start position */ + from: number; + /** End position */ + to: number; + /** Whether there is an active selection (not just cursor) */ + hasSelection: boolean; + /** Selected text */ + selectedText: string; + /** Active marks at selection */ + activeMarks: RtActiveMarks; + /** Active nodes at selection */ + activeNodes: RtActiveNodes; +} + +/** Emitted when focus state changes */ +export interface RtFocusChangeEvent { + /** Whether the editor is focused */ + focused: boolean; +} + +/** Emitted when word/character count changes */ +export interface RtCountChangeEvent { + /** Word count */ + words: number; + /** Character count */ + characters: number; +} + +/** Emitted for comment events */ +export interface RtCommentEvent { + type: 'add' | 'resolve' | 'reopen' | 'delete' | 'reply'; + comment: RtComment; + reply?: RtCommentReply; +} + +/** Emitted for suggestion events */ +export interface RtSuggestionEvent { + type: 'add' | 'accept' | 'reject'; + suggestion: RtSuggestion; +} + +/** Emitted for track change events */ +export interface RtTrackChangeEvent { + type: 'accept' | 'reject' | 'accept-all' | 'reject-all'; + change?: RtTrackChange; +} + +/** Emitted when editor state changes */ +export interface RtStateChangeEvent { + /** Whether undo is available */ + canUndo: boolean; + /** Whether redo is available */ + canRedo: boolean; + /** Active marks */ + activeMarks: RtActiveMarks; + /** Active nodes */ + activeNodes: RtActiveNodes; +} diff --git a/src/types/index.ts b/src/types/index.ts new file mode 100644 index 0000000..f5df9bb --- /dev/null +++ b/src/types/index.ts @@ -0,0 +1,6 @@ +export * from './editor.types'; +export * from './toolbar.types'; +export * from './template.types'; +export * from './collaboration.types'; +export * from './config.types'; +export * from './event.types'; diff --git a/src/types/template.types.ts b/src/types/template.types.ts new file mode 100644 index 0000000..5a01c9e --- /dev/null +++ b/src/types/template.types.ts @@ -0,0 +1,55 @@ +/** Variable type for template insertion */ +export type RtVariableType = 'text' | 'number' | 'date' | 'boolean' | 'list' | 'rich-text'; + +/** A template variable definition */ +export interface RtTemplateVariable { + /** Unique variable key (e.g., 'user.name') */ + key: string; + /** Display label */ + label: string; + /** Variable type */ + type: RtVariableType; + /** Category for grouping */ + category: string; + /** Default value */ + defaultValue?: string; + /** Description shown in picker */ + description?: string; + /** Whether this variable is required */ + required?: boolean; +} + +/** Category grouping for variables */ +export interface RtVariableCategory { + /** Category key */ + key: string; + /** Display label */ + label: string; + /** Icon name */ + icon?: string; + /** Variables in this category */ + variables: RtTemplateVariable[]; +} + +/** A saved template */ +export interface RtTemplate { + /** Unique template ID */ + id: string; + /** Template name */ + name: string; + /** Template description */ + description?: string; + /** HTML content with variable placeholders */ + content: string; + /** Variables used in this template */ + variables: RtTemplateVariable[]; + /** Creation timestamp */ + createdAt: string; + /** Last update timestamp */ + updatedAt: string; +} + +/** Context for resolving template variables */ +export interface RtTemplateContext { + [key: string]: string | number | boolean | string[] | undefined; +} diff --git a/src/types/toolbar.types.ts b/src/types/toolbar.types.ts new file mode 100644 index 0000000..8797d19 --- /dev/null +++ b/src/types/toolbar.types.ts @@ -0,0 +1,44 @@ +/** Individual toolbar item */ +export interface RtToolbarItem { + /** Unique action key */ + action: RtToolbarAction; + /** Icon name (for base-ui IconComponent) */ + icon?: string; + /** Tooltip label */ + label: string; + /** Whether this is a dropdown (e.g., heading level, color) */ + type?: 'button' | 'dropdown' | 'select' | 'separator'; + /** Dropdown options for select type */ + options?: RtToolbarSelectOption[]; +} + +/** Option for select-type toolbar items */ +export interface RtToolbarSelectOption { + value: string; + label: string; +} + +/** A group of toolbar items separated visually */ +export interface RtToolbarGroup { + /** Group label (for accessibility) */ + label: string; + /** Items in this group */ + items: RtToolbarItem[]; +} + +/** Toolbar action identifiers */ +export type RtToolbarAction = + | 'bold' | 'italic' | 'underline' | 'strikethrough' + | 'code' | 'subscript' | 'superscript' + | 'heading' | 'heading-1' | 'heading-2' | 'heading-3' | 'heading-4' + | 'bullet-list' | 'ordered-list' | 'task-list' + | 'blockquote' | 'code-block' | 'horizontal-rule' + | 'align-left' | 'align-center' | 'align-right' | 'align-justify' + | 'link' | 'image' | 'table' + | 'text-color' | 'highlight-color' + | 'undo' | 'redo' + | 'clear-formatting' + | 'separator'; + +/** Preset toolbar configurations */ +export type RtToolbarPreset = 'minimal' | 'standard' | 'full' | 'markdown'; diff --git a/src/utils/editor.utils.ts b/src/utils/editor.utils.ts new file mode 100644 index 0000000..841354f --- /dev/null +++ b/src/utils/editor.utils.ts @@ -0,0 +1,36 @@ +import type { Editor } from '@tiptap/core'; + +/** Get HTML content from editor */ +export function getEditorHtml(editor: Editor): string { + return editor.getHTML(); +} + +/** Get plain text content from editor */ +export function getEditorText(editor: Editor): string { + return editor.getText(); +} + +/** Count words in text */ +export function countWords(text: string): number { + if (!text || !text.trim()) return 0; + return text.trim().split(/\s+/).length; +} + +/** Count characters in text (excluding whitespace optionally) */ +export function countCharacters(text: string, excludeWhitespace = false): number { + if (!text) return 0; + if (excludeWhitespace) { + return text.replace(/\s/g, '').length; + } + return text.length; +} + +/** Generate a unique ID */ +export function generateId(prefix = 'rt'): string { + return `${prefix}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`; +} + +/** Check if editor content is effectively empty */ +export function isEditorEmpty(editor: Editor): boolean { + return editor.isEmpty; +} diff --git a/src/utils/html.utils.ts b/src/utils/html.utils.ts new file mode 100644 index 0000000..f20fad4 --- /dev/null +++ b/src/utils/html.utils.ts @@ -0,0 +1,50 @@ +/** Allowed HTML tags for sanitization */ +const ALLOWED_TAGS = new Set([ + 'p', 'br', 'hr', + 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', + 'strong', 'b', 'em', 'i', 'u', 's', 'del', 'sub', 'sup', + 'a', 'img', + 'ul', 'ol', 'li', + 'blockquote', 'pre', 'code', + 'table', 'thead', 'tbody', 'tr', 'th', 'td', + 'span', 'div', + 'input', 'label', +]); + +/** Allowed attributes per tag */ +const ALLOWED_ATTRS: Record> = { + a: new Set(['href', 'target', 'rel', 'title']), + img: new Set(['src', 'alt', 'title', 'width', 'height']), + td: new Set(['colspan', 'rowspan']), + th: new Set(['colspan', 'rowspan']), + span: new Set(['style', 'data-type', 'data-id', 'data-label', 'class']), + div: new Set(['class', 'data-type']), + input: new Set(['type', 'checked', 'disabled']), + pre: new Set(['class']), + code: new Set(['class']), + li: new Set(['data-type', 'data-checked']), + ul: new Set(['data-type']), + p: new Set(['style']), + h1: new Set(['style']), + h2: new Set(['style']), + h3: new Set(['style']), + h4: new Set(['style']), +}; + +/** Basic HTML sanitizer — strips disallowed tags and attributes */ +export function sanitizeHtml(html: string): string { + // Remove script tags and their content + let sanitized = html.replace(/)<[^<]*)*<\/script>/gi, ''); + + // Remove event handler attributes + sanitized = sanitized.replace(/\s+on\w+\s*=\s*(?:"[^"]*"|'[^']*'|[^\s>]+)/gi, ''); + + // Remove javascript: URLs + sanitized = sanitized.replace(/href\s*=\s*(?:"javascript:[^"]*"|'javascript:[^']*')/gi, ''); + sanitized = sanitized.replace(/src\s*=\s*(?:"javascript:[^"]*"|'javascript:[^']*')/gi, ''); + + // Remove data: URLs in src (except images) + sanitized = sanitized.replace(/src\s*=\s*(?:"data:(?!image\/)[^"]*"|'data:(?!image\/)[^']*')/gi, ''); + + return sanitized; +} diff --git a/src/utils/icons.utils.ts b/src/utils/icons.utils.ts new file mode 100644 index 0000000..a8b2ccb --- /dev/null +++ b/src/utils/icons.utils.ts @@ -0,0 +1,43 @@ +import type { IconRegistry } from '@sda/base-ui'; + +/** SVG paths for rich-text icons missing from base-ui (Lucide-style, 24x24 stroke) */ +const RT_ICONS: Record = { + 'bold': '', + 'italic': '', + 'underline': '', + 'strikethrough': '', + 'code': '', + 'subscript': '', + 'superscript': '', + 'heading': '', + 'list-ordered': '', + 'list-checks': '', + 'quote': '', + 'file-code': '', + 'link': '', + 'table': '', + 'highlighter': '', + 'eraser': '', + 'align-left': '', + 'align-center': '', + 'align-right': '', + 'align-justify': '', + 'undo': '', + 'redo': '', + 'list': '', + 'image': '', + 'minus': '', + 'palette': '', + 'edit': '', + 'columns': '', + 'eye': '', +}; + +/** Register all rich-text editor icons into the base-ui IconRegistry (idempotent). */ +export function registerRtIcons(registry: IconRegistry): void { + for (const [name, svg] of Object.entries(RT_ICONS)) { + if (!registry.get(name)) { + registry.register(name, svg); + } + } +} diff --git a/src/utils/index.ts b/src/utils/index.ts new file mode 100644 index 0000000..1d7ea7d --- /dev/null +++ b/src/utils/index.ts @@ -0,0 +1,6 @@ +export * from './editor.utils'; +export * from './toolbar.utils'; +export * from './markdown.utils'; +export * from './template.utils'; +export * from './html.utils'; +export * from './icons.utils'; diff --git a/src/utils/markdown.utils.ts b/src/utils/markdown.utils.ts new file mode 100644 index 0000000..08b33d4 --- /dev/null +++ b/src/utils/markdown.utils.ts @@ -0,0 +1,69 @@ +/** Wrap selected text with a markdown syntax marker */ +export function wrapSelection( + text: string, + selectionStart: number, + selectionEnd: number, + before: string, + after: string, +): { text: string; selectionStart: number; selectionEnd: number } { + const selectedText = text.slice(selectionStart, selectionEnd); + const newText = text.slice(0, selectionStart) + before + selectedText + after + text.slice(selectionEnd); + return { + text: newText, + selectionStart: selectionStart + before.length, + selectionEnd: selectionEnd + before.length, + }; +} + +/** Insert a prefix at the beginning of the current line */ +export function insertLinePrefix( + text: string, + cursorPos: number, + prefix: string, +): { text: string; cursorPos: number } { + const lineStart = text.lastIndexOf('\n', cursorPos - 1) + 1; + const newText = text.slice(0, lineStart) + prefix + text.slice(lineStart); + return { + text: newText, + cursorPos: cursorPos + prefix.length, + }; +} + +/** Insert a block element (code block, etc.) */ +export function insertBlock( + text: string, + cursorPos: number, + blockStart: string, + blockEnd: string, + placeholder = '', +): { text: string; selectionStart: number; selectionEnd: number } { + const before = cursorPos > 0 && text[cursorPos - 1] !== '\n' ? '\n' : ''; + const after = cursorPos < text.length && text[cursorPos] !== '\n' ? '\n' : ''; + const insert = before + blockStart + '\n' + placeholder + '\n' + blockEnd + after; + const newText = text.slice(0, cursorPos) + insert + text.slice(cursorPos); + const placeholderStart = cursorPos + before.length + blockStart.length + 1; + return { + text: newText, + selectionStart: placeholderStart, + selectionEnd: placeholderStart + placeholder.length, + }; +} + +/** Toggle a line prefix (heading, list, etc.) */ +export function toggleLinePrefix( + text: string, + cursorPos: number, + prefix: string, +): { text: string; cursorPos: number } { + const lineStart = text.lastIndexOf('\n', cursorPos - 1) + 1; + const lineEnd = text.indexOf('\n', cursorPos); + const line = text.slice(lineStart, lineEnd === -1 ? undefined : lineEnd); + + if (line.startsWith(prefix)) { + const newText = text.slice(0, lineStart) + line.slice(prefix.length) + text.slice(lineEnd === -1 ? text.length : lineEnd); + return { text: newText, cursorPos: cursorPos - prefix.length }; + } + + const newText = text.slice(0, lineStart) + prefix + text.slice(lineStart); + return { text: newText, cursorPos: cursorPos + prefix.length }; +} diff --git a/src/utils/template.utils.ts b/src/utils/template.utils.ts new file mode 100644 index 0000000..781bdfc --- /dev/null +++ b/src/utils/template.utils.ts @@ -0,0 +1,57 @@ +import type { RtTemplateContext, RtTemplateVariable } from '../types/template.types'; + +/** Variable pattern: {{variable.key}} */ +const VARIABLE_PATTERN = /\{\{(\w+(?:\.\w+)*)\}\}/g; + +/** Extract all variable keys from template content */ +export function extractVariableKeys(content: string): string[] { + const keys = new Set(); + let match: RegExpExecArray | null; + while ((match = VARIABLE_PATTERN.exec(content)) !== null) { + keys.add(match[1]); + } + return Array.from(keys); +} + +/** Resolve template content by replacing variables with values */ +export function resolveTemplate(content: string, context: RtTemplateContext): string { + return content.replace(VARIABLE_PATTERN, (full, key: string) => { + const value = context[key]; + if (value === undefined || value === null) return full; + if (Array.isArray(value)) return value.join(', '); + return String(value); + }); +} + +/** Validate that all required variables have values */ +export function validateTemplateValues( + variables: RtTemplateVariable[], + context: RtTemplateContext, +): { valid: boolean; missing: string[] } { + const missing: string[] = []; + for (const v of variables) { + if (v.required && (context[v.key] === undefined || context[v.key] === '')) { + missing.push(v.key); + } + } + return { valid: missing.length === 0, missing }; +} + +/** Build a variable placeholder string */ +export function buildVariablePlaceholder(key: string): string { + return `{{${key}}}`; +} + +/** Filter variables by search query */ +export function filterVariables( + variables: RtTemplateVariable[], + query: string, +): RtTemplateVariable[] { + if (!query.trim()) return variables; + const q = query.toLowerCase(); + return variables.filter(v => + v.label.toLowerCase().includes(q) || + v.key.toLowerCase().includes(q) || + (v.description && v.description.toLowerCase().includes(q)), + ); +} diff --git a/src/utils/toolbar.utils.ts b/src/utils/toolbar.utils.ts new file mode 100644 index 0000000..4d9c9eb --- /dev/null +++ b/src/utils/toolbar.utils.ts @@ -0,0 +1,206 @@ +import type { RtToolbarGroup, RtToolbarPreset } from '../types/toolbar.types'; + +/** Toolbar preset configurations */ +export const TOOLBAR_PRESETS: Record = { + minimal: [ + { + label: 'Text formatting', + items: [ + { action: 'bold', icon: 'bold', label: 'Bold', type: 'button' }, + { action: 'italic', icon: 'italic', label: 'Italic', type: 'button' }, + { action: 'underline', icon: 'underline', label: 'Underline', type: 'button' }, + ], + }, + { + label: 'Lists', + items: [ + { action: 'bullet-list', icon: 'list', label: 'Bullet list', type: 'button' }, + { action: 'ordered-list', icon: 'list-ordered', label: 'Ordered list', type: 'button' }, + ], + }, + { + label: 'Insert', + items: [ + { action: 'link', icon: 'link', label: 'Insert link', type: 'button' }, + ], + }, + ], + + standard: [ + { + label: 'History', + items: [ + { action: 'undo', icon: 'undo', label: 'Undo', type: 'button' }, + { action: 'redo', icon: 'redo', label: 'Redo', type: 'button' }, + ], + }, + { + label: 'Headings', + items: [ + { + action: 'heading', icon: 'heading', label: 'Heading level', type: 'select', + options: [ + { value: '0', label: 'Normal text' }, + { value: '1', label: 'Heading 1' }, + { value: '2', label: 'Heading 2' }, + { value: '3', label: 'Heading 3' }, + ], + }, + ], + }, + { + label: 'Text formatting', + items: [ + { action: 'bold', icon: 'bold', label: 'Bold', type: 'button' }, + { action: 'italic', icon: 'italic', label: 'Italic', type: 'button' }, + { action: 'underline', icon: 'underline', label: 'Underline', type: 'button' }, + { action: 'strikethrough', icon: 'strikethrough', label: 'Strikethrough', type: 'button' }, + { action: 'code', icon: 'code', label: 'Inline code', type: 'button' }, + ], + }, + { + label: 'Lists', + items: [ + { action: 'bullet-list', icon: 'list', label: 'Bullet list', type: 'button' }, + { action: 'ordered-list', icon: 'list-ordered', label: 'Ordered list', type: 'button' }, + { action: 'task-list', icon: 'list-checks', label: 'Task list', type: 'button' }, + ], + }, + { + label: 'Block formatting', + items: [ + { action: 'blockquote', icon: 'quote', label: 'Blockquote', type: 'button' }, + { action: 'code-block', icon: 'file-code', label: 'Code block', type: 'button' }, + { action: 'horizontal-rule', icon: 'minus', label: 'Horizontal rule', type: 'button' }, + ], + }, + { + label: 'Insert', + items: [ + { action: 'link', icon: 'link', label: 'Insert link', type: 'button' }, + { action: 'image', icon: 'image', label: 'Insert image', type: 'button' }, + ], + }, + ], + + full: [ + { + label: 'History', + items: [ + { action: 'undo', icon: 'undo', label: 'Undo', type: 'button' }, + { action: 'redo', icon: 'redo', label: 'Redo', type: 'button' }, + ], + }, + { + label: 'Headings', + items: [ + { + action: 'heading', icon: 'heading', label: 'Heading level', type: 'select', + options: [ + { value: '0', label: 'Normal text' }, + { value: '1', label: 'Heading 1' }, + { value: '2', label: 'Heading 2' }, + { value: '3', label: 'Heading 3' }, + { value: '4', label: 'Heading 4' }, + ], + }, + ], + }, + { + label: 'Text formatting', + items: [ + { action: 'bold', icon: 'bold', label: 'Bold', type: 'button' }, + { action: 'italic', icon: 'italic', label: 'Italic', type: 'button' }, + { action: 'underline', icon: 'underline', label: 'Underline', type: 'button' }, + { action: 'strikethrough', icon: 'strikethrough', label: 'Strikethrough', type: 'button' }, + { action: 'code', icon: 'code', label: 'Inline code', type: 'button' }, + { action: 'subscript', icon: 'subscript', label: 'Subscript', type: 'button' }, + { action: 'superscript', icon: 'superscript', label: 'Superscript', type: 'button' }, + ], + }, + { + label: 'Colors', + items: [ + { action: 'text-color', icon: 'palette', label: 'Text color', type: 'button' }, + { action: 'highlight-color', icon: 'highlighter', label: 'Highlight', type: 'button' }, + ], + }, + { + label: 'Alignment', + items: [ + { action: 'align-left', icon: 'align-left', label: 'Align left', type: 'button' }, + { action: 'align-center', icon: 'align-center', label: 'Align center', type: 'button' }, + { action: 'align-right', icon: 'align-right', label: 'Align right', type: 'button' }, + { action: 'align-justify', icon: 'align-justify', label: 'Justify', type: 'button' }, + ], + }, + { + label: 'Lists', + items: [ + { action: 'bullet-list', icon: 'list', label: 'Bullet list', type: 'button' }, + { action: 'ordered-list', icon: 'list-ordered', label: 'Ordered list', type: 'button' }, + { action: 'task-list', icon: 'list-checks', label: 'Task list', type: 'button' }, + ], + }, + { + label: 'Block formatting', + items: [ + { action: 'blockquote', icon: 'quote', label: 'Blockquote', type: 'button' }, + { action: 'code-block', icon: 'file-code', label: 'Code block', type: 'button' }, + { action: 'horizontal-rule', icon: 'minus', label: 'Horizontal rule', type: 'button' }, + ], + }, + { + label: 'Insert', + items: [ + { action: 'link', icon: 'link', label: 'Insert link', type: 'button' }, + { action: 'image', icon: 'image', label: 'Insert image', type: 'button' }, + { action: 'table', icon: 'table', label: 'Insert table', type: 'button' }, + ], + }, + { + label: 'Clear', + items: [ + { action: 'clear-formatting', icon: 'eraser', label: 'Clear formatting', type: 'button' }, + ], + }, + ], + + markdown: [ + { + label: 'Text formatting', + items: [ + { action: 'bold', icon: 'bold', label: 'Bold (**text**)', type: 'button' }, + { action: 'italic', icon: 'italic', label: 'Italic (*text*)', type: 'button' }, + { action: 'strikethrough', icon: 'strikethrough', label: 'Strikethrough (~~text~~)', type: 'button' }, + { action: 'code', icon: 'code', label: 'Inline code (`code`)', type: 'button' }, + ], + }, + { + label: 'Headings', + items: [ + { action: 'heading-1', icon: 'heading', label: 'Heading 1 (#)', type: 'button' }, + { action: 'heading-2', icon: 'heading', label: 'Heading 2 (##)', type: 'button' }, + { action: 'heading-3', icon: 'heading', label: 'Heading 3 (###)', type: 'button' }, + ], + }, + { + label: 'Lists & blocks', + items: [ + { action: 'bullet-list', icon: 'list', label: 'Bullet list (- )', type: 'button' }, + { action: 'ordered-list', icon: 'list-ordered', label: 'Ordered list (1. )', type: 'button' }, + { action: 'task-list', icon: 'list-checks', label: 'Task list (- [ ] )', type: 'button' }, + { action: 'blockquote', icon: 'quote', label: 'Blockquote (> )', type: 'button' }, + { action: 'code-block', icon: 'file-code', label: 'Code block (```)', type: 'button' }, + ], + }, + { + label: 'Insert', + items: [ + { action: 'link', icon: 'link', label: 'Link [text](url)', type: 'button' }, + { action: 'image', icon: 'image', label: 'Image ![alt](url)', type: 'button' }, + { action: 'horizontal-rule', icon: 'minus', label: 'Horizontal rule (---)', type: 'button' }, + ], + }, + ], +}; diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..dddab58 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,28 @@ +{ + "compileOnSave": false, + "compilerOptions": { + "outDir": "./dist/out-tsc", + "strict": true, + "noImplicitOverride": true, + "noPropertyAccessFromIndexSignature": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true, + "skipLibCheck": true, + "esModuleInterop": true, + "sourceMap": true, + "declaration": true, + "experimentalDecorators": true, + "moduleResolution": "bundler", + "importHelpers": true, + "target": "ES2022", + "module": "ES2022", + "lib": ["ES2022", "dom"], + "useDefineForClassFields": false + }, + "angularCompilerOptions": { + "enableI18nLegacyMessageIdFormat": false, + "strictInjectionParameters": true, + "strictInputAccessModifiers": true, + "strictTemplates": true + } +}