From ccdb068d52ccfdb1f82ed32b816d63bda4ce6696 Mon Sep 17 00:00:00 2001 From: Benjamin Canac Date: Mon, 3 Nov 2025 18:08:19 +0100 Subject: [PATCH 01/98] feat(Editor): new component --- docs/content/docs/2.components/editor.md | 35 ++ package.json | 2 + .../nuxt/app/composables/useNavigation.ts | 1 + .../nuxt/app/pages/components/editor.vue | 38 ++ pnpm-lock.yaml | 568 ++++++++++++++++++ src/runtime/components/Editor.vue | 69 +++ src/runtime/types/index.ts | 1 + src/theme/editor.ts | 5 + src/theme/index.ts | 1 + test/components/Editor.spec.ts | 30 + 10 files changed, 750 insertions(+) create mode 100644 docs/content/docs/2.components/editor.md create mode 100644 playgrounds/nuxt/app/pages/components/editor.vue create mode 100644 src/runtime/components/Editor.vue create mode 100644 src/theme/editor.ts create mode 100644 test/components/Editor.spec.ts diff --git a/docs/content/docs/2.components/editor.md b/docs/content/docs/2.components/editor.md new file mode 100644 index 0000000000..7e48a05de1 --- /dev/null +++ b/docs/content/docs/2.components/editor.md @@ -0,0 +1,35 @@ +--- +title: Editor +description: '' +links: + - label: GitHub + icon: i-simple-icons-github + to: https://github.com/nuxt/ui/blob/v4/src/runtime/components/Editor.vue +navigation.badge: Soon +--- + +## Usage + +## Examples + +## API + +### Props + +:component-props + +### Slots + +:component-slots + +### Emits + +:component-emits + +## Theme + +:component-theme + +## Changelog + +:component-changelog diff --git a/package.json b/package.json index 9af19a9c71..1a303afa0c 100644 --- a/package.json +++ b/package.json @@ -118,6 +118,8 @@ "@tailwindcss/vite": "^4.1.16", "@tanstack/vue-table": "^8.21.3", "@tanstack/vue-virtual": "^3.13.12", + "@tiptap/starter-kit": "^3.10.1", + "@tiptap/vue-3": "^3.10.1", "@unhead/vue": "^2.0.19", "@vueuse/core": "^13.9.0", "@vueuse/integrations": "^13.9.0", diff --git a/playgrounds/nuxt/app/composables/useNavigation.ts b/playgrounds/nuxt/app/composables/useNavigation.ts index 6248b38e9c..a67c00cea0 100644 --- a/playgrounds/nuxt/app/composables/useNavigation.ts +++ b/playgrounds/nuxt/app/composables/useNavigation.ts @@ -27,6 +27,7 @@ const components = [ 'context-menu', 'drawer', 'dropdown-menu', + 'editor', 'empty', 'error', 'field-group', diff --git a/playgrounds/nuxt/app/pages/components/editor.vue b/playgrounds/nuxt/app/pages/components/editor.vue new file mode 100644 index 0000000000..438aedd381 --- /dev/null +++ b/playgrounds/nuxt/app/pages/components/editor.vue @@ -0,0 +1,38 @@ + + + diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5e1751a9e5..f2dfa70893 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -62,6 +62,12 @@ importers: '@tanstack/vue-virtual': specifier: ^3.13.12 version: 3.13.12(vue@3.5.22(typescript@5.8.3)) + '@tiptap/starter-kit': + specifier: ^3.10.1 + version: 3.10.1 + '@tiptap/vue-3': + specifier: ^3.10.1 + version: 3.10.1(@floating-ui/dom@1.7.4)(@tiptap/core@3.10.1(@tiptap/pm@3.10.1))(@tiptap/pm@3.10.1)(vue@3.5.22(typescript@5.8.3)) '@unhead/vue': specifier: ^2.0.19 version: 2.0.19(vue@3.5.22(typescript@5.8.3)) @@ -1790,6 +1796,9 @@ packages: peerDependencies: release-it: ^18.0.0 || ^19.0.0 + '@remirror/core-constants@3.0.0': + resolution: {integrity: sha512-42aWfPrimMfDKDi4YegyS7x+/0tlzaqwPQCULLanv3DMIlu96KTJR0fM5isWX2UViOqlGnX6YFgqWepcX+XMNg==} + '@remusao/guess-url-type@2.1.0': resolution: {integrity: sha512-zI3dlTUxpjvx2GCxp9nLOSK5yEIqDCpxlAVGwb2Y49RKkS72oeNaxxo+VWS5+XQ5+Mf8Zfp9ZXIlk+G5eoEN8A==} @@ -2257,6 +2266,153 @@ packages: peerDependencies: vue: ^2.7.0 || ^3.0.0 + '@tiptap/core@3.10.1': + resolution: {integrity: sha512-YY/u+RsjLVhcUaIn+wv6vjMx8kldO7SzFFnRu0iuC+QW57VrlqUzqz5PR6CenphwJHuqGM5b3SCr4K2ZPjN8jQ==} + peerDependencies: + '@tiptap/pm': ^3.10.1 + + '@tiptap/extension-blockquote@3.10.1': + resolution: {integrity: sha512-swBtOW1g6LMwA1LTZN2GBpdgwOD6pL/SX1GrfZJ46uQF8uBuErsUc+Iop7SX3pVPGLmQg40k0qW5k9QjEC8dGw==} + peerDependencies: + '@tiptap/core': ^3.10.1 + + '@tiptap/extension-bold@3.10.1': + resolution: {integrity: sha512-8TE9oFEonoAs0k3Vd1RGW1FiDBayJiBWyd+1eoH6EEmk1DD7quHcP1mBNZwPpjhONMITaSmizs2FjweWYibFwA==} + peerDependencies: + '@tiptap/core': ^3.10.1 + + '@tiptap/extension-bubble-menu@3.10.1': + resolution: {integrity: sha512-oNRXAupEeDCeI4nkIhCYSUuT9eZeHDWXcC5fQeWDzCPv3hOcm7W4jqUGJhWWD6qhcbmUSKmsGDTLkBfNk4NT4Q==} + peerDependencies: + '@tiptap/core': ^3.10.1 + '@tiptap/pm': ^3.10.1 + + '@tiptap/extension-bullet-list@3.10.1': + resolution: {integrity: sha512-SzE8u9QrpzculNmtxKJZAvNG2hGLwishk4oUocK8aAYGUhesKd4pLHE1emA54TgWP0t1aXstg49QIhmHcUND0A==} + peerDependencies: + '@tiptap/extension-list': ^3.10.1 + + '@tiptap/extension-code-block@3.10.1': + resolution: {integrity: sha512-Yy7XREi27aUE3S1NMihq0j4vM9rNLa3AQVHWFx1Ze2Jec2MUK7ef8WUkMs28cX76M+yB4P63Q2z8meH6HUAzyA==} + peerDependencies: + '@tiptap/core': ^3.10.1 + '@tiptap/pm': ^3.10.1 + + '@tiptap/extension-code@3.10.1': + resolution: {integrity: sha512-jeStJuFR5jpwHw/jdnqc1sVNe73dJcqDhcjmNV8cxy86BBadSGynUL1O1/vIyGbF1BFkU69UDBAOLptPH/M2Xg==} + peerDependencies: + '@tiptap/core': ^3.10.1 + + '@tiptap/extension-document@3.10.1': + resolution: {integrity: sha512-HM9lmPGKX1s9NJYQh1BD6oLqwh0gWylNmgkT6hEI7lm7DANxaYyMZue9anCDae+K6tln22BauXGAfbRb6Bs0Lw==} + peerDependencies: + '@tiptap/core': ^3.10.1 + + '@tiptap/extension-dropcursor@3.10.1': + resolution: {integrity: sha512-fF3h2Oac8vr21uJh+tiUEz/XUoEzXqx5JpoyWj6BmrTulaMY5uw+SUbh1MxN2EeZ+dUvoc8wPATvn0TTq/3GpA==} + peerDependencies: + '@tiptap/extensions': ^3.10.1 + + '@tiptap/extension-floating-menu@3.10.1': + resolution: {integrity: sha512-D5ociNnOI3OP4NxS8eKiiqjUdO7geOguK4ZhJo1CFiIXXoLyV20wqqu4fe8Hq9+4gbEyyJ55Tz/AzLiaXw/GPQ==} + peerDependencies: + '@floating-ui/dom': ^1.0.0 + '@tiptap/core': ^3.10.1 + '@tiptap/pm': ^3.10.1 + + '@tiptap/extension-gapcursor@3.10.1': + resolution: {integrity: sha512-Tg43PHL21ZgVXiQZrXmMWCx8jZGEfxB7xxamEkl0CdRFGkcXRmARXuNKT72NtCI3t7/QSlKbpyD/2/9RFGvyeA==} + peerDependencies: + '@tiptap/extensions': ^3.10.1 + + '@tiptap/extension-hard-break@3.10.1': + resolution: {integrity: sha512-kCz/ILEVr3jd4/adOfl9d62dEe9PQrHXAB5rBy1ZFoNC+C7Trq8cgpyqUYFAK7Z500nKmUgQh1GtqGN2vy338Q==} + peerDependencies: + '@tiptap/core': ^3.10.1 + + '@tiptap/extension-heading@3.10.1': + resolution: {integrity: sha512-udG4cG1pmumECEb6WDW/qYtuHcHscTMPCR6mG8hz0WpYk1S+LQWGPaQPdvHK6qYrMo/3YwQcYZv5vuQiB3dpjg==} + peerDependencies: + '@tiptap/core': ^3.10.1 + + '@tiptap/extension-horizontal-rule@3.10.1': + resolution: {integrity: sha512-P9dJrVnVlYTESmXWMDmAMHw1TLHZwKQV8Yfz1f8mCuuIHTR++hoWVgjZ70MYZzdAMCug3FWsmDjo+sxGCWOTpg==} + peerDependencies: + '@tiptap/core': ^3.10.1 + '@tiptap/pm': ^3.10.1 + + '@tiptap/extension-italic@3.10.1': + resolution: {integrity: sha512-/VbABhC20z/KWhKjcFUk7jJuOgD8Hp2V5lr6fOLFJaRpptoJhmbCRrPJzEZhs/Z55nv6aF7ZxVxtjzO0FpKneQ==} + peerDependencies: + '@tiptap/core': ^3.10.1 + + '@tiptap/extension-link@3.10.1': + resolution: {integrity: sha512-87OBwlU/ylPCDNhNyKPQaM0KiT0FscyAqh8/oErmI7gKVdrUNfO4zcqIOKHql32lEu9KsmpSum/jSeeUJMR4pA==} + peerDependencies: + '@tiptap/core': ^3.10.1 + '@tiptap/pm': ^3.10.1 + + '@tiptap/extension-list-item@3.10.1': + resolution: {integrity: sha512-YCK2N2RJGnvMTolwMD3kutnN4x1duBhUH14SdigJuPQLhDi02ck6jjTCNTjQRgDfpL9qfSLpPdn0ou7+NbFu3g==} + peerDependencies: + '@tiptap/extension-list': ^3.10.1 + + '@tiptap/extension-list-keymap@3.10.1': + resolution: {integrity: sha512-EPFZtv4yzuCRXqyIQ6v7xvDFGb9L4O+r6NpQ/Aim6fgQmElxHKs75iDet0dFWGQ/Re/o1Q7zgW3mhBcl1MLszw==} + peerDependencies: + '@tiptap/extension-list': ^3.10.1 + + '@tiptap/extension-list@3.10.1': + resolution: {integrity: sha512-v1TqDqNq3RXwKXyCoObv+42qrxAEtpac3BRZKWwwUcxM55oP5HxeaiEo2usheLs3+fEFkKtWKof2I9gUW0HLvA==} + peerDependencies: + '@tiptap/core': ^3.10.1 + '@tiptap/pm': ^3.10.1 + + '@tiptap/extension-ordered-list@3.10.1': + resolution: {integrity: sha512-dpKNFFF8QqfwSuXYoTktb3Woeqqjc3pZ4Vx4F4JSyzIlgBPLim0Wkn18ClJFIC2But/FcLm6NQrlpnimExfFlQ==} + peerDependencies: + '@tiptap/extension-list': ^3.10.1 + + '@tiptap/extension-paragraph@3.10.1': + resolution: {integrity: sha512-ocxyg947q5yOSyripEingN7SnsJ/4cYuxOg8BdNlSao8HzUTw5298/81Almf2pT0FNAJHMp8R4Xsii2oMlJ/yQ==} + peerDependencies: + '@tiptap/core': ^3.10.1 + + '@tiptap/extension-strike@3.10.1': + resolution: {integrity: sha512-NYnQOQM/HRvOcCRdetZthMMOZFpxpJ2PBuYg6u6ysotFJPWVVaegtNfZ4se0UdxDNPYInTW3gAgF05Tq/XszRQ==} + peerDependencies: + '@tiptap/core': ^3.10.1 + + '@tiptap/extension-text@3.10.1': + resolution: {integrity: sha512-Af0WBQJvjiTnEArutOZENCVNGuK7Ln3BwUH3jXsk4OUHxOyt5JK9qsDePsO46Dj9OlXHbnBi5hAnhJGI8zGLzw==} + peerDependencies: + '@tiptap/core': ^3.10.1 + + '@tiptap/extension-underline@3.10.1': + resolution: {integrity: sha512-U56hHqCSjwP8wAq28n6A+l+aNW/DxJXiaNwXs7YlC4IjRDkbsl5q53UcOlRCoVnYVY2mxj1L6Zmu2u6dhjeuSQ==} + peerDependencies: + '@tiptap/core': ^3.10.1 + + '@tiptap/extensions@3.10.1': + resolution: {integrity: sha512-tZZ1IGIcch4ezuoid3iPSirh0s2GQuSKY6ceWRJCVeZ2gT2LsN3i10tqfidcYrsmyQRMuM7QUfRmH5HOKJZ73Q==} + peerDependencies: + '@tiptap/core': ^3.10.1 + '@tiptap/pm': ^3.10.1 + + '@tiptap/pm@3.10.1': + resolution: {integrity: sha512-LhTRI+bECLFqitWN821A7faVFVw5OitFGWn45LIIRc/1Jg3lkqsaqx3LcLN1sjXd+f/vfoeXLKSD6VJvv/B/nQ==} + + '@tiptap/starter-kit@3.10.1': + resolution: {integrity: sha512-7IRZqLbvb6VWTS1nIRClQiE54I37aXFejdViTBRvxWb2TiWW8wpsfSdNAMklfwmFbg7RmOO9vaOHlilVu+donw==} + + '@tiptap/vue-3@3.10.1': + resolution: {integrity: sha512-FbJL8JsqR4V9/LnXL1PHmhmzb5476C3Hn3nFYjX+ozPNvurPz/X4qj4YM56V3p7DcOLdEyfh7usg6FwYKyuAAA==} + peerDependencies: + '@floating-ui/dom': ^1.0.0 + '@tiptap/core': ^3.10.1 + '@tiptap/pm': ^3.10.1 + vue: ^3.0.0 + '@tootallnate/quickjs-emscripten@0.23.0': resolution: {integrity: sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==} @@ -2285,15 +2441,24 @@ packages: '@types/json-schema@7.0.15': resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + '@types/linkify-it@5.0.0': + resolution: {integrity: sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==} + '@types/lodash-es@4.17.12': resolution: {integrity: sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==} '@types/lodash@4.17.20': resolution: {integrity: sha512-H3MHACvFUEiujabxhaI/ImO6gUrd8oOurg7LQtS7mbwIXA/cUqWrvBsaeJ23aZEPk1TAYkurjfMbSELfoCXlGA==} + '@types/markdown-it@14.1.2': + resolution: {integrity: sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==} + '@types/mdast@4.0.4': resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==} + '@types/mdurl@2.0.0': + resolution: {integrity: sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==} + '@types/ms@2.1.0': resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==} @@ -3427,6 +3592,9 @@ packages: resolution: {integrity: sha512-piICUB6ei4IlTv1+653yq5+KoqfBYmj9bw6LqXoOneTMDXk5nM1qt12mFW1caG3LlJXEKW1Bp0WggEmIfQB34g==} engines: {node: '>= 14'} + crelt@1.0.6: + resolution: {integrity: sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==} + croner@9.1.0: resolution: {integrity: sha512-p9nwwR4qyT5W996vBZhdvBCnMhicY5ytZkR4D1Xj0wuTDEiMnjwR57Q3RXYY/s0EpX6Ay3vgIcfaR+ewGHsi+g==} engines: {node: '>=18.0'} @@ -4865,6 +5033,12 @@ packages: lines-and-columns@1.2.4: resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + linkify-it@5.0.0: + resolution: {integrity: sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==} + + linkifyjs@4.3.2: + resolution: {integrity: sha512-NT1CJtq3hHIreOianA8aSXn6Cw0JzYOuDQbOrSPe7gqFnCpKP++MQe3ODgO3oh2GJFORkAAdqredOa60z63GbA==} + listhen@1.9.0: resolution: {integrity: sha512-I8oW2+QL5KJo8zXNWX046M134WchxsXC7SawLPvRQpogCbkyQIaFxPE89A2HiwR7vAK2Dm2ERBAmyjTYGYEpBg==} hasBin: true @@ -4950,6 +5124,10 @@ packages: magicast@0.3.5: resolution: {integrity: sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ==} + markdown-it@14.1.0: + resolution: {integrity: sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==} + hasBin: true + markdown-table@3.0.4: resolution: {integrity: sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==} @@ -5008,6 +5186,9 @@ packages: mdn-data@2.12.2: resolution: {integrity: sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA==} + mdurl@2.0.0: + resolution: {integrity: sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==} + media-typer@1.1.0: resolution: {integrity: sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==} engines: {node: '>= 0.8'} @@ -5470,6 +5651,9 @@ packages: resolution: {integrity: sha512-m0pg2zscbYgWbqRR6ABga5c3sZdEon7bSgjnlXC64kxtxLOyjRcbbUkLj7HFyy/FTD+P2xdBWu8snGhYI0jc4A==} engines: {node: '>=20'} + orderedmap@2.1.1: + resolution: {integrity: sha512-TvAWxi0nDe1j/rtMcWcIj94+Ffe6n7zhow33h40SKxmsmozs6dz/e+EajymfoFcHd7sxNn8yHM8839uixMOV6g==} + os-name@6.1.0: resolution: {integrity: sha512-zBd1G8HkewNd2A8oQ8c6BN/f/c9EId7rSUueOLGu28govmUctXmM+3765GwsByv9nYUdrLqHphXlYIc86saYsg==} engines: {node: '>=18'} @@ -5868,6 +6052,64 @@ packages: property-information@7.1.0: resolution: {integrity: sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==} + prosemirror-changeset@2.3.1: + resolution: {integrity: sha512-j0kORIBm8ayJNl3zQvD1TTPHJX3g042et6y/KQhZhnPrruO8exkTgG8X+NRpj7kIyMMEx74Xb3DyMIBtO0IKkQ==} + + prosemirror-collab@1.3.1: + resolution: {integrity: sha512-4SnynYR9TTYaQVXd/ieUvsVV4PDMBzrq2xPUWutHivDuOshZXqQ5rGbZM84HEaXKbLdItse7weMGOUdDVcLKEQ==} + + prosemirror-commands@1.7.1: + resolution: {integrity: sha512-rT7qZnQtx5c0/y/KlYaGvtG411S97UaL6gdp6RIZ23DLHanMYLyfGBV5DtSnZdthQql7W+lEVbpSfwtO8T+L2w==} + + prosemirror-dropcursor@1.8.2: + resolution: {integrity: sha512-CCk6Gyx9+Tt2sbYk5NK0nB1ukHi2ryaRgadV/LvyNuO3ena1payM2z6Cg0vO1ebK8cxbzo41ku2DE5Axj1Zuiw==} + + prosemirror-gapcursor@1.4.0: + resolution: {integrity: sha512-z00qvurSdCEWUIulij/isHaqu4uLS8r/Fi61IbjdIPJEonQgggbJsLnstW7Lgdk4zQ68/yr6B6bf7sJXowIgdQ==} + + prosemirror-history@1.4.1: + resolution: {integrity: sha512-2JZD8z2JviJrboD9cPuX/Sv/1ChFng+xh2tChQ2X4bB2HeK+rra/bmJ3xGntCcjhOqIzSDG6Id7e8RJ9QPXLEQ==} + + prosemirror-inputrules@1.5.1: + resolution: {integrity: sha512-7wj4uMjKaXWAQ1CDgxNzNtR9AlsuwzHfdFH1ygEHA2KHF2DOEaXl1CJfNPAKCg9qNEh4rum975QLaCiQPyY6Fw==} + + prosemirror-keymap@1.2.3: + resolution: {integrity: sha512-4HucRlpiLd1IPQQXNqeo81BGtkY8Ai5smHhKW9jjPKRc2wQIxksg7Hl1tTI2IfT2B/LgX6bfYvXxEpJl7aKYKw==} + + prosemirror-markdown@1.13.2: + resolution: {integrity: sha512-FPD9rHPdA9fqzNmIIDhhnYQ6WgNoSWX9StUZ8LEKapaXU9i6XgykaHKhp6XMyXlOWetmaFgGDS/nu/w9/vUc5g==} + + prosemirror-menu@1.2.5: + resolution: {integrity: sha512-qwXzynnpBIeg1D7BAtjOusR+81xCp53j7iWu/IargiRZqRjGIlQuu1f3jFi+ehrHhWMLoyOQTSRx/IWZJqOYtQ==} + + prosemirror-model@1.25.4: + resolution: {integrity: sha512-PIM7E43PBxKce8OQeezAs9j4TP+5yDpZVbuurd1h5phUxEKIu+G2a+EUZzIC5nS1mJktDJWzbqS23n1tsAf5QA==} + + prosemirror-schema-basic@1.2.4: + resolution: {integrity: sha512-ELxP4TlX3yr2v5rM7Sb70SqStq5NvI15c0j9j/gjsrO5vaw+fnnpovCLEGIcpeGfifkuqJwl4fon6b+KdrODYQ==} + + prosemirror-schema-list@1.5.1: + resolution: {integrity: sha512-927lFx/uwyQaGwJxLWCZRkjXG0p48KpMj6ueoYiu4JX05GGuGcgzAy62dfiV8eFZftgyBUvLx76RsMe20fJl+Q==} + + prosemirror-state@1.4.4: + resolution: {integrity: sha512-6jiYHH2CIGbCfnxdHbXZ12gySFY/fz/ulZE333G6bPqIZ4F+TXo9ifiR86nAHpWnfoNjOb3o5ESi7J8Uz1jXHw==} + + prosemirror-tables@1.8.1: + resolution: {integrity: sha512-DAgDoUYHCcc6tOGpLVPSU1k84kCUWTWnfWX3UDy2Delv4ryH0KqTD6RBI6k4yi9j9I8gl3j8MkPpRD/vWPZbug==} + + prosemirror-trailing-node@3.0.0: + resolution: {integrity: sha512-xiun5/3q0w5eRnGYfNlW1uU9W6x5MoFKWwq/0TIRgt09lv7Hcser2QYV8t4muXbEr+Fwo0geYn79Xs4GKywrRQ==} + peerDependencies: + prosemirror-model: ^1.22.1 + prosemirror-state: ^1.4.2 + prosemirror-view: ^1.33.8 + + prosemirror-transform@1.10.4: + resolution: {integrity: sha512-pwDy22nAnGqNR1feOQKHxoFkkUtepoFAd3r2hbEDsnf4wp57kKA36hXsB3njA9FtONBEwSDnDeCiJe+ItD+ykw==} + + prosemirror-view@1.41.3: + resolution: {integrity: sha512-SqMiYMUQNNBP9kfPhLO8WXEk/fon47vc52FQsUiJzTBuyjKgEcoAwMyF04eQ4WZ2ArMn7+ReypYL60aKngbACQ==} + proto-list@1.2.4: resolution: {integrity: sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==} @@ -5888,6 +6130,10 @@ packages: pump@3.0.3: resolution: {integrity: sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==} + punycode.js@2.3.1: + resolution: {integrity: sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==} + engines: {node: '>=6'} + punycode@2.3.1: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} @@ -6116,6 +6362,9 @@ packages: engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true + rope-sequence@1.3.4: + resolution: {integrity: sha512-UT5EDe2cu2E/6O4igUr5PSFs23nvvukicWHx6GnOPlHAiiYbzNuCRQCuiUdHJQcqKalLKlrYJnjY0ySGsXNQXQ==} + router@2.2.0: resolution: {integrity: sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==} engines: {node: '>= 18'} @@ -6674,6 +6923,9 @@ packages: engines: {node: '>=14.17'} hasBin: true + uc.micro@2.1.0: + resolution: {integrity: sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==} + ufo@1.6.1: resolution: {integrity: sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA==} @@ -7161,6 +7413,9 @@ packages: typescript: optional: true + w3c-keyname@2.2.8: + resolution: {integrity: sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==} + web-namespaces@2.0.1: resolution: {integrity: sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==} @@ -9072,6 +9327,8 @@ snapshots: - conventional-commits-filter - conventional-commits-parser + '@remirror/core-constants@3.0.0': {} + '@remusao/guess-url-type@2.1.0': {} '@remusao/small@2.1.0': {} @@ -9440,6 +9697,176 @@ snapshots: '@tanstack/virtual-core': 3.13.12 vue: 3.5.22(typescript@5.8.3) + '@tiptap/core@3.10.1(@tiptap/pm@3.10.1)': + dependencies: + '@tiptap/pm': 3.10.1 + + '@tiptap/extension-blockquote@3.10.1(@tiptap/core@3.10.1(@tiptap/pm@3.10.1))': + dependencies: + '@tiptap/core': 3.10.1(@tiptap/pm@3.10.1) + + '@tiptap/extension-bold@3.10.1(@tiptap/core@3.10.1(@tiptap/pm@3.10.1))': + dependencies: + '@tiptap/core': 3.10.1(@tiptap/pm@3.10.1) + + '@tiptap/extension-bubble-menu@3.10.1(@tiptap/core@3.10.1(@tiptap/pm@3.10.1))(@tiptap/pm@3.10.1)': + dependencies: + '@floating-ui/dom': 1.7.4 + '@tiptap/core': 3.10.1(@tiptap/pm@3.10.1) + '@tiptap/pm': 3.10.1 + optional: true + + '@tiptap/extension-bullet-list@3.10.1(@tiptap/extension-list@3.10.1(@tiptap/core@3.10.1(@tiptap/pm@3.10.1))(@tiptap/pm@3.10.1))': + dependencies: + '@tiptap/extension-list': 3.10.1(@tiptap/core@3.10.1(@tiptap/pm@3.10.1))(@tiptap/pm@3.10.1) + + '@tiptap/extension-code-block@3.10.1(@tiptap/core@3.10.1(@tiptap/pm@3.10.1))(@tiptap/pm@3.10.1)': + dependencies: + '@tiptap/core': 3.10.1(@tiptap/pm@3.10.1) + '@tiptap/pm': 3.10.1 + + '@tiptap/extension-code@3.10.1(@tiptap/core@3.10.1(@tiptap/pm@3.10.1))': + dependencies: + '@tiptap/core': 3.10.1(@tiptap/pm@3.10.1) + + '@tiptap/extension-document@3.10.1(@tiptap/core@3.10.1(@tiptap/pm@3.10.1))': + dependencies: + '@tiptap/core': 3.10.1(@tiptap/pm@3.10.1) + + '@tiptap/extension-dropcursor@3.10.1(@tiptap/extensions@3.10.1(@tiptap/core@3.10.1(@tiptap/pm@3.10.1))(@tiptap/pm@3.10.1))': + dependencies: + '@tiptap/extensions': 3.10.1(@tiptap/core@3.10.1(@tiptap/pm@3.10.1))(@tiptap/pm@3.10.1) + + '@tiptap/extension-floating-menu@3.10.1(@floating-ui/dom@1.7.4)(@tiptap/core@3.10.1(@tiptap/pm@3.10.1))(@tiptap/pm@3.10.1)': + dependencies: + '@floating-ui/dom': 1.7.4 + '@tiptap/core': 3.10.1(@tiptap/pm@3.10.1) + '@tiptap/pm': 3.10.1 + optional: true + + '@tiptap/extension-gapcursor@3.10.1(@tiptap/extensions@3.10.1(@tiptap/core@3.10.1(@tiptap/pm@3.10.1))(@tiptap/pm@3.10.1))': + dependencies: + '@tiptap/extensions': 3.10.1(@tiptap/core@3.10.1(@tiptap/pm@3.10.1))(@tiptap/pm@3.10.1) + + '@tiptap/extension-hard-break@3.10.1(@tiptap/core@3.10.1(@tiptap/pm@3.10.1))': + dependencies: + '@tiptap/core': 3.10.1(@tiptap/pm@3.10.1) + + '@tiptap/extension-heading@3.10.1(@tiptap/core@3.10.1(@tiptap/pm@3.10.1))': + dependencies: + '@tiptap/core': 3.10.1(@tiptap/pm@3.10.1) + + '@tiptap/extension-horizontal-rule@3.10.1(@tiptap/core@3.10.1(@tiptap/pm@3.10.1))(@tiptap/pm@3.10.1)': + dependencies: + '@tiptap/core': 3.10.1(@tiptap/pm@3.10.1) + '@tiptap/pm': 3.10.1 + + '@tiptap/extension-italic@3.10.1(@tiptap/core@3.10.1(@tiptap/pm@3.10.1))': + dependencies: + '@tiptap/core': 3.10.1(@tiptap/pm@3.10.1) + + '@tiptap/extension-link@3.10.1(@tiptap/core@3.10.1(@tiptap/pm@3.10.1))(@tiptap/pm@3.10.1)': + dependencies: + '@tiptap/core': 3.10.1(@tiptap/pm@3.10.1) + '@tiptap/pm': 3.10.1 + linkifyjs: 4.3.2 + + '@tiptap/extension-list-item@3.10.1(@tiptap/extension-list@3.10.1(@tiptap/core@3.10.1(@tiptap/pm@3.10.1))(@tiptap/pm@3.10.1))': + dependencies: + '@tiptap/extension-list': 3.10.1(@tiptap/core@3.10.1(@tiptap/pm@3.10.1))(@tiptap/pm@3.10.1) + + '@tiptap/extension-list-keymap@3.10.1(@tiptap/extension-list@3.10.1(@tiptap/core@3.10.1(@tiptap/pm@3.10.1))(@tiptap/pm@3.10.1))': + dependencies: + '@tiptap/extension-list': 3.10.1(@tiptap/core@3.10.1(@tiptap/pm@3.10.1))(@tiptap/pm@3.10.1) + + '@tiptap/extension-list@3.10.1(@tiptap/core@3.10.1(@tiptap/pm@3.10.1))(@tiptap/pm@3.10.1)': + dependencies: + '@tiptap/core': 3.10.1(@tiptap/pm@3.10.1) + '@tiptap/pm': 3.10.1 + + '@tiptap/extension-ordered-list@3.10.1(@tiptap/extension-list@3.10.1(@tiptap/core@3.10.1(@tiptap/pm@3.10.1))(@tiptap/pm@3.10.1))': + dependencies: + '@tiptap/extension-list': 3.10.1(@tiptap/core@3.10.1(@tiptap/pm@3.10.1))(@tiptap/pm@3.10.1) + + '@tiptap/extension-paragraph@3.10.1(@tiptap/core@3.10.1(@tiptap/pm@3.10.1))': + dependencies: + '@tiptap/core': 3.10.1(@tiptap/pm@3.10.1) + + '@tiptap/extension-strike@3.10.1(@tiptap/core@3.10.1(@tiptap/pm@3.10.1))': + dependencies: + '@tiptap/core': 3.10.1(@tiptap/pm@3.10.1) + + '@tiptap/extension-text@3.10.1(@tiptap/core@3.10.1(@tiptap/pm@3.10.1))': + dependencies: + '@tiptap/core': 3.10.1(@tiptap/pm@3.10.1) + + '@tiptap/extension-underline@3.10.1(@tiptap/core@3.10.1(@tiptap/pm@3.10.1))': + dependencies: + '@tiptap/core': 3.10.1(@tiptap/pm@3.10.1) + + '@tiptap/extensions@3.10.1(@tiptap/core@3.10.1(@tiptap/pm@3.10.1))(@tiptap/pm@3.10.1)': + dependencies: + '@tiptap/core': 3.10.1(@tiptap/pm@3.10.1) + '@tiptap/pm': 3.10.1 + + '@tiptap/pm@3.10.1': + dependencies: + prosemirror-changeset: 2.3.1 + prosemirror-collab: 1.3.1 + prosemirror-commands: 1.7.1 + prosemirror-dropcursor: 1.8.2 + prosemirror-gapcursor: 1.4.0 + prosemirror-history: 1.4.1 + prosemirror-inputrules: 1.5.1 + prosemirror-keymap: 1.2.3 + prosemirror-markdown: 1.13.2 + prosemirror-menu: 1.2.5 + prosemirror-model: 1.25.4 + prosemirror-schema-basic: 1.2.4 + prosemirror-schema-list: 1.5.1 + prosemirror-state: 1.4.4 + prosemirror-tables: 1.8.1 + prosemirror-trailing-node: 3.0.0(prosemirror-model@1.25.4)(prosemirror-state@1.4.4)(prosemirror-view@1.41.3) + prosemirror-transform: 1.10.4 + prosemirror-view: 1.41.3 + + '@tiptap/starter-kit@3.10.1': + dependencies: + '@tiptap/core': 3.10.1(@tiptap/pm@3.10.1) + '@tiptap/extension-blockquote': 3.10.1(@tiptap/core@3.10.1(@tiptap/pm@3.10.1)) + '@tiptap/extension-bold': 3.10.1(@tiptap/core@3.10.1(@tiptap/pm@3.10.1)) + '@tiptap/extension-bullet-list': 3.10.1(@tiptap/extension-list@3.10.1(@tiptap/core@3.10.1(@tiptap/pm@3.10.1))(@tiptap/pm@3.10.1)) + '@tiptap/extension-code': 3.10.1(@tiptap/core@3.10.1(@tiptap/pm@3.10.1)) + '@tiptap/extension-code-block': 3.10.1(@tiptap/core@3.10.1(@tiptap/pm@3.10.1))(@tiptap/pm@3.10.1) + '@tiptap/extension-document': 3.10.1(@tiptap/core@3.10.1(@tiptap/pm@3.10.1)) + '@tiptap/extension-dropcursor': 3.10.1(@tiptap/extensions@3.10.1(@tiptap/core@3.10.1(@tiptap/pm@3.10.1))(@tiptap/pm@3.10.1)) + '@tiptap/extension-gapcursor': 3.10.1(@tiptap/extensions@3.10.1(@tiptap/core@3.10.1(@tiptap/pm@3.10.1))(@tiptap/pm@3.10.1)) + '@tiptap/extension-hard-break': 3.10.1(@tiptap/core@3.10.1(@tiptap/pm@3.10.1)) + '@tiptap/extension-heading': 3.10.1(@tiptap/core@3.10.1(@tiptap/pm@3.10.1)) + '@tiptap/extension-horizontal-rule': 3.10.1(@tiptap/core@3.10.1(@tiptap/pm@3.10.1))(@tiptap/pm@3.10.1) + '@tiptap/extension-italic': 3.10.1(@tiptap/core@3.10.1(@tiptap/pm@3.10.1)) + '@tiptap/extension-link': 3.10.1(@tiptap/core@3.10.1(@tiptap/pm@3.10.1))(@tiptap/pm@3.10.1) + '@tiptap/extension-list': 3.10.1(@tiptap/core@3.10.1(@tiptap/pm@3.10.1))(@tiptap/pm@3.10.1) + '@tiptap/extension-list-item': 3.10.1(@tiptap/extension-list@3.10.1(@tiptap/core@3.10.1(@tiptap/pm@3.10.1))(@tiptap/pm@3.10.1)) + '@tiptap/extension-list-keymap': 3.10.1(@tiptap/extension-list@3.10.1(@tiptap/core@3.10.1(@tiptap/pm@3.10.1))(@tiptap/pm@3.10.1)) + '@tiptap/extension-ordered-list': 3.10.1(@tiptap/extension-list@3.10.1(@tiptap/core@3.10.1(@tiptap/pm@3.10.1))(@tiptap/pm@3.10.1)) + '@tiptap/extension-paragraph': 3.10.1(@tiptap/core@3.10.1(@tiptap/pm@3.10.1)) + '@tiptap/extension-strike': 3.10.1(@tiptap/core@3.10.1(@tiptap/pm@3.10.1)) + '@tiptap/extension-text': 3.10.1(@tiptap/core@3.10.1(@tiptap/pm@3.10.1)) + '@tiptap/extension-underline': 3.10.1(@tiptap/core@3.10.1(@tiptap/pm@3.10.1)) + '@tiptap/extensions': 3.10.1(@tiptap/core@3.10.1(@tiptap/pm@3.10.1))(@tiptap/pm@3.10.1) + '@tiptap/pm': 3.10.1 + + '@tiptap/vue-3@3.10.1(@floating-ui/dom@1.7.4)(@tiptap/core@3.10.1(@tiptap/pm@3.10.1))(@tiptap/pm@3.10.1)(vue@3.5.22(typescript@5.8.3))': + dependencies: + '@floating-ui/dom': 1.7.4 + '@tiptap/core': 3.10.1(@tiptap/pm@3.10.1) + '@tiptap/pm': 3.10.1 + vue: 3.5.22(typescript@5.8.3) + optionalDependencies: + '@tiptap/extension-bubble-menu': 3.10.1(@tiptap/core@3.10.1(@tiptap/pm@3.10.1))(@tiptap/pm@3.10.1) + '@tiptap/extension-floating-menu': 3.10.1(@floating-ui/dom@1.7.4)(@tiptap/core@3.10.1(@tiptap/pm@3.10.1))(@tiptap/pm@3.10.1) + '@tootallnate/quickjs-emscripten@0.23.0': {} '@trysound/sax@0.2.0': @@ -9468,16 +9895,25 @@ snapshots: '@types/json-schema@7.0.15': {} + '@types/linkify-it@5.0.0': {} + '@types/lodash-es@4.17.12': dependencies: '@types/lodash': 4.17.20 '@types/lodash@4.17.20': {} + '@types/markdown-it@14.1.2': + dependencies: + '@types/linkify-it': 5.0.0 + '@types/mdurl': 2.0.0 + '@types/mdast@4.0.4': dependencies: '@types/unist': 3.0.3 + '@types/mdurl@2.0.0': {} + '@types/ms@2.1.0': {} '@types/node@20.19.19': @@ -10675,6 +11111,8 @@ snapshots: crc-32: 1.2.2 readable-stream: 4.7.0 + crelt@1.0.6: {} + croner@9.1.0: {} cross-fetch@3.2.0: @@ -12257,6 +12695,12 @@ snapshots: lines-and-columns@1.2.4: {} + linkify-it@5.0.0: + dependencies: + uc.micro: 2.1.0 + + linkifyjs@4.3.2: {} + listhen@1.9.0: dependencies: '@parcel/watcher': 2.5.1 @@ -12355,6 +12799,15 @@ snapshots: '@babel/types': 7.28.4 source-map-js: 1.2.1 + markdown-it@14.1.0: + dependencies: + argparse: 2.0.1 + entities: 4.5.0 + linkify-it: 5.0.0 + mdurl: 2.0.0 + punycode.js: 2.3.1 + uc.micro: 2.1.0 + markdown-table@3.0.4: {} marky@1.3.0: {} @@ -12484,6 +12937,8 @@ snapshots: mdn-data@2.12.2: {} + mdurl@2.0.0: {} + media-typer@1.1.0: {} meow@13.2.0: {} @@ -13275,6 +13730,8 @@ snapshots: string-width: 8.1.0 strip-ansi: 7.1.2 + orderedmap@2.1.1: {} + os-name@6.1.0: dependencies: macos-release: 3.4.0 @@ -13701,6 +14158,109 @@ snapshots: property-information@7.1.0: {} + prosemirror-changeset@2.3.1: + dependencies: + prosemirror-transform: 1.10.4 + + prosemirror-collab@1.3.1: + dependencies: + prosemirror-state: 1.4.4 + + prosemirror-commands@1.7.1: + dependencies: + prosemirror-model: 1.25.4 + prosemirror-state: 1.4.4 + prosemirror-transform: 1.10.4 + + prosemirror-dropcursor@1.8.2: + dependencies: + prosemirror-state: 1.4.4 + prosemirror-transform: 1.10.4 + prosemirror-view: 1.41.3 + + prosemirror-gapcursor@1.4.0: + dependencies: + prosemirror-keymap: 1.2.3 + prosemirror-model: 1.25.4 + prosemirror-state: 1.4.4 + prosemirror-view: 1.41.3 + + prosemirror-history@1.4.1: + dependencies: + prosemirror-state: 1.4.4 + prosemirror-transform: 1.10.4 + prosemirror-view: 1.41.3 + rope-sequence: 1.3.4 + + prosemirror-inputrules@1.5.1: + dependencies: + prosemirror-state: 1.4.4 + prosemirror-transform: 1.10.4 + + prosemirror-keymap@1.2.3: + dependencies: + prosemirror-state: 1.4.4 + w3c-keyname: 2.2.8 + + prosemirror-markdown@1.13.2: + dependencies: + '@types/markdown-it': 14.1.2 + markdown-it: 14.1.0 + prosemirror-model: 1.25.4 + + prosemirror-menu@1.2.5: + dependencies: + crelt: 1.0.6 + prosemirror-commands: 1.7.1 + prosemirror-history: 1.4.1 + prosemirror-state: 1.4.4 + + prosemirror-model@1.25.4: + dependencies: + orderedmap: 2.1.1 + + prosemirror-schema-basic@1.2.4: + dependencies: + prosemirror-model: 1.25.4 + + prosemirror-schema-list@1.5.1: + dependencies: + prosemirror-model: 1.25.4 + prosemirror-state: 1.4.4 + prosemirror-transform: 1.10.4 + + prosemirror-state@1.4.4: + dependencies: + prosemirror-model: 1.25.4 + prosemirror-transform: 1.10.4 + prosemirror-view: 1.41.3 + + prosemirror-tables@1.8.1: + dependencies: + prosemirror-keymap: 1.2.3 + prosemirror-model: 1.25.4 + prosemirror-state: 1.4.4 + prosemirror-transform: 1.10.4 + prosemirror-view: 1.41.3 + + prosemirror-trailing-node@3.0.0(prosemirror-model@1.25.4)(prosemirror-state@1.4.4)(prosemirror-view@1.41.3): + dependencies: + '@remirror/core-constants': 3.0.0 + escape-string-regexp: 4.0.0 + prosemirror-model: 1.25.4 + prosemirror-state: 1.4.4 + prosemirror-view: 1.41.3 + + prosemirror-transform@1.10.4: + dependencies: + prosemirror-model: 1.25.4 + + prosemirror-view@1.41.3: + dependencies: + prosemirror-model: 1.25.4 + prosemirror-state: 1.4.4 + prosemirror-transform: 1.10.4 + proto-list@1.2.4: {} protocols@2.0.2: {} @@ -13730,6 +14290,8 @@ snapshots: end-of-stream: 1.4.5 once: 1.4.0 + punycode.js@2.3.1: {} + punycode@2.3.1: {} puppeteer-core@24.23.0: @@ -14115,6 +14677,8 @@ snapshots: '@rollup/rollup-win32-x64-msvc': 4.52.5 fsevents: 2.3.3 + rope-sequence@1.3.4: {} + router@2.2.0: dependencies: debug: 4.3.7 @@ -14706,6 +15270,8 @@ snapshots: typescript@5.8.3: {} + uc.micro@2.1.0: {} + ufo@1.6.1: {} uglify-js@3.19.3: @@ -15279,6 +15845,8 @@ snapshots: optionalDependencies: typescript: 5.8.3 + w3c-keyname@2.2.8: {} + web-namespaces@2.0.1: {} webdriver-bidi-protocol@0.3.6: {} diff --git a/src/runtime/components/Editor.vue b/src/runtime/components/Editor.vue new file mode 100644 index 0000000000..0c76dc4a90 --- /dev/null +++ b/src/runtime/components/Editor.vue @@ -0,0 +1,69 @@ + + + + + diff --git a/src/runtime/types/index.ts b/src/runtime/types/index.ts index 015f48fc9e..addd1f3e7f 100644 --- a/src/runtime/types/index.ts +++ b/src/runtime/types/index.ts @@ -40,6 +40,7 @@ export * from '../components/DashboardSidebarToggle.vue' export * from '../components/DashboardToolbar.vue' export * from '../components/Drawer.vue' export * from '../components/DropdownMenu.vue' +export * from '../components/Editor.vue' export * from '../components/Empty.vue' export * from '../components/Error.vue' export * from '../components/FieldGroup.vue' diff --git a/src/theme/editor.ts b/src/theme/editor.ts new file mode 100644 index 0000000000..e45acb653e --- /dev/null +++ b/src/theme/editor.ts @@ -0,0 +1,5 @@ +export default { + slots: { + root: '[&_.tiptap]:focus:outline-none' + } +} diff --git a/src/theme/index.ts b/src/theme/index.ts index 2d40b33607..c69e3a0ea4 100644 --- a/src/theme/index.ts +++ b/src/theme/index.ts @@ -39,6 +39,7 @@ export { default as dashboardSidebarToggle } from './dashboard-sidebar-toggle' export { default as dashboardToolbar } from './dashboard-toolbar' export { default as drawer } from './drawer' export { default as dropdownMenu } from './dropdown-menu' +export { default as editor } from './editor' export { default as empty } from './empty' export { default as error } from './error' export { default as fieldGroup } from './field-group' diff --git a/test/components/Editor.spec.ts b/test/components/Editor.spec.ts new file mode 100644 index 0000000000..51063aa6f2 --- /dev/null +++ b/test/components/Editor.spec.ts @@ -0,0 +1,30 @@ +import { describe, it, expect } from 'vitest' +import { axe } from 'vitest-axe' +import { mountSuspended } from '@nuxt/test-utils/runtime' +import Editor from '../../src/runtime/components/Editor.vue' +import type { EditorProps, EditorSlots } from '../../src/runtime/components/Editor.vue' +import ComponentRender from '../component-render' + +describe('Editor', () => { + const props = {} + + it.each([ + // Props + ['with as', { props: { as: 'section' } }], + ['with class', { props: { class: '' } }], + ['with ui', { props: { ui: {} } }], + // Slots + ['with default slot', { props, slots: { default: () => 'Default slot' } }] + ])('renders %s correctly', async (nameOrHtml: string, options: { props?: EditorProps, slots?: Partial }) => { + const html = await ComponentRender(nameOrHtml, options, Editor) + expect(html).toMatchSnapshot() + }) + + it('passes accessibility tests', async () => { + const wrapper = await mountSuspended(Editor, { + props + }) + + expect(await axe(wrapper.element)).toHaveNoViolations() + }) +}) From f82f6dc4d9b0f9cd1f430e0a10e2f21f3da999c1 Mon Sep 17 00:00:00 2001 From: Benjamin Canac Date: Thu, 6 Nov 2025 19:25:47 +0100 Subject: [PATCH 02/98] improve --- .../docs/2.components/editor-toolbar.md | 35 + .../nuxt/app/pages/components/editor.vue | 613 +++++++++++++++++- playgrounds/nuxt/nuxt.config.ts | 2 +- src/runtime/components/Editor.vue | 86 ++- src/runtime/components/EditorToolbar.vue | 293 +++++++++ src/runtime/types/index.ts | 1 + src/runtime/utils/editor.ts | 33 + src/theme/editor-toolbar.ts | 23 + src/theme/editor.ts | 24 +- src/theme/index.ts | 1 + test/components/EditorToolbar.spec.ts | 30 + 11 files changed, 1076 insertions(+), 65 deletions(-) create mode 100644 docs/content/docs/2.components/editor-toolbar.md create mode 100644 src/runtime/components/EditorToolbar.vue create mode 100644 src/runtime/utils/editor.ts create mode 100644 src/theme/editor-toolbar.ts create mode 100644 test/components/EditorToolbar.spec.ts diff --git a/docs/content/docs/2.components/editor-toolbar.md b/docs/content/docs/2.components/editor-toolbar.md new file mode 100644 index 0000000000..91bbbe8b1b --- /dev/null +++ b/docs/content/docs/2.components/editor-toolbar.md @@ -0,0 +1,35 @@ +--- +title: EditorToolbar +description: '' +links: + - label: GitHub + icon: i-simple-icons-github + to: https://github.com/nuxt/ui/blob/v4/src/runtime/components/EditorToolbar.vue +navigation.badge: Soon +--- + +## Usage + +## Examples + +## API + +### Props + +:component-props + +### Slots + +:component-slots + +### Emits + +:component-emits + +## Theme + +:component-theme + +## Changelog + +:component-changelog diff --git a/playgrounds/nuxt/app/pages/components/editor.vue b/playgrounds/nuxt/app/pages/components/editor.vue index 438aedd381..2b19deefef 100644 --- a/playgrounds/nuxt/app/pages/components/editor.vue +++ b/playgrounds/nuxt/app/pages/components/editor.vue @@ -1,38 +1,587 @@ diff --git a/playgrounds/nuxt/nuxt.config.ts b/playgrounds/nuxt/nuxt.config.ts index fb577b2eb1..7c0e521214 100644 --- a/playgrounds/nuxt/nuxt.config.ts +++ b/playgrounds/nuxt/nuxt.config.ts @@ -18,7 +18,7 @@ export default defineNuxtConfig({ vite: { optimizeDeps: { // prevents reloading page when navigating between components - include: ['@ai-sdk/vue', '@internationalized/date', '@tanstack/vue-table', '@tanstack/vue-virtual', '@vue/devtools-core', '@vue/devtools-kit', '@vueuse/core', '@vueuse/integrations/useFuse', '@vueuse/shared', 'colortranslator', 'embla-carousel-auto-height', 'embla-carousel-auto-scroll', 'embla-carousel-autoplay', 'embla-carousel-class-names', 'embla-carousel-fade', 'embla-carousel-vue', 'embla-carousel-wheel-gestures', 'ohash/utils', 'reka-ui', 'reka-ui/namespaced', 'scule', 'tailwind-variants', 'tailwindcss/colors', 'ufo', 'vaul-vue', 'zod'] + include: ['@ai-sdk/vue', '@internationalized/date', '@tanstack/vue-table', '@tanstack/vue-virtual', '@tiptap/vue-3', '@tiptap/starter-kit', '@vue/devtools-core', '@vue/devtools-kit', '@vueuse/core', '@vueuse/integrations/useFuse', '@vueuse/shared', 'colortranslator', 'embla-carousel-auto-height', 'embla-carousel-auto-scroll', 'embla-carousel-autoplay', 'embla-carousel-class-names', 'embla-carousel-fade', 'embla-carousel-vue', 'embla-carousel-wheel-gestures', 'ohash/utils', 'reka-ui', 'reka-ui/namespaced', 'scule', 'tailwind-variants', 'tailwindcss/colors', 'ufo', 'vaul-vue', 'zod'] } } }) diff --git a/src/runtime/components/Editor.vue b/src/runtime/components/Editor.vue index 0c76dc4a90..99f9680c0e 100644 --- a/src/runtime/components/Editor.vue +++ b/src/runtime/components/Editor.vue @@ -1,69 +1,97 @@ - diff --git a/src/runtime/components/EditorToolbar.vue b/src/runtime/components/EditorToolbar.vue new file mode 100644 index 0000000000..6c99a39268 --- /dev/null +++ b/src/runtime/components/EditorToolbar.vue @@ -0,0 +1,293 @@ + + + + + + diff --git a/src/runtime/types/index.ts b/src/runtime/types/index.ts index addd1f3e7f..67ea20c6da 100644 --- a/src/runtime/types/index.ts +++ b/src/runtime/types/index.ts @@ -41,6 +41,7 @@ export * from '../components/DashboardToolbar.vue' export * from '../components/Drawer.vue' export * from '../components/DropdownMenu.vue' export * from '../components/Editor.vue' +export * from '../components/EditorToolbar.vue' export * from '../components/Empty.vue' export * from '../components/Error.vue' export * from '../components/FieldGroup.vue' diff --git a/src/runtime/utils/editor.ts b/src/runtime/utils/editor.ts new file mode 100644 index 0000000000..9162822a2a --- /dev/null +++ b/src/runtime/utils/editor.ts @@ -0,0 +1,33 @@ +import type { Editor, Mark } from '@tiptap/vue-3' + +export function isMarkInSchema(mark: string | Mark, editor: Editor | null): boolean { + if (!editor?.schema) { + return false + } + + const markName = typeof mark === 'string' ? mark : mark.name + return editor.schema.spec.marks.get(markName) !== undefined +} + +export function isNodeTypeSelected(editor: Editor | null, nodeTypes: string[]): boolean { + if (!editor) { + return false + } + + const { selection } = editor.state + const { $from, to } = selection + + return nodeTypes.some((nodeType) => { + return editor.state.doc.nodesBetween($from.pos, to, (node) => { + return node.type.name === nodeType + }) + }) +} + +export function isExtensionAvailable(editor: Editor | null, extensionName: string): boolean { + if (!editor) { + return false + } + + return editor.extensionManager.extensions.some(ext => ext.name === extensionName) +} diff --git a/src/theme/editor-toolbar.ts b/src/theme/editor-toolbar.ts new file mode 100644 index 0000000000..7f0437497a --- /dev/null +++ b/src/theme/editor-toolbar.ts @@ -0,0 +1,23 @@ +export default { + slots: { + root: 'bg-default p-1 flex items-center gap-1.5 min-h-0', + group: 'flex items-stretch gap-0.5 min-h-0', + separator: 'w-px h-full bg-border' + }, + variants: { + variant: { + bubble: { + root: 'border border-default rounded-lg z-100' + }, + floating: { + root: 'border border-default rounded-lg z-100' + }, + fixed: { + root: '' + } + } + }, + defaultVariants: { + variant: 'fixed' + } +} diff --git a/src/theme/editor.ts b/src/theme/editor.ts index e45acb653e..84de8e2518 100644 --- a/src/theme/editor.ts +++ b/src/theme/editor.ts @@ -1,5 +1,23 @@ -export default { +import type { NuxtOptions } from '@nuxt/schema' + +export default (_options: Required) => ({ slots: { - root: '[&_.tiptap]:focus:outline-none' + root: 'flex flex-col', + base: [ + '[&_.tiptap]:focus:outline-none [&_.tiptap]:size-full [&_.tiptap]:*:first:mt-0 [&_.tiptap]:*:last:mb-0', + '[&_p]:my-5 [&_p]:leading-7 [&_p]:text-pretty', + '[&_a]:text-primary [&_a]:border-b [&_a]:border-transparent [&_a]:hover:border-primary [&_a]:font-medium [&_a]:focus-visible:outline-primary [&_a]:[&>code]:border-dashed [&_a]:hover:[&>code]:border-primary [&_a]:hover:[&>code]:text-primary [&_a]:transition-colors [&_a]:[&>code]:transition-colors', + '[&_h1]:text-4xl [&_h1]:text-highlighted [&_h1]:font-bold [&_h1]:mb-8', + '[&_h2]:relative [&_h2]:text-2xl [&_h2]:text-highlighted [&_h2]:font-bold [&_h2]:mb-6 [&_h2]:[&>a]:focus-visible:outline-primary [&_h2]:[&>a>code]:border-dashed [&_h2]:hover:[&>a>code]:border-primary [&_h2]:hover:[&>a>code]:text-primary [&_h2]:[&>a>code]:text-xl/7 [&_h2]:[&>a>code]:font-bold [&_h2]:[&>a>code]:transition-colors', + '[&_h3]:relative [&_h3]:text-xl [&_h3]:text-highlighted [&_h3]:font-bold [&_h3]:mt-8 [&_h3]:mb-3 [&_h3]:[&>a]:focus-visible:outline-primary [&_h3]:[&>a>code]:border-dashed [&_h3]:hover:[&>a>code]:border-primary [&_h3]:hover:[&>a>code]:text-primary [&_h3]:[&>a>code]:text-lg/6 [&_h3]:[&>a>code]:font-bold [&_h3]:[&>a>code]:transition-colors', + '[&_h4]:text-lg [&_h4]:text-highlighted [&_h4]:font-bold [&_h4]:mt-6 [&_h4]:mb-2 [&_h4]:[&>a]:focus-visible:outline-primary', + '[&_blockquote]:border-s-4 [&_blockquote]:border-accented [&_blockquote]:ps-4 [&_blockquote]:italic', + '[&_hr]:border-t [&_hr]:border-default [&_hr]:my-12', + '[&_pre]:my-5 [&_pre]:font-mono [&_pre]:text-sm/6 [&_pre]:border [&_pre]:border-muted [&_pre]:bg-muted [&_pre]:rounded-md [&_pre]:px-4 [&_pre]:py-3 [&_pre]:whitespace-pre-wrap [&_pre]:break-words [&_pre]:overflow-x-auto [&_pre]:focus:outline-none', + '[&_code]:px-1.5 [&_code]:py-0.5 [&_code]:text-sm [&_code]:font-mono [&_code]:font-medium [&_code]:rounded-md [&_code]:inline-block [&_code]:border [&_code]:border-muted [&_code]:text-highlighted [&_code]:bg-muted', + '[&_ul]:list-disc [&_ul]:ps-6 [&_ul]:my-5 [&_ul]:marker:text-(--ui-border-accented)', + '[&_ol]:list-decimal [&_ol]:ps-6 [&_ol]:my-5 [&_ol]:marker:text-muted', + '[&_li]:my-1.5 [&_li]:ps-1.5 [&_li]:leading-7 [&_li]:[&>ul]:my-0' + ] } -} +}) diff --git a/src/theme/index.ts b/src/theme/index.ts index c69e3a0ea4..4633bda42a 100644 --- a/src/theme/index.ts +++ b/src/theme/index.ts @@ -40,6 +40,7 @@ export { default as dashboardToolbar } from './dashboard-toolbar' export { default as drawer } from './drawer' export { default as dropdownMenu } from './dropdown-menu' export { default as editor } from './editor' +export { default as editorToolbar } from './editor-toolbar' export { default as empty } from './empty' export { default as error } from './error' export { default as fieldGroup } from './field-group' diff --git a/test/components/EditorToolbar.spec.ts b/test/components/EditorToolbar.spec.ts new file mode 100644 index 0000000000..8849e94d14 --- /dev/null +++ b/test/components/EditorToolbar.spec.ts @@ -0,0 +1,30 @@ +import { describe, it, expect } from 'vitest' +import { axe } from 'vitest-axe' +import { mountSuspended } from '@nuxt/test-utils/runtime' +import EditorToolbar from '../../src/runtime/components/EditorToolbar.vue' +import type { EditorToolbarProps, EditorToolbarSlots } from '../../src/runtime/components/EditorToolbar.vue' +import ComponentRender from '../component-render' + +describe('EditorToolbar', () => { + const props = {} + + it.each([ + // Props + ['with as', { props: { as: 'section' } }], + ['with class', { props: { class: '' } }], + ['with ui', { props: { ui: {} } }], + // Slots + ['with default slot', { props, slots: { default: () => 'Default slot' } }] + ])('renders %s correctly', async (nameOrHtml: string, options: { props?: EditorToolbarProps, slots?: Partial }) => { + const html = await ComponentRender(nameOrHtml, options, EditorToolbar) + expect(html).toMatchSnapshot() + }) + + it('passes accessibility tests', async () => { + const wrapper = await mountSuspended(EditorToolbar, { + props + }) + + expect(await axe(wrapper.element)).toHaveNoViolations() + }) +}) From 1032eea111cd32b21882fb9b96a82ee83d7bb6d5 Mon Sep 17 00:00:00 2001 From: Benjamin Canac Date: Thu, 6 Nov 2025 22:07:39 +0100 Subject: [PATCH 03/98] rename `as` to `kind` --- .../nuxt/app/pages/components/editor.vue | 50 ++++++------- src/runtime/components/EditorToolbar.vue | 73 ++++++++++--------- 2 files changed, 60 insertions(+), 63 deletions(-) diff --git a/playgrounds/nuxt/app/pages/components/editor.vue b/playgrounds/nuxt/app/pages/components/editor.vue index 2b19deefef..f8b85045c0 100644 --- a/playgrounds/nuxt/app/pages/components/editor.vue +++ b/playgrounds/nuxt/app/pages/components/editor.vue @@ -480,96 +480,90 @@ const content = ref({ }) const items: EditorToolbarItem[][] = [[{ - as: 'dropdown', + kind: 'dropdown', icon: 'i-lucide-heading', - color: 'secondary', - variant: 'solid', items: [{ type: 'label', - label: 'Heading', - as: 'blockquote' - + label: 'Headings' }, { - as: 'heading', + kind: 'heading', level: 1, icon: 'i-lucide-heading', label: 'Heading 1' }, { - type: 'separator' - }, { - as: 'heading', + kind: 'heading', level: 2, icon: 'i-lucide-heading-2', label: 'Heading 2' }, { - as: 'heading', + kind: 'heading', level: 3, icon: 'i-lucide-heading-3', label: 'Heading 3' }, { - as: 'heading', + kind: 'heading', level: 4, icon: 'i-lucide-heading-4', label: 'Heading 4' }] }, { - as: 'dropdown', + kind: 'dropdown', icon: 'i-lucide-list', items: [{ - as: 'bulletList', + kind: 'bulletList', icon: 'i-lucide-list', label: 'Bullet List' }, { - as: 'orderedList', + kind: 'orderedList', icon: 'i-lucide-list-ordered', label: 'Ordered List' }] }, { - as: 'blockquote', + kind: 'blockquote', icon: 'i-lucide-text-quote' }, { - as: 'codeBlock', + kind: 'codeBlock', icon: 'i-lucide-square-code' }, { - as: 'horizontalRule', + kind: 'horizontalRule', icon: 'i-lucide-separator-horizontal' }, { - as: 'paragraph', + kind: 'paragraph', icon: 'i-lucide-letter-text' }], [{ - as: 'mark', + kind: 'mark', mark: 'bold', icon: 'i-lucide-bold' }, { - as: 'mark', + kind: 'mark', mark: 'italic', icon: 'i-lucide-italic' }, { - as: 'mark', + kind: 'mark', mark: 'underline', icon: 'i-lucide-underline' }, { - as: 'mark', + kind: 'mark', mark: 'strike', icon: 'i-lucide-strikethrough' }, { - as: 'mark', + kind: 'mark', mark: 'code', icon: 'i-lucide-code' }], [{ - as: 'textAlign', + kind: 'textAlign', align: 'left', icon: 'i-lucide-align-left' }, { - as: 'textAlign', + kind: 'textAlign', align: 'center', icon: 'i-lucide-align-center' }, { - as: 'textAlign', + kind: 'textAlign', align: 'right', icon: 'i-lucide-align-right' }, { - as: 'textAlign', + kind: 'textAlign', align: 'justify', icon: 'i-lucide-align-justify' }]] diff --git a/src/runtime/components/EditorToolbar.vue b/src/runtime/components/EditorToolbar.vue index 6c99a39268..8ed9bd97bd 100644 --- a/src/runtime/components/EditorToolbar.vue +++ b/src/runtime/components/EditorToolbar.vue @@ -16,17 +16,17 @@ type BaseItem = Pick>) & { - as: 'dropdown' + kind: 'dropdown' } export interface EditorToolbarProps = ArrayOrNested> { @@ -115,28 +115,32 @@ function isCommandActive(command: EditorToolbarItem): boolean { return false } + if (!('kind' in command)) { + return false + } + // Dropdown commands are active if any of their items are active - if (command.as === 'dropdown') { + if (command.kind === 'dropdown') { return command.items?.some((item): boolean => isCommandActive(item as EditorToolbarItem)) || false } // For textAlign commands, check with the align parameter - if (command.as === 'textAlign' && command.align) { + if (command.kind === 'textAlign' && command.align) { return props.editor.isActive({ textAlign: command.align }) } // For heading commands, check with the level parameter - if (command.as === 'heading' && command.level) { + if (command.kind === 'heading' && command.level) { return props.editor.isActive('heading', { level: command.level }) } // For mark commands, check the mark - if (command.as === 'mark' && command.mark) { + if (command.kind === 'mark' && command.mark) { return props.editor.isActive(command.mark) } // For other node types (blockquote, bulletList, etc.) - return props.editor.isActive(command.as) + return props.editor.isActive(command.kind) } function isCommandDisabled(command: EditorToolbarItem) { @@ -145,12 +149,12 @@ function isCommandDisabled(command: EditorToolbarItem) { } // Dropdown commands are never disabled (their items might be) - if (command.as === 'dropdown') { + if (command.kind === 'dropdown') { return false } // For mark commands, check if mark is in schema and if a restricted node is selected - if (command.as === 'mark' && command.mark) { + if (command.kind === 'mark' && command.mark) { if (!isMarkInSchema(command.mark, props.editor) || isNodeTypeSelected(props.editor, ['image'])) { return true } @@ -158,7 +162,7 @@ function isCommandDisabled(command: EditorToolbarItem) { } // For textAlign commands, check extension availability and restricted nodes - if (command.as === 'textAlign' && command.align) { + if (command.kind === 'textAlign' && command.align) { if (!isExtensionAvailable(props.editor, 'textAlign') || isNodeTypeSelected(props.editor, ['image', 'horizontalRule'])) { return true } @@ -166,32 +170,32 @@ function isCommandDisabled(command: EditorToolbarItem) { } // For heading commands, check with level - if (command.as === 'heading' && command.level) { + if (command.kind === 'heading' && command.level) { return !(props.editor.can() as any).toggleHeading({ level: command.level }) } // For node commands that use toggle - if (['blockquote', 'bulletList', 'orderedList', 'codeBlock'].includes(command.as)) { - const canFunction = functionMap[command.as] as keyof typeof props.editor.can + if (['blockquote', 'bulletList', 'orderedList', 'codeBlock'].includes(command.kind)) { + const canFunction = functionMap[command.kind] as keyof typeof props.editor.can return !(props.editor.can() as any)[canFunction]() } // For commands that use set (horizontalRule, paragraph) - if (['horizontalRule', 'paragraph'].includes(command.as)) { - const canFunction = functionMap[command.as] as keyof typeof props.editor.can + if (['horizontalRule', 'paragraph'].includes(command.kind)) { + const canFunction = functionMap[command.kind] as keyof typeof props.editor.can return !(props.editor.can() as any)[canFunction]() } return false } -function onCommandClick(e: Event, command: EditorToolbarItem) { +function onCommandClick(_: Event, command: EditorToolbarItem) { if (!props.editor?.isEditable) { return } // Dropdown commands don't have actions - if (command.as === 'dropdown') { + if (command.kind === 'dropdown') { return } @@ -200,14 +204,14 @@ function onCommandClick(e: Event, command: EditorToolbarItem) { } const chain = props.editor.chain().focus() as any - const chainFunction = functionMap[command.as] + const chainFunction = functionMap[command.kind] // Handle different command types with their specific arguments - if (command.as === 'mark' && command.mark) { + if (command.kind === 'mark' && command.mark) { chain[chainFunction](command.mark).run() - } else if (command.as === 'textAlign' && command.align) { + } else if (command.kind === 'textAlign' && command.align) { chain[chainFunction](command.align).run() - } else if (command.as === 'heading' && command.level) { + } else if (command.kind === 'heading' && command.level) { chain[chainFunction]({ level: command.level }).run() } else { // For commands without arguments (blockquote, bulletList, etc.) @@ -217,34 +221,33 @@ function onCommandClick(e: Event, command: EditorToolbarItem) { function getButtonProps(command: EditorToolbarItem) { return defu(pick(command, ['label', 'color', 'activeColor', 'variant', 'activeVariant', 'size', 'icon', 'leadingIcon', 'trailingIcon', 'loading', 'loadingIcon', 'disabled', 'active', 'class', 'ui']), { - color: 'neutral', - activeColor: 'primary', - variant: 'ghost', - activeVariant: 'soft', - size: 'sm' + color: 'neutral' as const, + activeColor: 'primary' as const, + variant: 'ghost' as const, + activeVariant: 'soft' as const, + size: 'sm' as const }) } -function getDropdownProps(command: EditorToolbarItem & { as: 'dropdown' }) { +function getDropdownProps(command: EditorToolbarItem & { kind: 'dropdown' }) { return reactivePick(command, ['checkedIcon', 'loadingIcon', 'externalIcon', 'content', 'arrow', 'portal', 'modal']) } function mapDropdownItem(item: EditorToolbarItem | DropdownMenuItem) { - // If it's a separator or label (no 'as' property), return as is - if (!('as' in item)) { + // If it's a separator or label (no 'kind' property), return as is + if (!('kind' in item)) { return item } // Otherwise it's an EditorToolbarItem, add computed props return { ...item, - ...getButtonProps(item as EditorToolbarItem), active: isCommandActive(item as EditorToolbarItem), disabled: isCommandDisabled(item as EditorToolbarItem), onClick: (e: Event) => onCommandClick(e, item as EditorToolbarItem) } } -function getDropdownItems(command: EditorToolbarItem & { as: 'dropdown' }) { +function getDropdownItems(command: EditorToolbarItem & { kind: 'dropdown' }) { if (!command.items) { return [] } @@ -264,7 +267,7 @@ function getDropdownItems(command: EditorToolbarItem & { as: 'dropdown' }) {