diff --git a/frontend/package.json b/frontend/package.json index 6a79c0a5..a5ba0341 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -18,7 +18,6 @@ }, "dependencies": { "@apollo/client": "^4.2.3", - "@codemirror/lang-markdown": "^6.5.0", "@graphql-typed-document-node/core": "^3.2.0", "@hookform/resolvers": "^5.4.0", "@radix-ui/react-accordion": "^1.2.14", @@ -44,12 +43,18 @@ "@react-pdf/renderer": "^4.5.1", "@tanstack/react-table": "^8.21.3", "@tanstack/react-virtual": "^3.14.3", - "@tiptap/core": "^3.27.0", - "@tiptap/extensions": "^3.27.0", - "@tiptap/pm": "^3.27.0", - "@tiptap/react": "^3.27.0", - "@tiptap/starter-kit": "^3.27.0", - "@uiw/react-codemirror": "^4.25.10", + "@tiptap/core": "^3.27.1", + "@tiptap/extension-code": "^3.27.1", + "@tiptap/extension-code-block": "^3.27.1", + "@tiptap/extension-hard-break": "^3.27.1", + "@tiptap/extension-image": "^3.27.1", + "@tiptap/extension-list": "^3.27.1", + "@tiptap/extension-table": "^3.27.1", + "@tiptap/extensions": "^3.27.1", + "@tiptap/markdown": "^3.27.1", + "@tiptap/pm": "^3.27.1", + "@tiptap/react": "^3.27.1", + "@tiptap/starter-kit": "^3.27.1", "@xterm/addon-fit": "^0.11.0", "@xterm/addon-search": "^0.16.0", "@xterm/addon-unicode11": "^0.9.0", @@ -86,7 +91,6 @@ "remark-gfm": "^4.0.1", "sonner": "^2.0.7", "tailwind-merge": "^3.6.0", - "tiptap-markdown": "^0.9.0", "tw-animate-css": "^1.4.0", "use-debounce": "^10.1.1", "vaul": "^1.1.2", diff --git a/frontend/pnpm-lock.yaml b/frontend/pnpm-lock.yaml index 40c3f057..2274eab5 100644 --- a/frontend/pnpm-lock.yaml +++ b/frontend/pnpm-lock.yaml @@ -11,9 +11,6 @@ importers: '@apollo/client': specifier: ^4.2.3 version: 4.2.3(graphql-ws@6.0.8(graphql@16.14.2)(ws@8.21.0))(graphql@16.14.2)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(rxjs@7.8.2) - '@codemirror/lang-markdown': - specifier: ^6.5.0 - version: 6.5.0 '@graphql-typed-document-node/core': specifier: ^3.2.0 version: 3.2.0(graphql@16.14.2) @@ -90,23 +87,41 @@ importers: specifier: ^3.14.3 version: 3.14.3(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@tiptap/core': - specifier: ^3.27.0 - version: 3.27.0(@tiptap/pm@3.27.0) + specifier: ^3.27.1 + version: 3.27.1(@tiptap/pm@3.27.1) + '@tiptap/extension-code': + specifier: ^3.27.1 + version: 3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1)) + '@tiptap/extension-code-block': + specifier: ^3.27.1 + version: 3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1) + '@tiptap/extension-hard-break': + specifier: ^3.27.1 + version: 3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1)) + '@tiptap/extension-image': + specifier: ^3.27.1 + version: 3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1)) + '@tiptap/extension-list': + specifier: ^3.27.1 + version: 3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1) + '@tiptap/extension-table': + specifier: ^3.27.1 + version: 3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1) '@tiptap/extensions': - specifier: ^3.27.0 - version: 3.27.0(@tiptap/core@3.27.0(@tiptap/pm@3.27.0))(@tiptap/pm@3.27.0) + specifier: ^3.27.1 + version: 3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1) + '@tiptap/markdown': + specifier: ^3.27.1 + version: 3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1) '@tiptap/pm': - specifier: ^3.27.0 - version: 3.27.0 + specifier: ^3.27.1 + version: 3.27.1 '@tiptap/react': - specifier: ^3.27.0 - version: 3.27.0(@floating-ui/dom@1.7.6)(@tiptap/core@3.27.0(@tiptap/pm@3.27.0))(@tiptap/pm@3.27.0)(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + specifier: ^3.27.1 + version: 3.27.1(@floating-ui/dom@1.7.6)(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1)(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@tiptap/starter-kit': - specifier: ^3.27.0 - version: 3.27.0 - '@uiw/react-codemirror': - specifier: ^4.25.10 - version: 4.25.10(@babel/runtime@7.29.7)(@codemirror/autocomplete@6.20.3)(@codemirror/language@6.12.4)(@codemirror/lint@6.9.7)(@codemirror/search@6.7.1)(@codemirror/state@6.7.0)(@codemirror/theme-one-dark@6.1.3)(@codemirror/view@6.43.3)(codemirror@6.0.2)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + specifier: ^3.27.1 + version: 3.27.1 '@xterm/addon-fit': specifier: ^0.11.0 version: 0.11.0 @@ -215,9 +230,6 @@ importers: tailwind-merge: specifier: ^3.6.0 version: 3.6.0 - tiptap-markdown: - specifier: ^0.9.0 - version: 0.9.0(@tiptap/core@3.27.0(@tiptap/pm@3.27.0)) tw-animate-css: specifier: ^1.4.0 version: 1.4.0 @@ -477,42 +489,6 @@ packages: resolution: {integrity: sha512-ctxtJ/eA+t+6q2++vj5j7FYX3nRu311q1wfYH3xjlLOsczhlhxAg2FWNUXhpGvAw3BWo1xBcvOV6/YLc2r5FJw==} hasBin: true - '@codemirror/autocomplete@6.20.3': - resolution: {integrity: sha512-tlosUqb+3BbxCxZdu4tKeRghPFC+QM7q4X5YhKV2eCmPG+1r2F3f4AaSz5sCrFqUtX4Jh20VFTKecl16MgiV9g==} - - '@codemirror/commands@6.10.4': - resolution: {integrity: sha512-Ryk9y9T0FFVF0cUGhAknveAyUOl/A1qReTFi+qPKtOh2Z9F4AUBz3XOrYD4ZEgZirdugVzHvd/2/Wcwy5OliTg==} - - '@codemirror/lang-css@6.3.1': - resolution: {integrity: sha512-kr5fwBGiGtmz6l0LSJIbno9QrifNMUusivHbnA1H6Dmqy4HZFte3UAICix1VuKo0lMPKQr2rqB+0BkKi/S3Ejg==} - - '@codemirror/lang-html@6.4.11': - resolution: {integrity: sha512-9NsXp7Nwp891pQchI7gPdTwBuSuT3K65NGTHWHNJ55HjYcHLllr0rbIZNdOzas9ztc1EUVBlHou85FFZS4BNnw==} - - '@codemirror/lang-javascript@6.2.5': - resolution: {integrity: sha512-zD4e5mS+50htS7F+TYjBPsiIFGanfVqg4HyUz6WNFikgOPf2BgKlx+TQedI1w6n/IqRBVBbBWmGFdLB/7uxO4A==} - - '@codemirror/lang-markdown@6.5.0': - resolution: {integrity: sha512-0K40bZ35jpHya6FriukbgaleaqzBLZfOh7HuzqbMxBXkbYMJDxfF39c23xOgxFezR+3G+tR2/Mup+Xk865OMvw==} - - '@codemirror/language@6.12.4': - resolution: {integrity: sha512-1q4PaT+o6PbgpkJt4Q8Fv5XJxTy4FUZ4MWETtyiDw3J0Pyr9E2vqcKL+k9wcvjNTIsauxvE7OfmWj3FRPHQ76A==} - - '@codemirror/lint@6.9.7': - resolution: {integrity: sha512-28/+iWLYxKxsvGYhSYL7zaCZqLz5+FFFDq9tVsvGv9kv8RY4fFAchJ5WX9M3YrrRlTIsECjsXPqeNgnSmNP2dg==} - - '@codemirror/search@6.7.1': - resolution: {integrity: sha512-uMe5UO6PamJtSHrXhhHOzSX3ReWtiJrva6GnPMwSOrZtiExb5X5eExhr2OUZQVvdxPsKpY3Ro2mFbQadpPWmHA==} - - '@codemirror/state@6.7.0': - resolution: {integrity: sha512-Zbl9NyscLMZkfXPQnNAIIAFftidrA1UbcJEIMp24C0Bukc2I5T8wJS0wsXYsnDOqCFJUeJ1BITGNs5CqPDSmSg==} - - '@codemirror/theme-one-dark@6.1.3': - resolution: {integrity: sha512-NzBdIvEJmx6fjeremiGp3t/okrLPYT0d9orIc7AFun8oZcRk58aejkqhv6spnz4MLAevrKNPMQYXEWMg4s+sKA==} - - '@codemirror/view@6.43.3': - resolution: {integrity: sha512-MwEwCAr/o0agJefhC2+reBv5kfOQpMcDRUNQrRYZgWlhH8IwQcerMZrpqWyUFSyO0ebgN2cnh/w87F7G4BGSng==} - '@csstools/color-helpers@6.0.2': resolution: {integrity: sha512-LMGQLS9EuADloEFkcTBR3BwV/CGHV7zyDxVRtVDTwdI2Ca4it0CCVTT9wCkxSgokjE5Ho41hEPgb8OEUwoXr6Q==} engines: {node: '>=20.19.0'} @@ -1248,30 +1224,6 @@ packages: '@jridgewell/trace-mapping@0.3.31': resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} - '@lezer/common@1.5.2': - resolution: {integrity: sha512-sxQE460fPZyU3sdc8lafxiPwJHBzZRy/udNFynGQky1SePYBdhkBl1kOagA9uT3pxR8K09bOrmTUqA9wb/PjSQ==} - - '@lezer/css@1.3.3': - resolution: {integrity: sha512-RzBo8r+/6QJeow7aPHIpGVIH59xTcJXp399820gZoMo9noQDRVpJLheIBUicYwKcsbOYoBRoLZlf2720dG/4Tg==} - - '@lezer/highlight@1.2.3': - resolution: {integrity: sha512-qXdH7UqTvGfdVBINrgKhDsVTJTxactNNxLk7+UMwZhU13lMHaOBlJe9Vqp907ya56Y3+ed2tlqzys7jDkTmW0g==} - - '@lezer/html@1.3.13': - resolution: {integrity: sha512-oI7n6NJml729m7pjm9lvLvmXbdoMoi2f+1pwSDJkl9d68zGr7a9Btz8NdHTGQZtW2DA25ybeuv/SyDb9D5tseg==} - - '@lezer/javascript@1.5.4': - resolution: {integrity: sha512-vvYx3MhWqeZtGPwDStM2dwgljd5smolYD2lR2UyFcHfxbBQebqx8yjmFmxtJ/E6nN6u1D9srOiVWm3Rb4tmcUA==} - - '@lezer/lr@1.4.10': - resolution: {integrity: sha512-rnCpTIBafOx4mRp43xOxDJbFipJm/c0cia/V5TiGlhmMa+wsSdoGmUN3w5Bqrks/09Q/D4tNAmWaT8p6NRi77A==} - - '@lezer/markdown@1.6.4': - resolution: {integrity: sha512-N0SxazMj4k65DBfaf1azqtMZd6u7MqluP84/NZnB/io8Td9aleFmAhz9hcbvSfsxT5tdYlJ5qgv5aMJGY4zEtA==} - - '@marijn/find-cluster-break@1.0.2': - resolution: {integrity: sha512-l0h88YhZFyKdXIFNfSWpyjStDjGHwZ/U7iobcK1cQQD8sejsONdQtTVU+1wVN1PBw40PiiHB1vA5S7VTfQiP9g==} - '@napi-rs/wasm-runtime@1.1.5': resolution: {integrity: sha512-AWPoBRJ9tsnVhor4sjO7rkni+7p+2IAEFj6cx06UgP10jkQHqay/36uRV/bFkgrh18D9vb4cr8Q0Pthskgzy+Q==} peerDependencies: @@ -2239,154 +2191,171 @@ packages: peerDependencies: '@testing-library/dom': '>=7.21.4' - '@tiptap/core@3.27.0': - resolution: {integrity: sha512-X53TQUq2xYn21FOC526GlVIycnDkiN9XPYO/NEsg3hXS/SUs1Q6ZLtaM8y3Ox7d+bnTW6CpWKlfyvUWp3scKEw==} + '@tiptap/core@3.27.1': + resolution: {integrity: sha512-rV6Qn4wmC6BxfF+4mu6bqGWj9vA4oXXhsrpXaJL2uhjxeHAGofjwcHof2X84VYzeyXgdlsGmqKie4TAppVXZUQ==} peerDependencies: - '@tiptap/pm': 3.27.0 + '@tiptap/pm': 3.27.1 - '@tiptap/extension-blockquote@3.27.0': - resolution: {integrity: sha512-xQvy+n1m4oxv/QLjWdg+FrrKpkyQqU1qbLjVQTdQP/kczqvaJhjzbJFBc38K0vvzMUvBKH0x6sr3GQVl4D84wg==} + '@tiptap/extension-blockquote@3.27.1': + resolution: {integrity: sha512-VMF7xJx6qEGiX6DTKNiL31NLqypOcd/4sNjFSe8rb41PwejBJh/nOqVIbBvWkiT6NMGFLxMhj7zJ8/zPo1hXeg==} peerDependencies: - '@tiptap/core': 3.27.0 + '@tiptap/core': 3.27.1 - '@tiptap/extension-bold@3.27.0': - resolution: {integrity: sha512-GbnnDVbChgp9KWv+6EBuNNYEvNVbtLPYXPm809DQPi7rvb2SI/LOzLgWgVKByQqhoh498WWVBhsg/pvQ9Va9tw==} + '@tiptap/extension-bold@3.27.1': + resolution: {integrity: sha512-TlC5bsS+pqETTrlz4CZz9RO/cKBYtELGIxwtKeivUn3eNfnOxQbbu4WDsiwIfzRFyd0OMnKl6BPM2KnYEehoEQ==} peerDependencies: - '@tiptap/core': 3.27.0 + '@tiptap/core': 3.27.1 - '@tiptap/extension-bubble-menu@3.27.0': - resolution: {integrity: sha512-cy52iePYfzaUUBg3S+00KN+18KMZH+UIlE4wRTEBW/c7OA6M/nkrVkxHYfKeJ3OEbZsRYv+GiRzASchDJVVTZg==} + '@tiptap/extension-bubble-menu@3.27.1': + resolution: {integrity: sha512-j/j8Qp9Z5nViade2m7zjrO/CYH/Ca80Qj7aqo0eUaei6FZQ5izlF9o4XQU5EFMAutV6mwynsPUp8FVo5sCuYfw==} peerDependencies: - '@tiptap/core': 3.27.0 - '@tiptap/pm': 3.27.0 + '@tiptap/core': 3.27.1 + '@tiptap/pm': 3.27.1 - '@tiptap/extension-bullet-list@3.27.0': - resolution: {integrity: sha512-86x8y0IzhOnRLnq5P9eTE/kgs6j383fTME+h0uNfOFfkQmzR4GLph8nkdGraormzI3+GKQUQaO4k/6bE+PSVpg==} + '@tiptap/extension-bullet-list@3.27.1': + resolution: {integrity: sha512-faCUHnRP47o9Zh9VZZX6EX/569udw9Vopm2PgEKPWuKLE2qaS5WBuUVU0iItdJmKUqaWiOZkpoW4jvnDmj0dfg==} peerDependencies: - '@tiptap/extension-list': 3.27.0 + '@tiptap/extension-list': 3.27.1 - '@tiptap/extension-code-block@3.27.0': - resolution: {integrity: sha512-t4cQ093ZRumHG954WKwAVYoZC6bkDrZCP9uqjHWUe1wBMzhqAWQ1OuHfaHpMnzsrdtm/svDO9rB26zBTQ0yZ/Q==} + '@tiptap/extension-code-block@3.27.1': + resolution: {integrity: sha512-pHlzmZx2OlHfyQ0yRlT5UL4mGokz947DthZuYefN1OleVqOkHpWBG+2JQwqoNq6bmzMne92zbH32rhcJUEYSjA==} peerDependencies: - '@tiptap/core': 3.27.0 - '@tiptap/pm': 3.27.0 + '@tiptap/core': 3.27.1 + '@tiptap/pm': 3.27.1 - '@tiptap/extension-code@3.27.0': - resolution: {integrity: sha512-lKJqy75GRxiNncaaBs9lutsEuVQR7AKZGFfy9Sg2nRTweWLpD6eNhSHI4N04EJvTIjxZjHAe9fGU50ZhS2ONiA==} + '@tiptap/extension-code@3.27.1': + resolution: {integrity: sha512-epOUpFfEmBzjvnqvjv2qHX7NAuLo5dlOGV690lWu+sAYMjibuJBeVvAiKPyFCfRCCTUxdbDB3jbaOA1yEcEJ7w==} peerDependencies: - '@tiptap/core': 3.27.0 + '@tiptap/core': 3.27.1 - '@tiptap/extension-document@3.27.0': - resolution: {integrity: sha512-xE+rUAPAA+65Usxbn5OoPVh0I0FSPz5dYprj+uo1mogPgqpcPLGVNMMoRLZ4WdiZ3I451d5+U7CUynIjD/iikw==} + '@tiptap/extension-document@3.27.1': + resolution: {integrity: sha512-8FbBTkfnRP4iVaoj+2h3iWa+H0eGDD3yTyVCwrmue/sQTkqUNUoSuAZa3GDG4Sd41xdPwTJxl9nUWGgM1qDCnw==} peerDependencies: - '@tiptap/core': 3.27.0 + '@tiptap/core': 3.27.1 - '@tiptap/extension-dropcursor@3.27.0': - resolution: {integrity: sha512-unWa4Fs2p081Dh/VC9rShby6MqVuom5FdOuLa3JKio6PufsT+Arb87yP6bdsiyUHmWb8ZXXGtxzlCTEt/bm/TA==} + '@tiptap/extension-dropcursor@3.27.1': + resolution: {integrity: sha512-blFf9x9RG0Qr7P3FoAH/033ffa+mMLZn34trVs8Vi0Ppk6FmJAg5HpYFOtmYoeREdNDJ5rHJKV7SoACbOHgskQ==} peerDependencies: - '@tiptap/extensions': 3.27.0 + '@tiptap/extensions': 3.27.1 - '@tiptap/extension-floating-menu@3.27.0': - resolution: {integrity: sha512-S9NV/3f1IFkPtIUClLUsJj98AOL3PrYMhPAH/b7SFtsvvfp2QXfL+I12ykPqOcPmj4KXw0Zc40E0QKg67M3pjw==} + '@tiptap/extension-floating-menu@3.27.1': + resolution: {integrity: sha512-BmJF1VqB7dSJkgAalrpVFj88WLhxKjcWPuWHOqf2ITrUU2832BhKLXKmxjWUy1gqV8PfNNVWtGfIERy7I0y0+Q==} peerDependencies: '@floating-ui/dom': ^1.0.0 - '@tiptap/core': 3.27.0 - '@tiptap/pm': 3.27.0 + '@tiptap/core': 3.27.1 + '@tiptap/pm': 3.27.1 - '@tiptap/extension-gapcursor@3.27.0': - resolution: {integrity: sha512-tHIUQmtebBytVpd2f5oCUMAivdN5Yj8zRDpkA5uT3x38s9OdXLjLLYZHDD7b0ANQnN0r1vkxPXi9YF6+2XWZvA==} + '@tiptap/extension-gapcursor@3.27.1': + resolution: {integrity: sha512-QoezN0wdvXIwLQ4ee2ccWDaX3RG0lzgQpIMpMz55oPDhpUVax1+19ApsS53LkcktpS4EbnPL4xO4DaJk0Vp7PQ==} peerDependencies: - '@tiptap/extensions': 3.27.0 + '@tiptap/extensions': 3.27.1 - '@tiptap/extension-hard-break@3.27.0': - resolution: {integrity: sha512-/DDTnvuW1/3qGfX5VKEFwTDHV1/ks5jMFXgMPlHXb/ASo+kb4HEohzuUYcO74Y3vtU2zENWJ2UBqsJ96TkjgOA==} + '@tiptap/extension-hard-break@3.27.1': + resolution: {integrity: sha512-iv/m9hzl6jfSj9Q8UEjAxONvCoUDaP7M9SRCPx3PaLNxA230TTD6RE0Ye4zFJ8ze7ZVoJJMAqg9Qpq1iYg2JOQ==} peerDependencies: - '@tiptap/core': 3.27.0 + '@tiptap/core': 3.27.1 - '@tiptap/extension-heading@3.27.0': - resolution: {integrity: sha512-KDN/8RyL3/VoXpSzRtNQ3hsreBUbHgMGmpqJpRvbykQ6wJ/W421uHfZR/hCsB3RNRB0kHZJdw4hDLfP73Ff6Ig==} + '@tiptap/extension-heading@3.27.1': + resolution: {integrity: sha512-SrC4l1kEIyv9ZXFaI/8LQqU2MyMmjczw7XXsWUQOTN4YXv0JyVgMNR3cI/wz0d2xsTfBdZ1N85Tdng+Ga1t0Sg==} peerDependencies: - '@tiptap/core': 3.27.0 + '@tiptap/core': 3.27.1 - '@tiptap/extension-horizontal-rule@3.27.0': - resolution: {integrity: sha512-04Xga9CqIqzKb1cqDk9AV9pTbtleqF+o8X3bb3n7HDgplYHxLjHId6RCzhfSZU6U8VZZF/RJQ1jbTebeYqGSBw==} + '@tiptap/extension-horizontal-rule@3.27.1': + resolution: {integrity: sha512-QlKE7qn5qMnIGVGhXQlvYedvLtNJ9z0dmit5w8vPb8tKzW4Spk6M7N2kruprrDA8GBwHfeR5wmF+njfUm34qxg==} peerDependencies: - '@tiptap/core': 3.27.0 - '@tiptap/pm': 3.27.0 + '@tiptap/core': 3.27.1 + '@tiptap/pm': 3.27.1 - '@tiptap/extension-italic@3.27.0': - resolution: {integrity: sha512-z/EomYhfaf9oxtYz4+yxaIO6cw9Sa2CVw7jsnGR0jf0CZk6B+5iTbNeJDMxpSeYg+dAPOWYiO8vWOeKM0gYPFQ==} + '@tiptap/extension-image@3.27.1': + resolution: {integrity: sha512-+JTahgQT+NxiGjduaB3qJVyhU/wh4m3pVkht1Earioku2bm/apj5Lb8rSowa/NJYP3B+oQgV/V4YLw5dtDgBoA==} peerDependencies: - '@tiptap/core': 3.27.0 + '@tiptap/core': 3.27.1 - '@tiptap/extension-link@3.27.0': - resolution: {integrity: sha512-c0fxb1cccM/1JNk7HOoJytKJ9kcpKPeGq86FNHQkqf+Bn+IzPgLb8Q3W89lOp33NlCOx2xiSrUFMBWcPX34z8A==} + '@tiptap/extension-italic@3.27.1': + resolution: {integrity: sha512-jGGeyn9uRUnNjSTHpbqhiGsp6KaYTSbV09jDXPJI9cDwfV9hpugLvpaCZd0BMBbhU1B1W6kOfX0BE15qX/HQfA==} peerDependencies: - '@tiptap/core': 3.27.0 - '@tiptap/pm': 3.27.0 + '@tiptap/core': 3.27.1 - '@tiptap/extension-list-item@3.27.0': - resolution: {integrity: sha512-e2yb7Vjh5YV2ELEwxAu3EQr4fKJPwfAWAgbZbUuEOh8FX8R+bnJyBW9oWwxnivwPsxVPtYseWuKNEJb7wW+c8w==} + '@tiptap/extension-link@3.27.1': + resolution: {integrity: sha512-/2jBfsxBZUDGJmpZifqRQPz7f1E5qpS1BckTZ39TADzUJX+feKy7RJ3DtQ02+8y6SSMzvP9loGVjrk6zEMTk4g==} peerDependencies: - '@tiptap/extension-list': 3.27.0 + '@tiptap/core': 3.27.1 + '@tiptap/pm': 3.27.1 - '@tiptap/extension-list-keymap@3.27.0': - resolution: {integrity: sha512-GV8miiDOTjSduesrO4arzC7F5T78vCy0YeVmtegtA2u5w8RBjKx3sRbS45IBF08bf4GRMRGbpGeLnqe/O0rZHQ==} + '@tiptap/extension-list-item@3.27.1': + resolution: {integrity: sha512-zwRl01ETfCkWUvtvK5fw9bXtAajMPkvlkE3Cq6JvH3LF7XXJwDtNj5Tj7exacMpCaSZmlNc43vFb2rAYnrnwMA==} peerDependencies: - '@tiptap/extension-list': 3.27.0 + '@tiptap/extension-list': 3.27.1 - '@tiptap/extension-list@3.27.0': - resolution: {integrity: sha512-27L9zP3SeKxW8DBfXcJtVnlfW6lJr6xm8C/WJ/iSlts/JHv1GwWwE7FWErmpkGeX6FwLnuaZbTMbfyyTlm4Dtg==} + '@tiptap/extension-list-keymap@3.27.1': + resolution: {integrity: sha512-OIMZNlzPSO8WRd4ic73Fxckzl4N1tesjjLL2XApaNA/uMpO0LoF6WSRPAWv+Z24Wp92ARRJAnRP7iZoI5+Jxig==} peerDependencies: - '@tiptap/core': 3.27.0 - '@tiptap/pm': 3.27.0 + '@tiptap/extension-list': 3.27.1 - '@tiptap/extension-ordered-list@3.27.0': - resolution: {integrity: sha512-hiI8xcAPGOgCny8cg2md708adIdjIe2C2nNh/6TROVGfpm/tAWK13jwE1sgY+eLZTsILqpBLsEUsdF9zoS0XqQ==} + '@tiptap/extension-list@3.27.1': + resolution: {integrity: sha512-c2Upru7lj0/ZV/Ibww6cNz6sUS8m6Dp/9uygFhYcZOd3X8M0xBIEk42c6m6SQehkPziVA8QOgNJz7sMqsbz1OQ==} peerDependencies: - '@tiptap/extension-list': 3.27.0 + '@tiptap/core': 3.27.1 + '@tiptap/pm': 3.27.1 - '@tiptap/extension-paragraph@3.27.0': - resolution: {integrity: sha512-miy096NqJJwD3ulGnOleXEIYP+pOICd5A9M3Zqy1Jpc7mw0+/YMZeR5JAXqdsYV3eq/pgYpykKlF4k+Gi84RYw==} + '@tiptap/extension-ordered-list@3.27.1': + resolution: {integrity: sha512-GYrKqD//9nHJ2r80uXqbDMzRnFpGzbaEQRTSGaO/SH7DvXWFMow8evkOdjQ7PCQO07jNjJo75+A85Jwu3Ov3AA==} peerDependencies: - '@tiptap/core': 3.27.0 + '@tiptap/extension-list': 3.27.1 - '@tiptap/extension-strike@3.27.0': - resolution: {integrity: sha512-srDgYCxu2OdYGB8rtJraYzrXCvj5qRmyrSG4NObVfDYNoMuVdkbNlxA8/LNJeKKZ9H8PZlrspvmxM4F91Lvb9g==} + '@tiptap/extension-paragraph@3.27.1': + resolution: {integrity: sha512-7K7eo1gruOgAsnbK+GCV23AUVUI0cL1bTig8HaPneoFMVbig7vddk8jNLKBWO8TXVbG7TuHdnDN4F98vdtwh5Q==} peerDependencies: - '@tiptap/core': 3.27.0 + '@tiptap/core': 3.27.1 - '@tiptap/extension-text@3.27.0': - resolution: {integrity: sha512-3ZiEeDDbvhvXwqSre62yxhe0Rc7t2+BGSNoVCCUFPsNUNvBL9s9bOwwnOG4fPZNBCyF38qBnTxblDwJ92E4AGw==} + '@tiptap/extension-strike@3.27.1': + resolution: {integrity: sha512-Y3DW1jlSlCNCyMGHP3+3qBNNPS83wuFz4RTYGjZtvRRTCRh7apZme9XRWMq1rN5mJ2Cr7fKocA2/5Bs13KgN6Q==} peerDependencies: - '@tiptap/core': 3.27.0 + '@tiptap/core': 3.27.1 - '@tiptap/extension-underline@3.27.0': - resolution: {integrity: sha512-0w3GDF5rC0oZXYh6jBt8jPS+SQ5mtJsyDjP94A0Qjh/7tsfeDzCzZMAadE4i+9g6DI1Hce4GqYDAAfPkoj4y5Q==} + '@tiptap/extension-table@3.27.1': + resolution: {integrity: sha512-tNB8kjxo0+XPremnWkd6NUikpeJ+JQrss7HntL8GgIxX4t0gkdnLn9yUWb0JzcaYP7Y8iTZZHdkPs0P154DG8Q==} peerDependencies: - '@tiptap/core': 3.27.0 + '@tiptap/core': 3.27.1 + '@tiptap/pm': 3.27.1 - '@tiptap/extensions@3.27.0': - resolution: {integrity: sha512-G4ufNa/F4upDI/2YWFBCVBs4skcCeQ2jvRQiFrLJCitUFcl03evP0znAvx5JKj9+nfm4ILYpPpncEFyMq4mFEg==} + '@tiptap/extension-text@3.27.1': + resolution: {integrity: sha512-6ZwaZwSrDh+KFFv6V1J79oO37yPs7y1bFxvk1/9Ih2rn3Xr5AWz+eMS+n8RpH3djBVVAQpdIAeYQgcn+VCSsTg==} peerDependencies: - '@tiptap/core': 3.27.0 - '@tiptap/pm': 3.27.0 + '@tiptap/core': 3.27.1 - '@tiptap/pm@3.27.0': - resolution: {integrity: sha512-cWuyaY19SoTOGwdPzhNrUuFMMILuR7mq/Ec02FO+y5jUnQiJYOy9pVx4RUIjBT24LYXrvA9YRhxL1v+KQKTz3A==} - - '@tiptap/react@3.27.0': - resolution: {integrity: sha512-C7FSa5yNPKaCll/Bvm0GmlJh2VLVJXNpKxC9I/L5BJdeVWXV3Prbt56U9FFYd8hjydFZ32GF3jUYQlVYHxnwHw==} + '@tiptap/extension-underline@3.27.1': + resolution: {integrity: sha512-N889J4nXN/TPfVt8uF9N1A0SY82E90zwc1y26lqOcw6KWNLmQrlhMh/9OD4ikLDbekmFpOBq/UicpHf/6S8hbQ==} peerDependencies: - '@tiptap/core': 3.27.0 - '@tiptap/pm': 3.27.0 + '@tiptap/core': 3.27.1 + + '@tiptap/extensions@3.27.1': + resolution: {integrity: sha512-1Tdx9faw8k0/83V6X+xCDVhV8yElGt95JxeW3YMkKQJI56QdlPz0xOdJPlMiSGJKinPyVier+x9LJD/YZUZIaw==} + peerDependencies: + '@tiptap/core': 3.27.1 + '@tiptap/pm': 3.27.1 + + '@tiptap/markdown@3.27.1': + resolution: {integrity: sha512-4m2LZcj/uN0uLlGnXr3i7sfdxbQNNmeMn4wFyFrP2xpshcKPL5WujUAcqT2rgKfaDjKQ8gIK/GpgSQKuRENFwg==} + peerDependencies: + '@tiptap/core': 3.27.1 + '@tiptap/pm': 3.27.1 + + '@tiptap/pm@3.27.1': + resolution: {integrity: sha512-Ffjx+vimmBU7zH/KrpXzJid3+pziCe/VL2aexSTP63cyQwKQ65LkFkCKaIsSpFdQQuakVZBGWjCA5RoBV852pw==} + + '@tiptap/react@3.27.1': + resolution: {integrity: sha512-/Wn2fc9zMtX08MXYScDFsm4wJ8lzfhfPEdbtls7WCDlbtrop48PWlkHDBBJrywARfAQTB2mFs9KiFy9yrQm5Lg==} + peerDependencies: + '@tiptap/core': 3.27.1 + '@tiptap/pm': 3.27.1 '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 '@types/react-dom': ^17.0.0 || ^18.0.0 || ^19.0.0 react: ^17.0.0 || ^18.0.0 || ^19.0.0 react-dom: ^17.0.0 || ^18.0.0 || ^19.0.0 - '@tiptap/starter-kit@3.27.0': - resolution: {integrity: sha512-AFrNuIpeyr2FJsWP5sjD0uhJfPBHdEKBByrqLu4O9h/OI8qJYKaPo9PxMXA2jQlWgDq6z0r8GPrr3PbJCMe1JA==} + '@tiptap/starter-kit@3.27.1': + resolution: {integrity: sha512-vfxRsqW8rCc0k4pzo0ilU3wobVi2wqVj88VZI2SlgZlNnUAkrDGDIAph7CTa9k9fshV+O1ivpEgPC5yC046jow==} '@tybys/wasm-util@0.10.2': resolution: {integrity: sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==} @@ -2442,30 +2411,12 @@ packages: '@types/json-schema@7.0.15': resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} - '@types/linkify-it@3.0.5': - resolution: {integrity: sha512-yg6E+u0/+Zjva+buc3EIb+29XEg4wltq7cSmd4Uc2EE/1nUVmxyzpX6gUXD0V8jIrG0r7YeOGVIbYRkxeooCtw==} - - '@types/linkify-it@5.0.0': - resolution: {integrity: sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==} - '@types/lodash@4.17.24': resolution: {integrity: sha512-gIW7lQLZbue7lRSWEFql49QJJWThrTFFeIMJdp3eH4tKoxm1OvEPg02rm4wCCSHS0cL3/Fizimb35b7k8atwsQ==} - '@types/markdown-it@13.0.9': - resolution: {integrity: sha512-1XPwR0+MgXLWfTn9gCsZ55AHOKW1WN+P9vr0PaQh5aerR9LLQXUbjfEAFhjmEmyoYFWAyuN2Mqkn40MZ4ukjBw==} - - '@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@1.0.5': - resolution: {integrity: sha512-6L6VymKTzYSrEf4Nev4Xa1LCHKrlTlYCBMTlQKFuddo1CvQcE52I0mwfOJayueUC7MJuXOeHTcIU683lzd0cUA==} - - '@types/mdurl@2.0.0': - resolution: {integrity: sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==} - '@types/ms@2.1.0': resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==} @@ -2554,28 +2505,6 @@ packages: resolution: {integrity: sha512-6fJ9MHWtK14C1DSkiMlHUSOmrVebL7150xZJBlJiL62jjhIA4JmOq6flwBgDxIdBKKdoiZRel+dfPD5MLfny3w==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@uiw/codemirror-extensions-basic-setup@4.25.10': - resolution: {integrity: sha512-P3vytLlpE62KYSWrMUnwDCv2lvaQDuDZzyj03mHntuHo5bSl34fRZpjTY3kQTPGuXHxkGSYpoPFFj+hMTqaaMQ==} - peerDependencies: - '@codemirror/autocomplete': '>=6.0.0' - '@codemirror/commands': '>=6.0.0' - '@codemirror/language': '>=6.0.0' - '@codemirror/lint': '>=6.0.0' - '@codemirror/search': '>=6.0.0' - '@codemirror/state': '>=6.0.0' - '@codemirror/view': '>=6.0.0' - - '@uiw/react-codemirror@4.25.10': - resolution: {integrity: sha512-DzgSMwM5qzB7v1FIb4gEeriYt67iiay756/HIOM9mAbeOVK0MO7rqefHf0O5c0269pJKMW7AH9FjclExD23V9w==} - peerDependencies: - '@babel/runtime': '>=7.11.0' - '@codemirror/state': '>=6.0.0' - '@codemirror/theme-one-dark': '>=6.0.0' - '@codemirror/view': '>=6.0.0' - codemirror: '>=6.0.0' - react: '>=17.0.0' - react-dom: '>=17.0.0' - '@ungap/structured-clone@1.3.1': resolution: {integrity: sha512-mUFwbeTqrVgDQxFveS+df2yfap6iuP20NAKAsBt5jDEoOTDew+zwLAOilHCeQJOVSvmgCX4ogqIrA0mnyr08yQ==} @@ -2955,9 +2884,6 @@ packages: react: ^18 || ^19 || ^19.0.0-rc react-dom: ^18 || ^19 || ^19.0.0-rc - codemirror@6.0.2: - resolution: {integrity: sha512-VhydHotNW5w1UGK0Qj96BwSk/Zqbp9WbnyK2W/eVMv4QyF41INRGpjUhFJY7/uDNuudSc33a/PKr4iDqRduvHw==} - color-convert@2.0.1: resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} engines: {node: '>=7.0.0'} @@ -3036,9 +2962,6 @@ packages: typescript: optional: true - crelt@1.0.6: - resolution: {integrity: sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==} - cross-inspect@1.0.1: resolution: {integrity: sha512-Pcw1JTvZLSJH83iiGWt6fRcT+BjZlCDRVwYLbUcHzv/CRpB7r0MlSrGbIyQvVSNyGnbt7G4AXuyCiDR3POvZ1A==} engines: {node: '>=16.0.0'} @@ -3268,10 +3191,6 @@ packages: entities@2.2.0: resolution: {integrity: sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==} - entities@4.5.0: - resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} - engines: {node: '>=0.12'} - entities@8.0.0: resolution: {integrity: sha512-zwfzJecQ/Uej6tusMqwAqU/6KL2XaB2VZ2Jg54Je6ahNBGNH6Ek6g3jjNCF0fG9EWQKGZNddNjU5F1ZQn/sBnA==} engines: {node: '>=20.19.0'} @@ -4150,9 +4069,6 @@ packages: lines-and-columns@1.2.4: resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} - linkify-it@5.0.1: - resolution: {integrity: sha512-wVoTjP4Q6R0NW5hiZkVJaFZPWgtXfoGF+6LucL3/FtiNjmcHhYjEr5f1Kqjirc1nBW07J/ZuRFumqr2oqccEWg==} - linkifyjs@4.3.3: resolution: {integrity: sha512-P8aEP5U/D1/IlTY2OeYsErdwh9bGuLE30NcXtKEjgdHcahveQoQwM2yZNsioQHsWFz0P7KKudisbrzCgR0sDHg==} @@ -4224,16 +4140,14 @@ packages: resolution: {integrity: sha512-8y/eV9QQZCiyn1SprXSrCmqJN0yNRATe+PO8ztwqrvrbdRLA3eYJF0yaR0YayLWkMbsQSKWS9N2gPcGEc4UsZg==} engines: {node: '>=0.10.0'} - markdown-it-task-lists@2.1.1: - resolution: {integrity: sha512-TxFAc76Jnhb2OUu+n3yz9RMu4CwGfaT788br6HhEDlvWfdeJcLUsxk1Hgw2yJio0OXsxv7pyIPmvECY7bMbluA==} - - markdown-it@14.2.0: - resolution: {integrity: sha512-1TGiQiJVRQ3NPmZH6sx5Cfnmg6GQm9jvC1ch4TK511NjSJvjzKLzn5pPfZRNZkRPZP0HqCioSndqH8v2nRaWVQ==} - hasBin: true - markdown-table@3.0.4: resolution: {integrity: sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==} + marked@17.0.6: + resolution: {integrity: sha512-gB0gkNafnonOw0obSTEGZTT86IuhILt2Wfx0mWH/1Au83kybTayroZ/V6nS25mN7u8ASy+5fMhgB3XPNrOZdmA==} + engines: {node: '>= 20'} + hasBin: true + marked@18.0.5: resolution: {integrity: sha512-S6GcvALHg6K4ohtu4E7x0a1AqhAjp6cV8KhLSyN9qVapnzJkusVBxZRcIU9AeYsbe6P1hKDusSbEOzGyyuce6w==} engines: {node: '>= 20'} @@ -4291,9 +4205,6 @@ packages: mdn-data@2.27.1: resolution: {integrity: sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==} - mdurl@2.0.0: - resolution: {integrity: sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==} - media-engine@1.0.3: resolution: {integrity: sha512-aa5tG6sDoK+k70B9iEX1NeyfT8ObCKhNDs6lJVpwF6r8vhUfuKMslIcirq6HIUYuuUYLefcEQOn9bSBOvawtwg==} @@ -4744,9 +4655,6 @@ packages: prosemirror-keymap@1.2.3: resolution: {integrity: sha512-4HucRlpiLd1IPQQXNqeo81BGtkY8Ai5smHhKW9jjPKRc2wQIxksg7Hl1tTI2IfT2B/LgX6bfYvXxEpJl7aKYKw==} - prosemirror-markdown@1.13.4: - resolution: {integrity: sha512-D98dm4cQ3Hs6EmjK500TdAOew4Z03EV71ajEFiWra3Upr7diytJsjF4mPV2dW+eK5uNectiRj0xFxYI9NLXDbw==} - prosemirror-model@1.25.9: resolution: {integrity: sha512-pRTklkDDMMRopyoAcrr9wV/8g/RYgrLHBuJAb5hlEuYZRdm5yqmPjWId83fpBwPpSFqEdja0H7Dfd7z1X/npcA==} @@ -4769,10 +4677,6 @@ packages: resolution: {integrity: sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==} engines: {node: '>=10'} - 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'} @@ -5197,9 +5101,6 @@ packages: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} - style-mod@4.1.3: - resolution: {integrity: sha512-i/n8VsZydrugj3Iuzll8+x/00GH2vnYsk1eomD8QiRrSAeW6ItbCQDtfXCeJHd0iwiNagqjQkvpvREEPtW3IoQ==} - style-to-js@1.1.21: resolution: {integrity: sha512-RjQetxJrrUJLQPHbLku6U/ocGtzyjbJMP9lCNK7Ag0CNh690nSH8woqWH9u16nMjYBAok+i7JO1NP2pOy8IsPQ==} @@ -5270,11 +5171,6 @@ packages: resolution: {integrity: sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==} engines: {node: '>=14.0.0'} - tiptap-markdown@0.9.0: - resolution: {integrity: sha512-dKLQ9iiuGNgrlGVjrNauF/UBzWu4LYOx5pkD0jNkmQt/GOwfCJsBuzZTsf1jZ204ANHOm572mZ9PYvGh1S7tpQ==} - peerDependencies: - '@tiptap/core': ^3.0.1 - title-case@3.0.3: resolution: {integrity: sha512-e1zGYRvbffpcHIrnuqT0Dh+gEJtDaxDSoG4JAIpq4oDFyooziLBIiYQv0GBT4FUAnUop5uZ1hiIAj7oAF6sOCA==} @@ -5360,9 +5256,6 @@ packages: engines: {node: '>=14.17'} hasBin: true - uc.micro@2.1.0: - resolution: {integrity: sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==} - unbox-primitive@1.1.0: resolution: {integrity: sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==} engines: {node: '>= 0.4'} @@ -5894,99 +5787,6 @@ snapshots: dependencies: css-tree: 3.2.1 - '@codemirror/autocomplete@6.20.3': - dependencies: - '@codemirror/language': 6.12.4 - '@codemirror/state': 6.7.0 - '@codemirror/view': 6.43.3 - '@lezer/common': 1.5.2 - - '@codemirror/commands@6.10.4': - dependencies: - '@codemirror/language': 6.12.4 - '@codemirror/state': 6.7.0 - '@codemirror/view': 6.43.3 - '@lezer/common': 1.5.2 - - '@codemirror/lang-css@6.3.1': - dependencies: - '@codemirror/autocomplete': 6.20.3 - '@codemirror/language': 6.12.4 - '@codemirror/state': 6.7.0 - '@lezer/common': 1.5.2 - '@lezer/css': 1.3.3 - - '@codemirror/lang-html@6.4.11': - dependencies: - '@codemirror/autocomplete': 6.20.3 - '@codemirror/lang-css': 6.3.1 - '@codemirror/lang-javascript': 6.2.5 - '@codemirror/language': 6.12.4 - '@codemirror/state': 6.7.0 - '@codemirror/view': 6.43.3 - '@lezer/common': 1.5.2 - '@lezer/css': 1.3.3 - '@lezer/html': 1.3.13 - - '@codemirror/lang-javascript@6.2.5': - dependencies: - '@codemirror/autocomplete': 6.20.3 - '@codemirror/language': 6.12.4 - '@codemirror/lint': 6.9.7 - '@codemirror/state': 6.7.0 - '@codemirror/view': 6.43.3 - '@lezer/common': 1.5.2 - '@lezer/javascript': 1.5.4 - - '@codemirror/lang-markdown@6.5.0': - dependencies: - '@codemirror/autocomplete': 6.20.3 - '@codemirror/lang-html': 6.4.11 - '@codemirror/language': 6.12.4 - '@codemirror/state': 6.7.0 - '@codemirror/view': 6.43.3 - '@lezer/common': 1.5.2 - '@lezer/markdown': 1.6.4 - - '@codemirror/language@6.12.4': - dependencies: - '@codemirror/state': 6.7.0 - '@codemirror/view': 6.43.3 - '@lezer/common': 1.5.2 - '@lezer/highlight': 1.2.3 - '@lezer/lr': 1.4.10 - style-mod: 4.1.3 - - '@codemirror/lint@6.9.7': - dependencies: - '@codemirror/state': 6.7.0 - '@codemirror/view': 6.43.3 - crelt: 1.0.6 - - '@codemirror/search@6.7.1': - dependencies: - '@codemirror/state': 6.7.0 - '@codemirror/view': 6.43.3 - crelt: 1.0.6 - - '@codemirror/state@6.7.0': - dependencies: - '@marijn/find-cluster-break': 1.0.2 - - '@codemirror/theme-one-dark@6.1.3': - dependencies: - '@codemirror/language': 6.12.4 - '@codemirror/state': 6.7.0 - '@codemirror/view': 6.43.3 - '@lezer/highlight': 1.2.3 - - '@codemirror/view@6.43.3': - dependencies: - '@codemirror/state': 6.7.0 - crelt: 1.0.6 - style-mod: 4.1.3 - w3c-keyname: 2.2.8 - '@csstools/color-helpers@6.0.2': {} '@csstools/css-calc@3.2.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)': @@ -6822,41 +6622,6 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.5 - '@lezer/common@1.5.2': {} - - '@lezer/css@1.3.3': - dependencies: - '@lezer/common': 1.5.2 - '@lezer/highlight': 1.2.3 - '@lezer/lr': 1.4.10 - - '@lezer/highlight@1.2.3': - dependencies: - '@lezer/common': 1.5.2 - - '@lezer/html@1.3.13': - dependencies: - '@lezer/common': 1.5.2 - '@lezer/highlight': 1.2.3 - '@lezer/lr': 1.4.10 - - '@lezer/javascript@1.5.4': - dependencies: - '@lezer/common': 1.5.2 - '@lezer/highlight': 1.2.3 - '@lezer/lr': 1.4.10 - - '@lezer/lr@1.4.10': - dependencies: - '@lezer/common': 1.5.2 - - '@lezer/markdown@1.6.4': - dependencies: - '@lezer/common': 1.5.2 - '@lezer/highlight': 1.2.3 - - '@marijn/find-cluster-break@1.0.2': {} - '@napi-rs/wasm-runtime@1.1.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)': dependencies: '@emnapi/core': 1.10.0 @@ -7792,119 +7557,134 @@ snapshots: dependencies: '@testing-library/dom': 10.4.1 - '@tiptap/core@3.27.0(@tiptap/pm@3.27.0)': + '@tiptap/core@3.27.1(@tiptap/pm@3.27.1)': dependencies: - '@tiptap/pm': 3.27.0 + '@tiptap/pm': 3.27.1 - '@tiptap/extension-blockquote@3.27.0(@tiptap/core@3.27.0(@tiptap/pm@3.27.0))': + '@tiptap/extension-blockquote@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))': dependencies: - '@tiptap/core': 3.27.0(@tiptap/pm@3.27.0) + '@tiptap/core': 3.27.1(@tiptap/pm@3.27.1) - '@tiptap/extension-bold@3.27.0(@tiptap/core@3.27.0(@tiptap/pm@3.27.0))': + '@tiptap/extension-bold@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))': dependencies: - '@tiptap/core': 3.27.0(@tiptap/pm@3.27.0) + '@tiptap/core': 3.27.1(@tiptap/pm@3.27.1) - '@tiptap/extension-bubble-menu@3.27.0(@tiptap/core@3.27.0(@tiptap/pm@3.27.0))(@tiptap/pm@3.27.0)': + '@tiptap/extension-bubble-menu@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1)': dependencies: '@floating-ui/dom': 1.7.6 - '@tiptap/core': 3.27.0(@tiptap/pm@3.27.0) - '@tiptap/pm': 3.27.0 + '@tiptap/core': 3.27.1(@tiptap/pm@3.27.1) + '@tiptap/pm': 3.27.1 optional: true - '@tiptap/extension-bullet-list@3.27.0(@tiptap/extension-list@3.27.0(@tiptap/core@3.27.0(@tiptap/pm@3.27.0))(@tiptap/pm@3.27.0))': + '@tiptap/extension-bullet-list@3.27.1(@tiptap/extension-list@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1))': dependencies: - '@tiptap/extension-list': 3.27.0(@tiptap/core@3.27.0(@tiptap/pm@3.27.0))(@tiptap/pm@3.27.0) + '@tiptap/extension-list': 3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1) - '@tiptap/extension-code-block@3.27.0(@tiptap/core@3.27.0(@tiptap/pm@3.27.0))(@tiptap/pm@3.27.0)': + '@tiptap/extension-code-block@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1)': dependencies: - '@tiptap/core': 3.27.0(@tiptap/pm@3.27.0) - '@tiptap/pm': 3.27.0 + '@tiptap/core': 3.27.1(@tiptap/pm@3.27.1) + '@tiptap/pm': 3.27.1 - '@tiptap/extension-code@3.27.0(@tiptap/core@3.27.0(@tiptap/pm@3.27.0))': + '@tiptap/extension-code@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))': dependencies: - '@tiptap/core': 3.27.0(@tiptap/pm@3.27.0) + '@tiptap/core': 3.27.1(@tiptap/pm@3.27.1) - '@tiptap/extension-document@3.27.0(@tiptap/core@3.27.0(@tiptap/pm@3.27.0))': + '@tiptap/extension-document@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))': dependencies: - '@tiptap/core': 3.27.0(@tiptap/pm@3.27.0) + '@tiptap/core': 3.27.1(@tiptap/pm@3.27.1) - '@tiptap/extension-dropcursor@3.27.0(@tiptap/extensions@3.27.0(@tiptap/core@3.27.0(@tiptap/pm@3.27.0))(@tiptap/pm@3.27.0))': + '@tiptap/extension-dropcursor@3.27.1(@tiptap/extensions@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1))': dependencies: - '@tiptap/extensions': 3.27.0(@tiptap/core@3.27.0(@tiptap/pm@3.27.0))(@tiptap/pm@3.27.0) + '@tiptap/extensions': 3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1) - '@tiptap/extension-floating-menu@3.27.0(@floating-ui/dom@1.7.6)(@tiptap/core@3.27.0(@tiptap/pm@3.27.0))(@tiptap/pm@3.27.0)': + '@tiptap/extension-floating-menu@3.27.1(@floating-ui/dom@1.7.6)(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1)': dependencies: '@floating-ui/dom': 1.7.6 - '@tiptap/core': 3.27.0(@tiptap/pm@3.27.0) - '@tiptap/pm': 3.27.0 + '@tiptap/core': 3.27.1(@tiptap/pm@3.27.1) + '@tiptap/pm': 3.27.1 optional: true - '@tiptap/extension-gapcursor@3.27.0(@tiptap/extensions@3.27.0(@tiptap/core@3.27.0(@tiptap/pm@3.27.0))(@tiptap/pm@3.27.0))': + '@tiptap/extension-gapcursor@3.27.1(@tiptap/extensions@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1))': dependencies: - '@tiptap/extensions': 3.27.0(@tiptap/core@3.27.0(@tiptap/pm@3.27.0))(@tiptap/pm@3.27.0) + '@tiptap/extensions': 3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1) - '@tiptap/extension-hard-break@3.27.0(@tiptap/core@3.27.0(@tiptap/pm@3.27.0))': + '@tiptap/extension-hard-break@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))': dependencies: - '@tiptap/core': 3.27.0(@tiptap/pm@3.27.0) + '@tiptap/core': 3.27.1(@tiptap/pm@3.27.1) - '@tiptap/extension-heading@3.27.0(@tiptap/core@3.27.0(@tiptap/pm@3.27.0))': + '@tiptap/extension-heading@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))': dependencies: - '@tiptap/core': 3.27.0(@tiptap/pm@3.27.0) + '@tiptap/core': 3.27.1(@tiptap/pm@3.27.1) - '@tiptap/extension-horizontal-rule@3.27.0(@tiptap/core@3.27.0(@tiptap/pm@3.27.0))(@tiptap/pm@3.27.0)': + '@tiptap/extension-horizontal-rule@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1)': dependencies: - '@tiptap/core': 3.27.0(@tiptap/pm@3.27.0) - '@tiptap/pm': 3.27.0 + '@tiptap/core': 3.27.1(@tiptap/pm@3.27.1) + '@tiptap/pm': 3.27.1 - '@tiptap/extension-italic@3.27.0(@tiptap/core@3.27.0(@tiptap/pm@3.27.0))': + '@tiptap/extension-image@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))': dependencies: - '@tiptap/core': 3.27.0(@tiptap/pm@3.27.0) + '@tiptap/core': 3.27.1(@tiptap/pm@3.27.1) - '@tiptap/extension-link@3.27.0(@tiptap/core@3.27.0(@tiptap/pm@3.27.0))(@tiptap/pm@3.27.0)': + '@tiptap/extension-italic@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))': dependencies: - '@tiptap/core': 3.27.0(@tiptap/pm@3.27.0) - '@tiptap/pm': 3.27.0 + '@tiptap/core': 3.27.1(@tiptap/pm@3.27.1) + + '@tiptap/extension-link@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1)': + dependencies: + '@tiptap/core': 3.27.1(@tiptap/pm@3.27.1) + '@tiptap/pm': 3.27.1 linkifyjs: 4.3.3 - '@tiptap/extension-list-item@3.27.0(@tiptap/extension-list@3.27.0(@tiptap/core@3.27.0(@tiptap/pm@3.27.0))(@tiptap/pm@3.27.0))': + '@tiptap/extension-list-item@3.27.1(@tiptap/extension-list@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1))': dependencies: - '@tiptap/extension-list': 3.27.0(@tiptap/core@3.27.0(@tiptap/pm@3.27.0))(@tiptap/pm@3.27.0) + '@tiptap/extension-list': 3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1) - '@tiptap/extension-list-keymap@3.27.0(@tiptap/extension-list@3.27.0(@tiptap/core@3.27.0(@tiptap/pm@3.27.0))(@tiptap/pm@3.27.0))': + '@tiptap/extension-list-keymap@3.27.1(@tiptap/extension-list@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1))': dependencies: - '@tiptap/extension-list': 3.27.0(@tiptap/core@3.27.0(@tiptap/pm@3.27.0))(@tiptap/pm@3.27.0) + '@tiptap/extension-list': 3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1) - '@tiptap/extension-list@3.27.0(@tiptap/core@3.27.0(@tiptap/pm@3.27.0))(@tiptap/pm@3.27.0)': + '@tiptap/extension-list@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1)': dependencies: - '@tiptap/core': 3.27.0(@tiptap/pm@3.27.0) - '@tiptap/pm': 3.27.0 + '@tiptap/core': 3.27.1(@tiptap/pm@3.27.1) + '@tiptap/pm': 3.27.1 - '@tiptap/extension-ordered-list@3.27.0(@tiptap/extension-list@3.27.0(@tiptap/core@3.27.0(@tiptap/pm@3.27.0))(@tiptap/pm@3.27.0))': + '@tiptap/extension-ordered-list@3.27.1(@tiptap/extension-list@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1))': dependencies: - '@tiptap/extension-list': 3.27.0(@tiptap/core@3.27.0(@tiptap/pm@3.27.0))(@tiptap/pm@3.27.0) + '@tiptap/extension-list': 3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1) - '@tiptap/extension-paragraph@3.27.0(@tiptap/core@3.27.0(@tiptap/pm@3.27.0))': + '@tiptap/extension-paragraph@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))': dependencies: - '@tiptap/core': 3.27.0(@tiptap/pm@3.27.0) + '@tiptap/core': 3.27.1(@tiptap/pm@3.27.1) - '@tiptap/extension-strike@3.27.0(@tiptap/core@3.27.0(@tiptap/pm@3.27.0))': + '@tiptap/extension-strike@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))': dependencies: - '@tiptap/core': 3.27.0(@tiptap/pm@3.27.0) + '@tiptap/core': 3.27.1(@tiptap/pm@3.27.1) - '@tiptap/extension-text@3.27.0(@tiptap/core@3.27.0(@tiptap/pm@3.27.0))': + '@tiptap/extension-table@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1)': dependencies: - '@tiptap/core': 3.27.0(@tiptap/pm@3.27.0) + '@tiptap/core': 3.27.1(@tiptap/pm@3.27.1) + '@tiptap/pm': 3.27.1 - '@tiptap/extension-underline@3.27.0(@tiptap/core@3.27.0(@tiptap/pm@3.27.0))': + '@tiptap/extension-text@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))': dependencies: - '@tiptap/core': 3.27.0(@tiptap/pm@3.27.0) + '@tiptap/core': 3.27.1(@tiptap/pm@3.27.1) - '@tiptap/extensions@3.27.0(@tiptap/core@3.27.0(@tiptap/pm@3.27.0))(@tiptap/pm@3.27.0)': + '@tiptap/extension-underline@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))': dependencies: - '@tiptap/core': 3.27.0(@tiptap/pm@3.27.0) - '@tiptap/pm': 3.27.0 + '@tiptap/core': 3.27.1(@tiptap/pm@3.27.1) - '@tiptap/pm@3.27.0': + '@tiptap/extensions@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1)': + dependencies: + '@tiptap/core': 3.27.1(@tiptap/pm@3.27.1) + '@tiptap/pm': 3.27.1 + + '@tiptap/markdown@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1)': + dependencies: + '@tiptap/core': 3.27.1(@tiptap/pm@3.27.1) + '@tiptap/pm': 3.27.1 + marked: 17.0.6 + + '@tiptap/pm@3.27.1': dependencies: prosemirror-changeset: 2.4.1 prosemirror-commands: 1.7.1 @@ -7920,10 +7700,10 @@ snapshots: prosemirror-transform: 1.12.0 prosemirror-view: 1.41.9 - '@tiptap/react@3.27.0(@floating-ui/dom@1.7.6)(@tiptap/core@3.27.0(@tiptap/pm@3.27.0))(@tiptap/pm@3.27.0)(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + '@tiptap/react@3.27.1(@floating-ui/dom@1.7.6)(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1)(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@tiptap/core': 3.27.0(@tiptap/pm@3.27.0) - '@tiptap/pm': 3.27.0 + '@tiptap/core': 3.27.1(@tiptap/pm@3.27.1) + '@tiptap/pm': 3.27.1 '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) '@types/use-sync-external-store': 0.0.6 @@ -7932,37 +7712,37 @@ snapshots: react-dom: 19.2.7(react@19.2.7) use-sync-external-store: 1.6.0(react@19.2.7) optionalDependencies: - '@tiptap/extension-bubble-menu': 3.27.0(@tiptap/core@3.27.0(@tiptap/pm@3.27.0))(@tiptap/pm@3.27.0) - '@tiptap/extension-floating-menu': 3.27.0(@floating-ui/dom@1.7.6)(@tiptap/core@3.27.0(@tiptap/pm@3.27.0))(@tiptap/pm@3.27.0) + '@tiptap/extension-bubble-menu': 3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1) + '@tiptap/extension-floating-menu': 3.27.1(@floating-ui/dom@1.7.6)(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1) transitivePeerDependencies: - '@floating-ui/dom' - '@tiptap/starter-kit@3.27.0': + '@tiptap/starter-kit@3.27.1': dependencies: - '@tiptap/core': 3.27.0(@tiptap/pm@3.27.0) - '@tiptap/extension-blockquote': 3.27.0(@tiptap/core@3.27.0(@tiptap/pm@3.27.0)) - '@tiptap/extension-bold': 3.27.0(@tiptap/core@3.27.0(@tiptap/pm@3.27.0)) - '@tiptap/extension-bullet-list': 3.27.0(@tiptap/extension-list@3.27.0(@tiptap/core@3.27.0(@tiptap/pm@3.27.0))(@tiptap/pm@3.27.0)) - '@tiptap/extension-code': 3.27.0(@tiptap/core@3.27.0(@tiptap/pm@3.27.0)) - '@tiptap/extension-code-block': 3.27.0(@tiptap/core@3.27.0(@tiptap/pm@3.27.0))(@tiptap/pm@3.27.0) - '@tiptap/extension-document': 3.27.0(@tiptap/core@3.27.0(@tiptap/pm@3.27.0)) - '@tiptap/extension-dropcursor': 3.27.0(@tiptap/extensions@3.27.0(@tiptap/core@3.27.0(@tiptap/pm@3.27.0))(@tiptap/pm@3.27.0)) - '@tiptap/extension-gapcursor': 3.27.0(@tiptap/extensions@3.27.0(@tiptap/core@3.27.0(@tiptap/pm@3.27.0))(@tiptap/pm@3.27.0)) - '@tiptap/extension-hard-break': 3.27.0(@tiptap/core@3.27.0(@tiptap/pm@3.27.0)) - '@tiptap/extension-heading': 3.27.0(@tiptap/core@3.27.0(@tiptap/pm@3.27.0)) - '@tiptap/extension-horizontal-rule': 3.27.0(@tiptap/core@3.27.0(@tiptap/pm@3.27.0))(@tiptap/pm@3.27.0) - '@tiptap/extension-italic': 3.27.0(@tiptap/core@3.27.0(@tiptap/pm@3.27.0)) - '@tiptap/extension-link': 3.27.0(@tiptap/core@3.27.0(@tiptap/pm@3.27.0))(@tiptap/pm@3.27.0) - '@tiptap/extension-list': 3.27.0(@tiptap/core@3.27.0(@tiptap/pm@3.27.0))(@tiptap/pm@3.27.0) - '@tiptap/extension-list-item': 3.27.0(@tiptap/extension-list@3.27.0(@tiptap/core@3.27.0(@tiptap/pm@3.27.0))(@tiptap/pm@3.27.0)) - '@tiptap/extension-list-keymap': 3.27.0(@tiptap/extension-list@3.27.0(@tiptap/core@3.27.0(@tiptap/pm@3.27.0))(@tiptap/pm@3.27.0)) - '@tiptap/extension-ordered-list': 3.27.0(@tiptap/extension-list@3.27.0(@tiptap/core@3.27.0(@tiptap/pm@3.27.0))(@tiptap/pm@3.27.0)) - '@tiptap/extension-paragraph': 3.27.0(@tiptap/core@3.27.0(@tiptap/pm@3.27.0)) - '@tiptap/extension-strike': 3.27.0(@tiptap/core@3.27.0(@tiptap/pm@3.27.0)) - '@tiptap/extension-text': 3.27.0(@tiptap/core@3.27.0(@tiptap/pm@3.27.0)) - '@tiptap/extension-underline': 3.27.0(@tiptap/core@3.27.0(@tiptap/pm@3.27.0)) - '@tiptap/extensions': 3.27.0(@tiptap/core@3.27.0(@tiptap/pm@3.27.0))(@tiptap/pm@3.27.0) - '@tiptap/pm': 3.27.0 + '@tiptap/core': 3.27.1(@tiptap/pm@3.27.1) + '@tiptap/extension-blockquote': 3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1)) + '@tiptap/extension-bold': 3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1)) + '@tiptap/extension-bullet-list': 3.27.1(@tiptap/extension-list@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1)) + '@tiptap/extension-code': 3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1)) + '@tiptap/extension-code-block': 3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1) + '@tiptap/extension-document': 3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1)) + '@tiptap/extension-dropcursor': 3.27.1(@tiptap/extensions@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1)) + '@tiptap/extension-gapcursor': 3.27.1(@tiptap/extensions@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1)) + '@tiptap/extension-hard-break': 3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1)) + '@tiptap/extension-heading': 3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1)) + '@tiptap/extension-horizontal-rule': 3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1) + '@tiptap/extension-italic': 3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1)) + '@tiptap/extension-link': 3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1) + '@tiptap/extension-list': 3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1) + '@tiptap/extension-list-item': 3.27.1(@tiptap/extension-list@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1)) + '@tiptap/extension-list-keymap': 3.27.1(@tiptap/extension-list@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1)) + '@tiptap/extension-ordered-list': 3.27.1(@tiptap/extension-list@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1)) + '@tiptap/extension-paragraph': 3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1)) + '@tiptap/extension-strike': 3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1)) + '@tiptap/extension-text': 3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1)) + '@tiptap/extension-underline': 3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1)) + '@tiptap/extensions': 3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1) + '@tiptap/pm': 3.27.1 '@tybys/wasm-util@0.10.2': dependencies: @@ -8018,30 +7798,12 @@ snapshots: '@types/json-schema@7.0.15': {} - '@types/linkify-it@3.0.5': {} - - '@types/linkify-it@5.0.0': {} - '@types/lodash@4.17.24': {} - '@types/markdown-it@13.0.9': - dependencies: - '@types/linkify-it': 3.0.5 - '@types/mdurl': 1.0.5 - - '@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@1.0.5': {} - - '@types/mdurl@2.0.0': {} - '@types/ms@2.1.0': {} '@types/node@24.13.2': @@ -8159,33 +7921,6 @@ snapshots: '@typescript-eslint/types': 8.61.1 eslint-visitor-keys: 5.0.1 - '@uiw/codemirror-extensions-basic-setup@4.25.10(@codemirror/autocomplete@6.20.3)(@codemirror/commands@6.10.4)(@codemirror/language@6.12.4)(@codemirror/lint@6.9.7)(@codemirror/search@6.7.1)(@codemirror/state@6.7.0)(@codemirror/view@6.43.3)': - dependencies: - '@codemirror/autocomplete': 6.20.3 - '@codemirror/commands': 6.10.4 - '@codemirror/language': 6.12.4 - '@codemirror/lint': 6.9.7 - '@codemirror/search': 6.7.1 - '@codemirror/state': 6.7.0 - '@codemirror/view': 6.43.3 - - '@uiw/react-codemirror@4.25.10(@babel/runtime@7.29.7)(@codemirror/autocomplete@6.20.3)(@codemirror/language@6.12.4)(@codemirror/lint@6.9.7)(@codemirror/search@6.7.1)(@codemirror/state@6.7.0)(@codemirror/theme-one-dark@6.1.3)(@codemirror/view@6.43.3)(codemirror@6.0.2)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': - dependencies: - '@babel/runtime': 7.29.7 - '@codemirror/commands': 6.10.4 - '@codemirror/state': 6.7.0 - '@codemirror/theme-one-dark': 6.1.3 - '@codemirror/view': 6.43.3 - '@uiw/codemirror-extensions-basic-setup': 4.25.10(@codemirror/autocomplete@6.20.3)(@codemirror/commands@6.10.4)(@codemirror/language@6.12.4)(@codemirror/lint@6.9.7)(@codemirror/search@6.7.1)(@codemirror/state@6.7.0)(@codemirror/view@6.43.3) - codemirror: 6.0.2 - react: 19.2.7 - react-dom: 19.2.7(react@19.2.7) - transitivePeerDependencies: - - '@codemirror/autocomplete' - - '@codemirror/language' - - '@codemirror/lint' - - '@codemirror/search' - '@ungap/structured-clone@1.3.1': {} '@vitejs/plugin-react-swc@4.3.1(@swc/helpers@0.5.23)(vite@8.0.16(@types/node@24.13.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(tsx@4.22.4)(yaml@2.9.0))': @@ -8604,16 +8339,6 @@ snapshots: - '@types/react' - '@types/react-dom' - codemirror@6.0.2: - dependencies: - '@codemirror/autocomplete': 6.20.3 - '@codemirror/commands': 6.10.4 - '@codemirror/language': 6.12.4 - '@codemirror/lint': 6.9.7 - '@codemirror/search': 6.7.1 - '@codemirror/state': 6.7.0 - '@codemirror/view': 6.43.3 - color-convert@2.0.1: dependencies: color-name: 1.1.4 @@ -8678,8 +8403,6 @@ snapshots: optionalDependencies: typescript: 6.0.3 - crelt@1.0.6: {} - cross-inspect@1.0.1: dependencies: tslib: 2.8.1 @@ -8890,8 +8613,6 @@ snapshots: entities@2.2.0: {} - entities@4.5.0: {} - entities@8.0.0: {} env-paths@2.2.1: {} @@ -9899,10 +9620,6 @@ snapshots: lines-and-columns@1.2.4: {} - linkify-it@5.0.1: - dependencies: - uc.micro: 2.1.0 - linkifyjs@4.3.3: {} listr2@10.2.1: @@ -9980,19 +9697,10 @@ snapshots: map-cache@0.2.2: {} - markdown-it-task-lists@2.1.1: {} - - markdown-it@14.2.0: - dependencies: - argparse: 2.0.1 - entities: 4.5.0 - linkify-it: 5.0.1 - mdurl: 2.0.0 - punycode.js: 2.3.1 - uc.micro: 2.1.0 - markdown-table@3.0.4: {} + marked@17.0.6: {} + marked@18.0.5: {} math-intrinsics@1.1.0: {} @@ -10152,8 +9860,6 @@ snapshots: mdn-data@2.27.1: {} - mdurl@2.0.0: {} - media-engine@1.0.3: {} memoize-one@6.0.0: {} @@ -10671,12 +10377,6 @@ snapshots: prosemirror-state: 1.4.4 w3c-keyname: 2.2.8 - prosemirror-markdown@1.13.4: - dependencies: - '@types/markdown-it': 14.1.2 - markdown-it: 14.2.0 - prosemirror-model: 1.25.9 - prosemirror-model@1.25.9: dependencies: orderedmap: 2.1.1 @@ -10713,8 +10413,6 @@ snapshots: proxy-from-env@2.1.0: {} - punycode.js@2.3.1: {} - punycode@2.3.1: {} queue-microtask@1.2.3: {} @@ -11236,8 +10934,6 @@ snapshots: strip-json-comments@3.1.1: {} - style-mod@4.1.3: {} - style-to-js@1.1.21: dependencies: style-to-object: 1.0.14 @@ -11296,14 +10992,6 @@ snapshots: tinyrainbow@3.1.0: {} - tiptap-markdown@0.9.0(@tiptap/core@3.27.0(@tiptap/pm@3.27.0)): - dependencies: - '@tiptap/core': 3.27.0(@tiptap/pm@3.27.0) - '@types/markdown-it': 13.0.9 - markdown-it: 14.2.0 - markdown-it-task-lists: 2.1.1 - prosemirror-markdown: 1.13.4 - title-case@3.0.3: dependencies: tslib: 2.8.1 @@ -11389,8 +11077,6 @@ snapshots: typescript@6.0.3: {} - uc.micro@2.1.0: {} - unbox-primitive@1.1.0: dependencies: call-bound: 1.0.4 diff --git a/frontend/src/components/shared/code-editor.tsx b/frontend/src/components/shared/code-editor.tsx deleted file mode 100644 index bd281d63..00000000 --- a/frontend/src/components/shared/code-editor.tsx +++ /dev/null @@ -1,52 +0,0 @@ -import { markdown } from '@codemirror/lang-markdown'; -import CodeMirror, { EditorView, type ReactCodeMirrorRef } from '@uiw/react-codemirror'; -import { type Ref, useMemo } from 'react'; - -import { useTheme } from '@/hooks/use-theme'; -import { cn } from '@/lib/utils'; - -export type { ReactCodeMirrorRef }; - -export interface CodeEditorProps { - className?: string; - disabled?: boolean; - onBlur?: () => void; - onChange: (value: string) => void; - placeholder?: string; - ref?: Ref; - value: string; -} - -const extensions = [markdown(), EditorView.lineWrapping]; - -/** - * Byte-faithful text editor: CodeMirror edits the raw document string verbatim, with no - * parse/serialize round-trip. Don't swap in a markdown/rich editor — it would corrupt - * Go-template tables, ``, and significant whitespace on round-trip. - */ -export function CodeEditor({ className, disabled, onBlur, onChange, placeholder, ref, value }: CodeEditorProps) { - const { theme } = useTheme(); - const isDark = useMemo( - () => theme === 'dark' || (theme === 'system' && window.matchMedia('(prefers-color-scheme: dark)').matches), - [theme], - ); - - return ( - - ); -} diff --git a/frontend/src/components/shared/editor-highlight-regex.test.ts b/frontend/src/components/shared/editor-highlight-regex.test.ts new file mode 100644 index 00000000..73ffff30 --- /dev/null +++ b/frontend/src/components/shared/editor-highlight-regex.test.ts @@ -0,0 +1,57 @@ +import { describe, expect, it } from 'vitest'; + +import { TAG_RE } from './editor-tag-highlight'; +import { VARIABLE_RE } from './editor-variable-highlight'; + +// Decorations highlight per text node via `node.text.matchAll(RE)`; assert the RE itself matches the +// right spans and — critically — does NOT false-positive on prose that merely contains < or {{. +const matches = (re: RegExp, text: string) => [...text.matchAll(re)].map((m) => m[0]); + +describe('VARIABLE_RE — {{ go-template actions }}', () => { + it.each([ + ['{{.Var}}', ['{{.Var}}']], + ['{{- if .X}}', ['{{- if .X}}']], + ['{{end}}', ['{{end}}']], + ['{{.X | upper}}', ['{{.X | upper}}']], + ['{{printf "%d" .N}}', ['{{printf "%d" .N}}']], + ['{{- .B -}}', ['{{- .B -}}']], + ['a {{.X}} b {{.Y}} c', ['{{.X}}', '{{.Y}}']], + ])('matches %s', (input, expected) => { + expect(matches(VARIABLE_RE, input)).toEqual(expected); + }); + + it.each(['{single brace}', '${shell}', 'open {{ no close', 'a }} before {{ open'])( + 'does NOT match %s', + (input) => { + expect(matches(VARIABLE_RE, input)).toEqual([]); + }, + ); +}); + +describe('TAG_RE — ', () => { + it.each([ + ['', ['']], + ['', ['']], + ['', ['']], + ['', ['']], + ['', ['']], + ])('matches %s', (input, expected) => { + expect(matches(TAG_RE, input)).toEqual(expected); + }); + + it.each([ + 'count a < b here', // less-than, space after < + 'loop x<5 times', // digit after < + 'rating <3', // not a tag + 'empty <>', + 'spaced < tag>', + '', // markdown autolink — must NOT be a tag + '', // html comment + ])('does NOT match %s', (input) => { + expect(matches(TAG_RE, input)).toEqual([]); + }); + + it('documented false-positive: a single-letter in prose is highlighted (harmless, view-only)', () => { + expect(matches(TAG_RE, 'if a then')).toEqual(['']); + }); +}); diff --git a/frontend/src/components/shared/editor-markdown.ts b/frontend/src/components/shared/editor-markdown.ts new file mode 100644 index 00000000..dba166b9 --- /dev/null +++ b/frontend/src/components/shared/editor-markdown.ts @@ -0,0 +1,82 @@ +import type { JSONContent } from '@tiptap/core'; + +import { Extension } from '@tiptap/core'; +import { renderTableToMarkdown, Table } from '@tiptap/extension-table'; +import { Markdown } from '@tiptap/markdown'; +import { Marked } from 'marked'; + +// @tiptap/markdown parses with `marked`, which — unlike markdown-it's `html: false` — always tries to +// interpret `<...>` as HTML. Our content uses literal XML-ish tags (``, ``) +// that must survive verbatim; marked silently swallows the ones whose names match real HTML elements +// (``, `
`, …). Neutralising marked's block (`html`) and inline (`tag`) HTML tokenizers makes +// every `<...>` fall through to plain text, recreating markdown-it's `html: false`. +const createFaithfulMarked = () => { + const instance = new Marked(); + + instance.use({ tokenizer: { html: () => undefined, tag: () => undefined } }); + + // A PRIVATE instance — mutating the shared global `marked` would also affect report-pdf. + return instance; +}; + +// marked-side parsing keeps tags literal; this is the serialize-side counterpart. @tiptap/markdown's +// MarkdownManager.encodeTextForMarkdown HTML-entity-encodes text (`<` → `<`) and backslash-escapes +// ``` ` * _ [ ] ~ ``` — both corrupt our content (tags become entities, `[1-1000]`/`*.php`/`snake_case` +// gain stray backslashes). Text serialization is hard-coded in the manager (no per-extension hook), so we +// retune that one method: drop the entity-encoding entirely, and backslash-escape only the chars that +// would otherwise re-parse as inline syntax (`` ` ``, `~`, `\`). +const faithfulEscape = (text: string): string => text.replace(/([\\`~])/g, '\\$1'); + +type ManagerWithEncode = { + codeTypes: Set; + encodeTextForMarkdown: (text: string, node: MarkdownNode, parentNode?: MarkdownNode) => string; +}; +type MarkdownNode = { marks?: (string | { type: string })[]; text?: string; type?: string }; + +export const FaithfulMarkdownText = Extension.create({ + name: 'faithfulMarkdownText', + onBeforeCreate() { + const manager = this.editor.markdown as unknown as ManagerWithEncode | undefined; + + if (!manager) { + return; + } + + manager.encodeTextForMarkdown = function encodeTextForMarkdown(text, node, parentNode) { + const isInsideCode = + (parentNode?.type != null && this.codeTypes.has(parentNode.type)) || + (node.marks ?? []).some((mark) => this.codeTypes.has(typeof mark === 'string' ? mark : mark.type)); + + return isInsideCode ? text : faithfulEscape(text); + }; + }, + // Lower priority than the Markdown extension so this runs AFTER its onBeforeCreate has created the + // manager and assigned editor.markdown. onBeforeCreate (not onCreate) because it is synchronous — + // a headless editor's onCreate fires after construction, too late for the first getMarkdown(). + priority: 50, +}); + +// @tiptap/extension-table's renderTableToMarkdown is alignment-aware but never escapes pipes, so a literal +// `|` in a cell (even inside inline code) re-parses as a column delimiter on the next save and drops cells +// (tiptap PR #7884, still open). renderTableToMarkdown only emits cell content via h.renderChildren, so +// wrapping that one call to escape pipes is enough — and it stays on the official, alignment-aware renderer. +type RenderHelpers = { renderChildren: (nodes: JSONContent | JSONContent[], separator?: string) => string }; + +export const MarkdownTable = Table.extend({ + renderMarkdown(node: JSONContent, helpers: RenderHelpers) { + const pipeEscaping: RenderHelpers = { + ...helpers, + renderChildren: (nodes, separator) => helpers.renderChildren(nodes, separator).replace(/\|/g, '\\|'), + }; + + return renderTableToMarkdown(node, pipeEscaping as never); + }, +}); + +// The official Markdown extension wired with our faithful `marked`, plus the serialize-side retune. +// The `as never` bridges a transitive version skew: @tiptap/markdown's `marked` option is typed against +// its own marked@17 while our direct dep is marked@18; the instance is runtime-compatible (corpus-verified). +export const createMarkdownLayer = () => [ + Markdown.configure({ marked: createFaithfulMarked() as never }), + FaithfulMarkdownText, +]; diff --git a/frontend/src/components/shared/editor-tag-highlight.ts b/frontend/src/components/shared/editor-tag-highlight.ts new file mode 100644 index 00000000..b9ce2864 --- /dev/null +++ b/frontend/src/components/shared/editor-tag-highlight.ts @@ -0,0 +1,53 @@ +import type { Node as PMNode } from '@tiptap/pm/model'; + +import { Extension } from '@tiptap/core'; +import { Plugin, PluginKey } from '@tiptap/pm/state'; +import { Decoration, DecorationSet } from '@tiptap/pm/view'; + +// Highlights xml-like tags (, , ) +// as VIEW-ONLY decorations — same byte-neutral approach as editor-variable-highlight. A node would +// have to tell a structural tag from a tag NAME quoted inline as documentation (the prompts do both, +// in prose AND code), which can't be done reliably and risks corrupting the template on save; a +// decoration colours any tag-shaped text wherever it appears and never touches the document. +const tagHighlightKey = new PluginKey('tagHighlight'); + +// `[^<>]` for the attribute span keeps the scan linear and stops at the next angle bracket. +export const TAG_RE = /<\/?[a-zA-Z][\w-]*(?:\s[^<>]*)?\/?>/g; + +const buildDecorations = (doc: PMNode): DecorationSet => { + const decorations: Decoration[] = []; + + doc.descendants((node, pos) => { + if (!node.isText || !node.text) { + return; + } + + for (const match of node.text.matchAll(TAG_RE)) { + const from = pos + (match.index ?? 0); + + decorations.push(Decoration.inline(from, from + match[0].length, { class: 'template-tag' })); + } + }); + + return DecorationSet.create(doc, decorations); +}; + +export const TagHighlight = Extension.create({ + addProseMirrorPlugins() { + return [ + new Plugin({ + key: tagHighlightKey, + props: { + decorations(state) { + return tagHighlightKey.getState(state); + }, + }, + state: { + apply: (tr, old: DecorationSet) => (tr.docChanged ? buildDecorations(tr.doc) : old), + init: (_config, { doc }) => buildDecorations(doc), + }, + }), + ]; + }, + name: 'tagHighlight', +}); diff --git a/frontend/src/components/shared/editor-variable-highlight.ts b/frontend/src/components/shared/editor-variable-highlight.ts new file mode 100644 index 00000000..98b4f334 --- /dev/null +++ b/frontend/src/components/shared/editor-variable-highlight.ts @@ -0,0 +1,55 @@ +import type { Node as PMNode } from '@tiptap/pm/model'; + +import { Extension } from '@tiptap/core'; +import { Plugin, PluginKey } from '@tiptap/pm/state'; +import { Decoration, DecorationSet } from '@tiptap/pm/view'; + +// Highlights Go-template actions ({{.Var}}, {{- if .X}}, {{end}}, {{.A | upper}}, …) as +// VIEW-ONLY decorations. Decorations never touch the document, so getMarkdown() stays +// byte-identical — and {{ }} already round-trips verbatim (prosemirror-markdown esc() +// escapes only ` * \ ~ [ ] _, tiptap-markdown escapeHTML only < >; neither touches { }). +// A node/mark would gain nothing here and would break the variables side-panel, which +// inserts {{.X}} as plain text and cycles uses by regex over the serialized string. +const variableHighlightKey = new PluginKey('variableHighlight'); + +// `[^{}]` keeps the scan linear (no catastrophic backtracking); Go actions never nest braces. +export const VARIABLE_RE = /\{\{[^{}]*\}\}/g; + +// Scan per text node — NOT over doc.textContent — or inline positions misalign across blocks. +const buildDecorations = (doc: PMNode): DecorationSet => { + const decorations: Decoration[] = []; + + doc.descendants((node, pos) => { + if (!node.isText || !node.text) { + return; + } + + for (const match of node.text.matchAll(VARIABLE_RE)) { + const from = pos + (match.index ?? 0); + + decorations.push(Decoration.inline(from, from + match[0].length, { class: 'template-variable' })); + } + }); + + return DecorationSet.create(doc, decorations); +}; + +export const VariableHighlight = Extension.create({ + addProseMirrorPlugins() { + return [ + new Plugin({ + key: variableHighlightKey, + props: { + decorations(state) { + return variableHighlightKey.getState(state); + }, + }, + state: { + apply: (tr, old: DecorationSet) => (tr.docChanged ? buildDecorations(tr.doc) : old), + init: (_config, { doc }) => buildDecorations(doc), + }, + }), + ]; + }, + name: 'variableHighlight', +}); diff --git a/frontend/src/components/shared/markdown-editor-corpus.test.ts b/frontend/src/components/shared/markdown-editor-corpus.test.ts new file mode 100644 index 00000000..af7989a8 --- /dev/null +++ b/frontend/src/components/shared/markdown-editor-corpus.test.ts @@ -0,0 +1,55 @@ +import { Editor } from '@tiptap/core'; +import { readdirSync, readFileSync } from 'node:fs'; +import { join } from 'node:path'; +import { beforeAll, describe, expect, it } from 'vitest'; + +import { createMarkdownExtensions } from './markdown-editor-extensions'; + +// The editor's Placeholder extension tracks the viewport via document.elementFromPoint + +// Range.getClientRects, which jsdom lacks — stub them or every editor mount throws. +beforeAll(() => { + document.elementFromPoint = () => null; + const r = { bottom: 0, height: 0, left: 0, right: 0, toJSON: () => ({}), top: 0, width: 0, x: 0, y: 0 }; + Range.prototype.getBoundingClientRect = () => r as DOMRect; + Range.prototype.getClientRects = () => + ({ item: () => null, length: 0, [Symbol.iterator]: [][Symbol.iterator] }) as unknown as DOMRectList; +}); + +// Node-fs corpus test: reads the real backend prompt templates (XML-tag-heavy Go templates) and asserts +// the @tiptap/markdown round-trip preserves their CONTENT on EVERY one. Excluded from the app `tsc` build +// (uses node APIs); validated at runtime by vitest. +const roundTrip = (content: string): string => { + const editor = new Editor({ content, contentType: 'markdown', extensions: createMarkdownExtensions() }); + const out = editor.getMarkdown(); + editor.destroy(); + + return out; +}; + +const variables = (s: string) => s.match(/\{\{[^{}]*\}\}/g) ?? []; +const words = (s: string) => s.match(/[\p{L}\p{N}]+/gu) ?? []; + +describe('corpus — every real prompt .tmpl survives the round-trip with no content loss', () => { + const dir = join(__dirname, '..', '..', '..', '..', 'backend', 'pkg', 'templates', 'prompts'); + const files = readdirSync(dir).filter((file) => file.endsWith('.tmpl')); + + for (const file of files) { + it(file + ': tags literal, {{ }} preserved, no word dropped, converges', () => { + const src = readFileSync(join(dir, file), 'utf8'); + const save1 = roundTrip(src); + const save2 = roundTrip(save1); + + // tags stay literal — no HTML-entity escaping introduced. + expect(save1).not.toContain('<'); + expect(save1).not.toContain('>'); + // every {{ }} action survives (set + order). + expect(variables(save1)).toEqual(variables(src)); + // no source word is dropped (cosmetic whitespace reformatting aside). + const after = new Set(words(save2)); + const lost = [...new Set(words(src))].filter((w) => !after.has(w)); + expect(lost).toEqual([]); + // converges — the canonical form is stable on resave. + expect(roundTrip(save2)).toBe(save2); + }); + } +}); diff --git a/frontend/src/components/shared/markdown-editor-extensions.test.ts b/frontend/src/components/shared/markdown-editor-extensions.test.ts new file mode 100644 index 00000000..07d719f1 --- /dev/null +++ b/frontend/src/components/shared/markdown-editor-extensions.test.ts @@ -0,0 +1,127 @@ +import { Editor } from '@tiptap/core'; +import { beforeAll, describe, expect, it } from 'vitest'; + +import { createMarkdownExtensions } from './markdown-editor-extensions'; + +beforeAll(() => { + document.elementFromPoint = () => null; + const r = { bottom: 0, height: 0, left: 0, right: 0, toJSON: () => ({}), top: 0, width: 0, x: 0, y: 0 }; + Range.prototype.getBoundingClientRect = () => r as DOMRect; + Range.prototype.getClientRects = () => + ({ item: () => null, length: 0, [Symbol.iterator]: [][Symbol.iterator] }) as unknown as DOMRectList; +}); + +// Runs the EXACT production extension set (createMarkdownExtensions) through the official +// @tiptap/markdown round-trip: markdown string -> marked parse -> ProseMirror doc -> getMarkdown(). +const roundTrip = (content: string): string => { + const editor = new Editor({ content, contentType: 'markdown', extensions: createMarkdownExtensions() }); + const out = editor.getMarkdown(); + editor.destroy(); + + return out; +}; + +const words = (s: string): string[] => s.match(/[\p{L}\p{N}]+/gu) ?? []; +const sameWords = (a: string, b: string) => expect(words(b).sort()).toEqual(words(a).sort()); + +describe('literal survive (marked HTML-tokenizers neutralized + no entity-encode)', () => { + it.each([ + '', + '', + '', + // void HTML elements: marked would SWALLOW these without the neutralized html/tag tokenizers. + '', + '
', + ])('keeps %s verbatim (no <, no swallow)', (tag) => { + const out = roundTrip('lead ' + tag + ' tail'); + + expect(out).toContain(tag); + expect(out).not.toContain('<'); + expect(out).not.toContain('>'); + }); +}); + +describe('Go-template variables survive verbatim', () => { + it.each(['{{.Var}}', '{{- if .X}}', '{{range .Items}}', '{{.X | upper}}', '{{printf "%d" .N}}', '{{- .Both -}}'])( + 'keeps %s', + (v) => { + expect(roundTrip('lead ' + v + ' tail')).toContain(v); + }, + ); +}); + +describe('selective escape — no stray backslashes on literal punctuation', () => { + it.each(['Scan ports [1-1000].', 'run nmap *.php here', 'a snake_case_name and array[i] index'])( + 'leaves %s unescaped', + (s) => { + const out = roundTrip(s); + + expect(out).not.toContain('\\['); + expect(out).not.toContain('\\*'); + expect(out).not.toContain('\\_'); + sameWords(s, out); + }, + ); +}); + +describe('inline marks round-trip', () => { + it.each(['**bold**', '*italic*', '`code span`', '~~strike~~', '[a link](https://example.com)', '**bold `code` end**'])( + 'preserves %s', + (s) => { + expect(roundTrip(s)).toContain(s); + }, + ); +}); + +describe('MarkdownTable — cell pipes escaped + alignment preserved, idempotent', () => { + it.each([ + ['pipe in a code-span cell', '| Op | Meaning |\n| --- | --- |\n| `\\|` | pipe op |\n| `\\|\\|` | or op |', 'pipe op'], + ['pipes inside inline code', '| Name | Payload |\n| --- | --- |\n| chain | `echo \\| base64 \\| sh` |', 'base64'], + ])('cell content survives two saves: %s', (_l, src, marker) => { + const save1 = roundTrip(src); + const save2 = roundTrip(save1); + + expect(save2).toBe(save1); + expect(save2).toContain(marker); + }); + + it('preserves per-column alignment (left :--- / center :---: / right ---:)', () => { + const save1 = roundTrip('| L | C | R |\n| :-- | :-: | --: |\n| a | b | c |'); + + // renderTableToMarkdown emits the alignment colons (dash count padded to column width, min 3). + expect(save1).toContain('| :--- | :---: | ---: |'); + expect(roundTrip(save1)).toBe(save1); + }); +}); + +describe('nesting & sequencing — content preserved and converges (≤2 saves)', () => { + it.each([ + ['bullet + code', '- item one\n- item two:\n\n ```\n code alpha\n ```\n- item three'], + ['ordered + code', '1. first\n2. second:\n\n ```python\n code beta\n ```\n3. third'], + ['nested bullets', '- outer aaa\n - inner bbb\n - deepest ccc'], + ['blockquote + list', '> - quoted alpha\n> - quoted beta'], + ['blockquote + code', '> intro\n>\n> ```\n> quoted code zeta\n> ```'], + ['task list nested', '- [ ] task alpha\n - [x] subtask beta\n- [x] task delta'], + ['heading > list > code', '## Title\n\n- item alpha\n- item beta\n\n```\nstandalone epsilon\n```'], + ['table > list > quote', '| A | B |\n| --- | --- |\n| 1 | 2 |\n\n- item one\n\n> quote alpha'], + ])('%s', (_l, src) => { + const save1 = roundTrip(src); + const save2 = roundTrip(save1); + + // converges (canonicalizes once, then stable) and no word is dropped. + expect(save2).toBe(save1); + sameWords(src, save2); + }); +}); + +// KNOWN BUG (marked parser, not our customizations — vanilla @tiptap/markdown repros it). A code block +// nested in a bullet sublist that is itself inside an ordered list is SILENTLY DROPPED. markdown-it kept +// it. 0 corpus docs hit it. This test pins the CURRENT (buggy) behavior so we notice if an upstream +// @tiptap/markdown release fixes it (then flip it to a passing round-trip + drop this note). +describe('KNOWN UPSTREAM BUG — ordered > bullet > code drops the code block', () => { + it('still loses the deeply-nested code (remove this test once @tiptap/markdown fixes it)', () => { + const out = roundTrip('1. lvl1\n - lvl2\n\n ```\n deepcode\n ```'); + + expect(out).not.toContain('deepcode'); + }); +}); diff --git a/frontend/src/components/shared/markdown-editor-extensions.ts b/frontend/src/components/shared/markdown-editor-extensions.ts new file mode 100644 index 00000000..36a291b4 --- /dev/null +++ b/frontend/src/components/shared/markdown-editor-extensions.ts @@ -0,0 +1,28 @@ +import { Image } from '@tiptap/extension-image'; +import { TaskItem, TaskList } from '@tiptap/extension-list'; +import { TableCell, TableHeader, TableRow } from '@tiptap/extension-table'; +import { Placeholder } from '@tiptap/extensions'; +import StarterKit from '@tiptap/starter-kit'; + +import { createMarkdownLayer, MarkdownTable } from './editor-markdown'; +import { TagHighlight } from './editor-tag-highlight'; +import { VariableHighlight } from './editor-variable-highlight'; + +// Single source of truth for the editor's extension stack — shared by markdown-editor.tsx AND the +// round-trip tests so they can never drift. createMarkdownLayer is the official @tiptap/markdown layer +// tuned for our content (see editor-markdown.ts); VariableHighlight/TagHighlight are view-only decorations +// ({{vars}} / ) that don't affect serialization. +export const createMarkdownExtensions = (placeholder?: string) => [ + StarterKit.configure({ codeBlock: { HTMLAttributes: { class: 'hljs' } } }), + MarkdownTable.configure({ resizable: true }), + TableRow, + TableHeader, + TableCell, + TaskList, + TaskItem.configure({ nested: true }), + Image, + VariableHighlight, + TagHighlight, + Placeholder.configure({ emptyEditorClass: 'is-editor-empty', placeholder }), + ...createMarkdownLayer(), +]; diff --git a/frontend/src/components/shared/markdown-editor.tsx b/frontend/src/components/shared/markdown-editor.tsx index cc6f9450..dcda6d1f 100644 --- a/frontend/src/components/shared/markdown-editor.tsx +++ b/frontend/src/components/shared/markdown-editor.tsx @@ -1,10 +1,8 @@ import type { Editor } from '@tiptap/react'; -import { Placeholder } from '@tiptap/extensions'; import { history } from '@tiptap/pm/history'; import { EditorState } from '@tiptap/pm/state'; import { EditorContent, useEditor } from '@tiptap/react'; -import StarterKit from '@tiptap/starter-kit'; import { Bold, Code, @@ -12,26 +10,31 @@ import { Heading1, Heading2, Heading3, + ImagePlus, Italic, Link as LinkIcon, List, ListOrdered, + ListTodo, Minus, Quote, Redo, Strikethrough, + Table, Undo, } from 'lucide-react'; import { type Ref, useCallback, useEffect, useImperativeHandle, useRef } from 'react'; -import { Markdown } from 'tiptap-markdown'; import { Separator } from '@/components/ui/separator'; import { Toggle } from '@/components/ui/toggle'; import { cn } from '@/lib/utils'; +import { createMarkdownExtensions } from './markdown-editor-extensions'; + export interface MarkdownEditorHandle { focus: () => void; getEditor: () => Editor | null; + insertAtCursor: (text: string) => void; } export interface MarkdownEditorProps { @@ -115,9 +118,9 @@ function MarkdownEditor({ const onChangeRef = useRef(onChange); const onBlurRef = useRef(onBlur); // Tracks the last markdown the editor reported externally. We compare - // against this to suppress echo updates: tiptap-markdown can re-serialize - // content slightly differently than the input string (whitespace/list - // markers/hard breaks/etc.), and we don't want to flag those + // against this to suppress echo updates: the markdown round-trip can + // re-serialize content slightly differently than the input string + // (whitespace/list markers/blank lines/etc.), and we don't want to flag those // normalizations as user edits — that would falsely flip RHF's // `isDirty` flag. // @@ -146,33 +149,13 @@ function MarkdownEditor({ const editor = useEditor({ content: value, + contentType: 'markdown', editable: !disabled, - extensions: [ - StarterKit.configure({ - codeBlock: { HTMLAttributes: { class: 'hljs' } }, - }), - Placeholder.configure({ - emptyEditorClass: 'is-editor-empty', - placeholder, - }), - Markdown.configure({ - breaks: true, - html: false, - linkify: true, - tightLists: true, - transformCopiedText: true, - // Plain text pasted from the OS clipboard is left as-is. - // With `transformPastedText: true`, a leading "- " (or - // "1. ", "> ", etc.) would be parsed as markdown and - // turn the paste into a list/blockquote — almost never - // what the user wants for knowledge documents. - transformPastedText: false, - }), - ], + extensions: createMarkdownExtensions(placeholder), immediatelyRender: false, onBlur: () => onBlurRef.current?.(), onCreate: ({ editor: instance }) => { - lastEmittedRef.current = instance.storage.markdown.getMarkdown(); + lastEmittedRef.current = instance.getMarkdown(); isInitializedRef.current = true; }, onUpdate: ({ editor: instance }) => { @@ -180,7 +163,7 @@ function MarkdownEditor({ return; } - const next = instance.storage.markdown.getMarkdown(); + const next = instance.getMarkdown(); if (next === lastEmittedRef.current) { return; @@ -196,6 +179,16 @@ function MarkdownEditor({ () => ({ focus: () => editor?.commands.focus(), getEditor: () => editor, + insertAtCursor: (text: string) => { + if (!editor) { + return; + } + + const { state, view } = editor; + + view.dispatch(state.tr.insertText(text).scrollIntoView()); + view.focus(); + }, }), [editor], ); @@ -207,11 +200,17 @@ function MarkdownEditor({ return; } - const current = editor.storage.markdown.getMarkdown(); + const current = editor.getMarkdown(); const shouldExternalSync = current !== value; if (shouldExternalSync) { - editor.commands.setContent(value, { emitUpdate: false }); + // @tiptap/markdown only wires `contentType: 'markdown'` into the initial + // content + insertContent — NOT setContent — so parse the markdown explicitly. + const parsed = editor.markdown?.parse(value); + + if (parsed) { + editor.commands.setContent(parsed, { emitUpdate: false }); + } } // The editor's own serialized markdown is the canonical form — @@ -219,7 +218,7 @@ function MarkdownEditor({ // representation, not the (possibly non-normalized) input // `value`. Storing the raw `value` here would cause the first // user keystroke to also re-emit the round-trip normalization. - lastEmittedRef.current = editor.storage.markdown.getMarkdown(); + lastEmittedRef.current = editor.getMarkdown(); // Clear the undo stack: // - on initial mount, to discard the construction-time @@ -422,6 +421,15 @@ function MarkdownEditorToolbar({ disabled, editor }: MarkdownEditorToolbarProps) > + editor.chain().focus().toggleTaskList().run()} + pressed={editor.isActive('taskList')} + size="sm" + title="Task list" + > + + + { + const url = window.prompt('Image URL'); + + if (url) { + editor.chain().focus().setImage({ src: url }).run(); + } + }} + pressed={false} + size="sm" + title="Insert image" + > + + editor.chain().focus().setHorizontalRule().run()} @@ -464,6 +487,17 @@ function MarkdownEditorToolbar({ disabled, editor }: MarkdownEditorToolbarProps) > + + editor.chain().focus().insertTable({ cols: 3, rows: 3, withHeaderRow: true }).run() + } + pressed={editor.isActive('table')} + size="sm" + title="Insert table" + > + +
{showLabel ? Content : null} - + {viewMode === 'plain' ? ( +