From 7ee86ad5abff516fd77081802c84fbb28e2de2f0 Mon Sep 17 00:00:00 2001 From: andresgutgon Date: Wed, 18 Jun 2025 15:21:50 +0200 Subject: [PATCH 01/22] feat: wip --- packages/web-ui/package.json | 3 + .../src/ds/molecules/BlocksEditor/index.tsx | 14 + pnpm-lock.yaml | 363 +++++++++++++++++- 3 files changed, 366 insertions(+), 14 deletions(-) create mode 100644 packages/web-ui/src/ds/molecules/BlocksEditor/index.tsx diff --git a/packages/web-ui/package.json b/packages/web-ui/package.json index abf7713bd..05ede5882 100644 --- a/packages/web-ui/package.json +++ b/packages/web-ui/package.json @@ -141,6 +141,9 @@ "@radix-ui/react-switch": "^1.1.3", "@radix-ui/react-toast": "^1.2.6", "@radix-ui/react-tooltip": "^1.1.8", + "@tiptap/core": "3.0.0-beta.8", + "@tiptap/pm": "3.0.0-beta.8", + "@tiptap/react": "3.0.0-beta.8", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "cmdk": "^1.1.1", diff --git a/packages/web-ui/src/ds/molecules/BlocksEditor/index.tsx b/packages/web-ui/src/ds/molecules/BlocksEditor/index.tsx new file mode 100644 index 000000000..6e13cfdd3 --- /dev/null +++ b/packages/web-ui/src/ds/molecules/BlocksEditor/index.tsx @@ -0,0 +1,14 @@ +import { useEditor, EditorContent } from '@tiptap/react' + +const content = '

Hello World!

' + +const Tiptap = () => { + const editor = useEditor({ + extensions: [], + content, + }) + + return +} + +export default Tiptap diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c45787ce5..0e9ba3483 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -218,7 +218,7 @@ importers: version: 0.0.4 '@sentry/nextjs': specifier: 9.10.1 - version: 9.10.1(@opentelemetry/context-async-hooks@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.57.2(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0))(next@15.3.3(@opentelemetry/api@1.8.0)(react-dom@19.0.0-rc-5d19e1c8-20240923(react@19.0.0-rc-5d19e1c8-20240923))(react@19.0.0-rc-5d19e1c8-20240923))(react@19.0.0-rc-5d19e1c8-20240923)(webpack@5.99.9(esbuild@0.25.5)) + version: 9.10.1(@opentelemetry/context-async-hooks@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.57.2(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0))(next@15.3.3(@opentelemetry/api@1.8.0)(react-dom@19.0.0-rc-5d19e1c8-20240923(react@19.0.0-rc-5d19e1c8-20240923))(react@19.0.0-rc-5d19e1c8-20240923))(react@19.0.0-rc-5d19e1c8-20240923)(webpack@5.99.9) '@sindresorhus/slugify': specifier: 2.2.1 version: 2.2.1 @@ -1195,6 +1195,15 @@ importers: '@radix-ui/react-tooltip': specifier: ^1.1.8 version: 1.2.7(@types/react-dom@18.3.0)(@types/react@18.3.0)(react-dom@18.3.0(react@18.3.0))(react@18.3.0) + '@tiptap/core': + specifier: 3.0.0-beta.8 + version: 3.0.0-beta.8(@tiptap/pm@3.0.0-beta.8) + '@tiptap/pm': + specifier: 3.0.0-beta.8 + version: 3.0.0-beta.8 + '@tiptap/react': + specifier: 3.0.0-beta.8 + version: 3.0.0-beta.8(@floating-ui/dom@1.7.1)(@tiptap/core@3.0.0-beta.8(@tiptap/pm@3.0.0-beta.8))(@tiptap/pm@3.0.0-beta.8)(react-dom@18.3.0(react@18.3.0))(react@18.3.0) class-variance-authority: specifier: ^0.7.1 version: 0.7.1 @@ -5262,6 +5271,9 @@ packages: peerDependencies: react: ^18.0 || ^19.0 || ^19.0.0-rc + '@remirror/core-constants@3.0.0': + resolution: {integrity: sha512-42aWfPrimMfDKDi4YegyS7x+/0tlzaqwPQCULLanv3DMIlu96KTJR0fM5isWX2UViOqlGnX6YFgqWepcX+XMNg==} + '@rollup/plugin-alias@5.1.1': resolution: {integrity: sha512-PR9zDb+rOzkRb2VD+EuKB7UC41vU5DIwZ5qqCpk0KJudcWAyi8rvYOhS7+L5aZCspw1stTViLgN5v6FF1p5cgQ==} engines: {node: '>=14.0.0'} @@ -6376,6 +6388,35 @@ packages: '@types/react-dom': optional: true + '@tiptap/core@3.0.0-beta.8': + resolution: {integrity: sha512-TQsHwPZV/YGydnYO99b9otg1zwCLRsI2+KCxMzSkK+IQNfya5oUIqZ2MQjbPLBfiR6rSR9cFx88K+DTozbTXsQ==} + peerDependencies: + '@tiptap/pm': 3.0.0-beta.8 + + '@tiptap/extension-bubble-menu@3.0.0-beta.8': + resolution: {integrity: sha512-RCj+jxR9y+4XcCkLQG5c0Kf8FQ78jhoF1SvxuCxiPvvRS775MwXCpkMctnM7EUDd/XwTwmESxpUSyAsP5Z9XVQ==} + peerDependencies: + '@tiptap/core': 3.0.0-beta.8 + '@tiptap/pm': 3.0.0-beta.8 + + '@tiptap/extension-floating-menu@3.0.0-beta.8': + resolution: {integrity: sha512-ZtQHz4lc6ZSd6ynTIhnlSzjtXz+oqDt0OeGwC6wkiB2MuiWqbBybxcz274tFRiilnJUo222wwCPftJkegckfcw==} + peerDependencies: + '@floating-ui/dom': ^1.0.0 + '@tiptap/core': 3.0.0-beta.8 + '@tiptap/pm': 3.0.0-beta.8 + + '@tiptap/pm@3.0.0-beta.8': + resolution: {integrity: sha512-mPgXEY/HrZZBTPqZ9EGHGV2oVzXXYXL+RnJ4F8oxb6d8PeNhCMqSUPCzhZW6/efL7g3147OisA4X3nzhR4YaeQ==} + + '@tiptap/react@3.0.0-beta.8': + resolution: {integrity: sha512-Wp9KzS8QyX2ZN2u4nQoS5Z54lcWGEbjKID3ARFnEg3gFTON88D+M7M8udyivddbXSo52tcYaMLPFsG97kYVjJA==} + peerDependencies: + '@tiptap/core': 3.0.0-beta.8 + '@tiptap/pm': 3.0.0-beta.8 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^17.0.0 || ^18.0.0 || ^19.0.0 + '@tokenizer/token@0.3.0': resolution: {integrity: sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==} @@ -6569,6 +6610,9 @@ packages: '@types/katex@0.16.7': resolution: {integrity: sha512-HMwFiRujE5PjrgwHQ25+bsLJgowjGjm5Z8FVSf0N6PwgJrwxH0QxzHYDcKsTfV3wva0vzrpqMTJS2jXPr5BMEQ==} + '@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==} @@ -6584,9 +6628,15 @@ packages: '@types/long@4.0.2': resolution: {integrity: sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==} + '@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/mdx@2.0.13': resolution: {integrity: sha512-+OWZQfAYyio6YkJb3HLxDrvnx6SWWDbC0zVPfBRzUk0/nqoDyf6dNxQi3eArPe8rJ473nobTMQ/8Zk+LxJ+Yuw==} @@ -6728,6 +6778,9 @@ packages: '@types/urijs@1.19.25': resolution: {integrity: sha512-XOfUup9r3Y06nFAZh3WvO0rBU4OtlfPB/vgxpjg+NRdGU6CN6djdc6OEiH+PcqHCY6eFLo9Ista73uarf4gnBg==} + '@types/use-sync-external-store@0.0.6': + resolution: {integrity: sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==} + '@types/uuid@10.0.0': resolution: {integrity: sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ==} @@ -8186,6 +8239,9 @@ packages: typescript: optional: true + crelt@1.0.6: + resolution: {integrity: sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==} + cron-parser@4.9.0: resolution: {integrity: sha512-p0SaNjrHOnQeR8/VnfGbmg9te2kfyYSQ7Sc/j/6DtPL3JQvKxmjO9TSjNFpujqV3vEYYBvNNvXSxzyksBWAx1Q==} engines: {node: '>=12.0.0'} @@ -10432,6 +10488,9 @@ 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==} + llamaindex@0.8.37: resolution: {integrity: sha512-2q7rbfGH478+qFGN/45ZyJymWhPBhVxCC1nJgBlcwSFMkZKwsOJ2BRuxKv3EK+w157Mg1rJu0ub1LW86slo3lg==} engines: {node: '>=18.0.0'} @@ -10629,6 +10688,10 @@ packages: resolution: {integrity: sha512-o5vL7aDWatOTX8LzaS1WMoaoxIiLRQJuIKKe2wAw6IeULDHaqbiqiggmx+pKvZDb1Sj+pE46Sn1T7lCqfFtg1Q==} engines: {node: '>=16'} + markdown-it@14.1.0: + resolution: {integrity: sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==} + hasBin: true + markdown-table@3.0.4: resolution: {integrity: sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==} @@ -10714,6 +10777,9 @@ packages: mdn-data@2.0.30: resolution: {integrity: sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==} + mdurl@2.0.0: + resolution: {integrity: sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==} + media-typer@0.3.0: resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==} engines: {node: '>= 0.6'} @@ -11469,6 +11535,9 @@ packages: resolution: {integrity: sha512-ERAyNnZOfqM+Ao3RAvIXkYh5joP220yf59gVe2X/cI6SiCxIdi4c9HZKZD8R6q/RDXEje1THBju6iExiSsgJaQ==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + orderedmap@2.1.1: + resolution: {integrity: sha512-TvAWxi0nDe1j/rtMcWcIj94+Ffe6n7zhow33h40SKxmsmozs6dz/e+EajymfoFcHd7sxNn8yHM8839uixMOV6g==} + os-tmpdir@1.0.2: resolution: {integrity: sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==} engines: {node: '>=0.10.0'} @@ -11964,6 +12033,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.3.2: + resolution: {integrity: sha512-wtjswVBd2vaQRrnYZaBCbyDqr232Ed4p2QPtRIUK5FuqHYKGWkEwl08oQM4Tw7DOR0FsasARV5uJFvMZWxdNxQ==} + + prosemirror-history@1.4.1: + resolution: {integrity: sha512-2JZD8z2JviJrboD9cPuX/Sv/1ChFng+xh2tChQ2X4bB2HeK+rra/bmJ3xGntCcjhOqIzSDG6Id7e8RJ9QPXLEQ==} + + prosemirror-inputrules@1.5.0: + resolution: {integrity: sha512-K0xJRCmt+uSw7xesnHmcn72yBGTbY45vm8gXI4LZXbx2Z0jwh5aF9xrGQgrVPu0WbyFVFF3E/o9VhJYz6SQWnA==} + + 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.1: + resolution: {integrity: sha512-AUvbm7qqmpZa5d9fPKMvH1Q5bqYQvAZWOGRvxsB6iFLyycvC9MwNemNVjHVrWgjaoxAfY8XVg7DbvQ/qxvI9Eg==} + + 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.3: + resolution: {integrity: sha512-goFKORVbvPuAQaXhpbemJFRKJ2aixr+AZMGiquiqKxaucC6hlpHNZHWgz5R7dS4roHiwq9vDctE//CZ++o0W1Q==} + + prosemirror-tables@1.7.1: + resolution: {integrity: sha512-eRQ97Bf+i9Eby99QbyAiyov43iOKgWa7QCGly+lrDt7efZ1v8NWolhXiB43hSDGIXT1UXgbs4KJN3a06FGpr1Q==} + + 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.40.0: + resolution: {integrity: sha512-2G3svX0Cr1sJjkD/DYWSe3cfV5VPVTBOxI9XQEGWJDFEpsZb/gh4MV29ctv+OJx2RFX4BLt09i+6zaGM/ldkCw==} + proto3-json-serializer@2.0.2: resolution: {integrity: sha512-SAzp/O4Yh02jGdRc+uIrGoe87dkN/XtwxfZ4ZyafJHymd79ozp5VG5nyZ7ygqPM5+cpLDjjGnYFUkngonyDPOQ==} engines: {node: '>=14.0.0'} @@ -12000,6 +12127,10 @@ packages: pump@3.0.2: resolution: {integrity: sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw==} + 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'} @@ -12464,6 +12595,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'} @@ -13389,6 +13523,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==} @@ -13781,6 +13918,9 @@ packages: typescript: optional: true + w3c-keyname@2.2.8: + resolution: {integrity: sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==} + w3c-xmlserializer@5.0.0: resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==} engines: {node: '>=18'} @@ -19418,6 +19558,8 @@ snapshots: dependencies: react: 18.3.0 + '@remirror/core-constants@3.0.0': {} + '@rollup/plugin-alias@5.1.1(rollup@4.43.0)': optionalDependencies: rollup: 4.43.0 @@ -19763,7 +19905,7 @@ snapshots: '@sentry/core@9.9.0': {} - '@sentry/nextjs@9.10.1(@opentelemetry/context-async-hooks@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.57.2(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0))(next@15.3.3(@opentelemetry/api@1.8.0)(react-dom@19.0.0-rc-5d19e1c8-20240923(react@19.0.0-rc-5d19e1c8-20240923))(react@19.0.0-rc-5d19e1c8-20240923))(react@19.0.0-rc-5d19e1c8-20240923)(webpack@5.99.9(esbuild@0.25.5))': + '@sentry/nextjs@9.10.1(@opentelemetry/context-async-hooks@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.57.2(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0))(next@15.3.3(@opentelemetry/api@1.8.0)(react-dom@19.0.0-rc-5d19e1c8-20240923(react@19.0.0-rc-5d19e1c8-20240923))(react@19.0.0-rc-5d19e1c8-20240923))(react@19.0.0-rc-5d19e1c8-20240923)(webpack@5.99.9)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/semantic-conventions': 1.34.0 @@ -19771,10 +19913,10 @@ snapshots: '@sentry-internal/browser-utils': 9.10.1 '@sentry/core': 9.10.1 '@sentry/node': 9.10.1 - '@sentry/opentelemetry': 9.10.1(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@1.30.1(@opentelemetry/api@1.8.0))(@opentelemetry/core@1.30.1(@opentelemetry/api@1.8.0))(@opentelemetry/instrumentation@0.57.2(@opentelemetry/api@1.8.0))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.8.0))(@opentelemetry/semantic-conventions@1.34.0) + '@sentry/opentelemetry': 9.10.1(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.57.2(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.34.0) '@sentry/react': 9.10.1(react@19.0.0-rc-5d19e1c8-20240923) '@sentry/vercel-edge': 9.10.1 - '@sentry/webpack-plugin': 3.2.4(webpack@5.99.9(esbuild@0.25.5)) + '@sentry/webpack-plugin': 3.2.4(webpack@5.99.9) chalk: 3.0.0 next: 15.3.3(@opentelemetry/api@1.8.0)(react-dom@19.0.0-rc-5d19e1c8-20240923(react@19.0.0-rc-5d19e1c8-20240923))(react@19.0.0-rc-5d19e1c8-20240923) resolve: 1.22.8 @@ -19824,7 +19966,7 @@ snapshots: '@opentelemetry/semantic-conventions': 1.34.0 '@prisma/instrumentation': 6.5.0(@opentelemetry/api@1.9.0) '@sentry/core': 9.10.1 - '@sentry/opentelemetry': 9.10.1(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@1.30.1(@opentelemetry/api@1.8.0))(@opentelemetry/core@1.30.1(@opentelemetry/api@1.8.0))(@opentelemetry/instrumentation@0.57.2(@opentelemetry/api@1.8.0))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.8.0))(@opentelemetry/semantic-conventions@1.34.0) + '@sentry/opentelemetry': 9.10.1(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.57.2(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.34.0) import-in-the-middle: 1.14.0 transitivePeerDependencies: - supports-color @@ -19868,7 +20010,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@sentry/opentelemetry@9.10.1(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@1.30.1(@opentelemetry/api@1.8.0))(@opentelemetry/core@1.30.1(@opentelemetry/api@1.8.0))(@opentelemetry/instrumentation@0.57.2(@opentelemetry/api@1.8.0))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.8.0))(@opentelemetry/semantic-conventions@1.34.0)': + '@sentry/opentelemetry@9.10.1(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.57.2(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.34.0)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/context-async-hooks': 1.30.1(@opentelemetry/api@1.9.0) @@ -19900,12 +20042,12 @@ snapshots: '@opentelemetry/api': 1.9.0 '@sentry/core': 9.10.1 - '@sentry/webpack-plugin@3.2.4(webpack@5.99.9(esbuild@0.25.5))': + '@sentry/webpack-plugin@3.2.4(webpack@5.99.9)': dependencies: '@sentry/bundler-plugin-core': 3.2.4 unplugin: 1.0.1 uuid: 9.0.1 - webpack: 5.99.9(esbuild@0.25.5) + webpack: 5.99.9 transitivePeerDependencies: - encoding - supports-color @@ -20911,6 +21053,60 @@ snapshots: '@types/react': 18.3.0 '@types/react-dom': 18.3.0 + '@tiptap/core@3.0.0-beta.8(@tiptap/pm@3.0.0-beta.8)': + dependencies: + '@tiptap/pm': 3.0.0-beta.8 + + '@tiptap/extension-bubble-menu@3.0.0-beta.8(@tiptap/core@3.0.0-beta.8(@tiptap/pm@3.0.0-beta.8))(@tiptap/pm@3.0.0-beta.8)': + dependencies: + '@floating-ui/dom': 1.7.1 + '@tiptap/core': 3.0.0-beta.8(@tiptap/pm@3.0.0-beta.8) + '@tiptap/pm': 3.0.0-beta.8 + optional: true + + '@tiptap/extension-floating-menu@3.0.0-beta.8(@floating-ui/dom@1.7.1)(@tiptap/core@3.0.0-beta.8(@tiptap/pm@3.0.0-beta.8))(@tiptap/pm@3.0.0-beta.8)': + dependencies: + '@floating-ui/dom': 1.7.1 + '@tiptap/core': 3.0.0-beta.8(@tiptap/pm@3.0.0-beta.8) + '@tiptap/pm': 3.0.0-beta.8 + optional: true + + '@tiptap/pm@3.0.0-beta.8': + dependencies: + prosemirror-changeset: 2.3.1 + prosemirror-collab: 1.3.1 + prosemirror-commands: 1.7.1 + prosemirror-dropcursor: 1.8.2 + prosemirror-gapcursor: 1.3.2 + prosemirror-history: 1.4.1 + prosemirror-inputrules: 1.5.0 + prosemirror-keymap: 1.2.3 + prosemirror-markdown: 1.13.2 + prosemirror-menu: 1.2.5 + prosemirror-model: 1.25.1 + prosemirror-schema-basic: 1.2.4 + prosemirror-schema-list: 1.5.1 + prosemirror-state: 1.4.3 + prosemirror-tables: 1.7.1 + prosemirror-trailing-node: 3.0.0(prosemirror-model@1.25.1)(prosemirror-state@1.4.3)(prosemirror-view@1.40.0) + prosemirror-transform: 1.10.4 + prosemirror-view: 1.40.0 + + '@tiptap/react@3.0.0-beta.8(@floating-ui/dom@1.7.1)(@tiptap/core@3.0.0-beta.8(@tiptap/pm@3.0.0-beta.8))(@tiptap/pm@3.0.0-beta.8)(react-dom@18.3.0(react@18.3.0))(react@18.3.0)': + dependencies: + '@tiptap/core': 3.0.0-beta.8(@tiptap/pm@3.0.0-beta.8) + '@tiptap/pm': 3.0.0-beta.8 + '@types/use-sync-external-store': 0.0.6 + fast-deep-equal: 3.1.3 + react: 18.3.0 + react-dom: 18.3.0(react@18.3.0) + use-sync-external-store: 1.5.0(react@18.3.0) + optionalDependencies: + '@tiptap/extension-bubble-menu': 3.0.0-beta.8(@tiptap/core@3.0.0-beta.8(@tiptap/pm@3.0.0-beta.8))(@tiptap/pm@3.0.0-beta.8) + '@tiptap/extension-floating-menu': 3.0.0-beta.8(@floating-ui/dom@1.7.1)(@tiptap/core@3.0.0-beta.8(@tiptap/pm@3.0.0-beta.8))(@tiptap/pm@3.0.0-beta.8) + transitivePeerDependencies: + - '@floating-ui/dom' + '@tokenizer/token@0.3.0': {} '@tootallnate/once@2.0.0': {} @@ -21168,6 +21364,8 @@ snapshots: '@types/katex@0.16.7': {} + '@types/linkify-it@5.0.0': {} + '@types/lodash-es@4.17.12': dependencies: '@types/lodash': 4.17.17 @@ -21184,10 +21382,17 @@ snapshots: '@types/long@4.0.2': {} + '@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/mdx@2.0.13': {} '@types/mime@1.3.5': {} @@ -21346,6 +21551,8 @@ snapshots: '@types/urijs@1.19.25': {} + '@types/use-sync-external-store@0.0.6': {} + '@types/uuid@10.0.0': {} '@types/uuid@9.0.8': {} @@ -22860,6 +23067,8 @@ snapshots: optionalDependencies: typescript: 5.8.3 + crelt@1.0.6: {} + cron-parser@4.9.0: dependencies: luxon: 3.6.1 @@ -25608,6 +25817,10 @@ snapshots: lines-and-columns@1.2.4: {} + linkify-it@5.0.0: + dependencies: + uc.micro: 2.1.0 + llamaindex@0.8.37(@aws-crypto/sha256-js@5.2.0)(@aws-sdk/credential-providers@3.826.0)(@huggingface/transformers@3.5.2)(cohere-ai@7.17.1)(js-tiktoken@1.0.20)(jsdom@24.1.3(canvas@2.11.2))(msw@2.10.2(@types/node@22.15.31)(typescript@5.8.3))(openai@4.104.0(ws@8.18.2)(zod@3.24.2))(pathe@1.1.2)(pg-query-stream@4.8.1(pg@8.14.1))(pg@8.14.1)(socks@2.8.5)(terser@5.42.0)(tree-sitter@0.22.4)(typescript@5.8.3)(web-tree-sitter@0.24.7)(ws@8.18.2)(zod@3.24.2): dependencies: '@llamaindex/anthropic': 0.0.33(@aws-crypto/sha256-js@5.2.0)(@huggingface/transformers@3.5.2)(@types/node@22.15.31)(gpt-tokenizer@2.9.0)(js-tiktoken@1.0.20)(jsdom@24.1.3(canvas@2.11.2))(msw@2.10.2(@types/node@22.15.31)(typescript@5.8.3))(pathe@1.1.2)(terser@5.42.0) @@ -25864,6 +26077,15 @@ snapshots: markdown-extensions@2.0.0: {} + 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: {} marked@7.0.4: {} @@ -26071,6 +26293,8 @@ snapshots: mdn-data@2.0.30: {} + mdurl@2.0.0: {} + media-typer@0.3.0: {} media-typer@1.1.0: {} @@ -27110,6 +27334,8 @@ snapshots: strip-ansi: 7.1.0 wcwidth: 1.0.1 + orderedmap@2.1.1: {} + os-tmpdir@1.0.2: {} oslo@1.2.0: @@ -27602,6 +27828,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.3 + + prosemirror-commands@1.7.1: + dependencies: + prosemirror-model: 1.25.1 + prosemirror-state: 1.4.3 + prosemirror-transform: 1.10.4 + + prosemirror-dropcursor@1.8.2: + dependencies: + prosemirror-state: 1.4.3 + prosemirror-transform: 1.10.4 + prosemirror-view: 1.40.0 + + prosemirror-gapcursor@1.3.2: + dependencies: + prosemirror-keymap: 1.2.3 + prosemirror-model: 1.25.1 + prosemirror-state: 1.4.3 + prosemirror-view: 1.40.0 + + prosemirror-history@1.4.1: + dependencies: + prosemirror-state: 1.4.3 + prosemirror-transform: 1.10.4 + prosemirror-view: 1.40.0 + rope-sequence: 1.3.4 + + prosemirror-inputrules@1.5.0: + dependencies: + prosemirror-state: 1.4.3 + prosemirror-transform: 1.10.4 + + prosemirror-keymap@1.2.3: + dependencies: + prosemirror-state: 1.4.3 + 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.1 + + prosemirror-menu@1.2.5: + dependencies: + crelt: 1.0.6 + prosemirror-commands: 1.7.1 + prosemirror-history: 1.4.1 + prosemirror-state: 1.4.3 + + prosemirror-model@1.25.1: + dependencies: + orderedmap: 2.1.1 + + prosemirror-schema-basic@1.2.4: + dependencies: + prosemirror-model: 1.25.1 + + prosemirror-schema-list@1.5.1: + dependencies: + prosemirror-model: 1.25.1 + prosemirror-state: 1.4.3 + prosemirror-transform: 1.10.4 + + prosemirror-state@1.4.3: + dependencies: + prosemirror-model: 1.25.1 + prosemirror-transform: 1.10.4 + prosemirror-view: 1.40.0 + + prosemirror-tables@1.7.1: + dependencies: + prosemirror-keymap: 1.2.3 + prosemirror-model: 1.25.1 + prosemirror-state: 1.4.3 + prosemirror-transform: 1.10.4 + prosemirror-view: 1.40.0 + + prosemirror-trailing-node@3.0.0(prosemirror-model@1.25.1)(prosemirror-state@1.4.3)(prosemirror-view@1.40.0): + dependencies: + '@remirror/core-constants': 3.0.0 + escape-string-regexp: 4.0.0 + prosemirror-model: 1.25.1 + prosemirror-state: 1.4.3 + prosemirror-view: 1.40.0 + + prosemirror-transform@1.10.4: + dependencies: + prosemirror-model: 1.25.1 + + prosemirror-view@1.40.0: + dependencies: + prosemirror-model: 1.25.1 + prosemirror-state: 1.4.3 + prosemirror-transform: 1.10.4 + proto3-json-serializer@2.0.2: dependencies: protobufjs: 7.5.3 @@ -27676,6 +28005,8 @@ snapshots: end-of-stream: 1.4.4 once: 1.4.0 + punycode.js@2.3.1: {} + punycode@2.3.1: {} puppeteer-core@22.15.0: @@ -28401,6 +28732,8 @@ snapshots: '@rollup/rollup-win32-x64-msvc': 4.43.0 fsevents: 2.3.3 + rope-sequence@1.3.4: {} + router@2.2.0: dependencies: debug: 4.4.1 @@ -29255,16 +29588,14 @@ snapshots: - encoding - supports-color - terser-webpack-plugin@5.3.14(esbuild@0.25.5)(webpack@5.99.9(esbuild@0.25.5)): + terser-webpack-plugin@5.3.14(webpack@5.99.9): dependencies: '@jridgewell/trace-mapping': 0.3.25 jest-worker: 27.5.1 schema-utils: 4.3.2 serialize-javascript: 6.0.2 terser: 5.42.0 - webpack: 5.99.9(esbuild@0.25.5) - optionalDependencies: - esbuild: 0.25.5 + webpack: 5.99.9 terser@5.42.0: dependencies: @@ -29579,6 +29910,8 @@ snapshots: typescript@5.8.3: {} + uc.micro@2.1.0: {} + ufo@1.6.1: {} unbox-primitive@1.1.0: @@ -30183,6 +30516,8 @@ snapshots: optionalDependencies: typescript: 5.8.3 + w3c-keyname@2.2.8: {} + w3c-xmlserializer@5.0.0: dependencies: xml-name-validator: 5.0.0 @@ -30229,7 +30564,7 @@ snapshots: webpack-virtual-modules@0.5.0: {} - webpack@5.99.9(esbuild@0.25.5): + webpack@5.99.9: dependencies: '@types/eslint-scope': 3.7.7 '@types/estree': 1.0.8 @@ -30252,7 +30587,7 @@ snapshots: neo-async: 2.6.2 schema-utils: 4.3.2 tapable: 2.2.2 - terser-webpack-plugin: 5.3.14(esbuild@0.25.5)(webpack@5.99.9(esbuild@0.25.5)) + terser-webpack-plugin: 5.3.14(webpack@5.99.9) watchpack: 2.4.4 webpack-sources: 3.3.2 transitivePeerDependencies: From 9a439fe7bcf8588c049efed49cf06d444da618ca Mon Sep 17 00:00:00 2001 From: andresgutgon Date: Wed, 18 Jun 2025 15:46:35 +0200 Subject: [PATCH 02/22] feat: wip --- packages/web-ui/package.json | 2 + .../BlocksEditor/extensions/BlocksSchema.ts | 61 +++++++ .../extensions/ContentFileExtension.ts | 120 ++++++++++++++ .../extensions/ContentImageExtension.ts | 112 +++++++++++++ .../extensions/DocumentExtension.ts | 23 +++ .../extensions/MessageBlockExtension.ts | 115 +++++++++++++ .../extensions/StepBlockExtension.ts | 132 +++++++++++++++ .../extensions/TextBlockComponent.tsx | 148 +++++++++++++++++ .../extensions/TextBlockExtension.ts | 151 +++++++++++++++++ .../BlocksEditor/extensions/TextExtension.ts | 10 ++ .../extensions/ToolCallExtension.ts | 136 +++++++++++++++ .../src/ds/molecules/BlocksEditor/index.tsx | 156 +++++++++++++++++- pnpm-lock.yaml | 50 ++++-- 13 files changed, 1198 insertions(+), 18 deletions(-) create mode 100644 packages/web-ui/src/ds/molecules/BlocksEditor/extensions/BlocksSchema.ts create mode 100644 packages/web-ui/src/ds/molecules/BlocksEditor/extensions/ContentFileExtension.ts create mode 100644 packages/web-ui/src/ds/molecules/BlocksEditor/extensions/ContentImageExtension.ts create mode 100644 packages/web-ui/src/ds/molecules/BlocksEditor/extensions/DocumentExtension.ts create mode 100644 packages/web-ui/src/ds/molecules/BlocksEditor/extensions/MessageBlockExtension.ts create mode 100644 packages/web-ui/src/ds/molecules/BlocksEditor/extensions/StepBlockExtension.ts create mode 100644 packages/web-ui/src/ds/molecules/BlocksEditor/extensions/TextBlockComponent.tsx create mode 100644 packages/web-ui/src/ds/molecules/BlocksEditor/extensions/TextBlockExtension.ts create mode 100644 packages/web-ui/src/ds/molecules/BlocksEditor/extensions/TextExtension.ts create mode 100644 packages/web-ui/src/ds/molecules/BlocksEditor/extensions/ToolCallExtension.ts diff --git a/packages/web-ui/package.json b/packages/web-ui/package.json index 05ede5882..f41ce533a 100644 --- a/packages/web-ui/package.json +++ b/packages/web-ui/package.json @@ -142,6 +142,8 @@ "@radix-ui/react-toast": "^1.2.6", "@radix-ui/react-tooltip": "^1.1.8", "@tiptap/core": "3.0.0-beta.8", + "@tiptap/extension-document": "3.0.0-beta.8", + "@tiptap/extension-text": "3.0.0-beta.8", "@tiptap/pm": "3.0.0-beta.8", "@tiptap/react": "3.0.0-beta.8", "class-variance-authority": "^0.7.1", diff --git a/packages/web-ui/src/ds/molecules/BlocksEditor/extensions/BlocksSchema.ts b/packages/web-ui/src/ds/molecules/BlocksEditor/extensions/BlocksSchema.ts new file mode 100644 index 000000000..694cab1a6 --- /dev/null +++ b/packages/web-ui/src/ds/molecules/BlocksEditor/extensions/BlocksSchema.ts @@ -0,0 +1,61 @@ +import { Extension } from '@tiptap/core' + +export interface BlocksSchemaOptions { + // Global options for all block types +} + +/** + * Base schema extension that defines the overall structure for blocks + */ +export const BlocksSchema = Extension.create({ + name: 'blocksSchema', + + addOptions() { + return {} + }, + + addGlobalAttributes() { + return [ + { + types: [ + 'textBlock', + 'contentImage', + 'contentFile', + 'toolCall', + 'promptBlock', + 'messageBlock', + 'stepBlock' + ], + attributes: { + id: { + default: null, + parseHTML: element => element.getAttribute('data-id'), + renderHTML: attributes => { + if (!attributes.id) { + return {} + } + return { + 'data-id': attributes.id, + } + }, + }, + errors: { + default: null, + parseHTML: element => { + const errors = element.getAttribute('data-errors') + return errors ? JSON.parse(errors) : null + }, + renderHTML: attributes => { + if (!attributes.errors || !Array.isArray(attributes.errors) || attributes.errors.length === 0) { + return {} + } + return { + 'data-errors': JSON.stringify(attributes.errors), + } + }, + }, + }, + }, + ] + }, +}) diff --git a/packages/web-ui/src/ds/molecules/BlocksEditor/extensions/ContentFileExtension.ts b/packages/web-ui/src/ds/molecules/BlocksEditor/extensions/ContentFileExtension.ts new file mode 100644 index 000000000..9cb586890 --- /dev/null +++ b/packages/web-ui/src/ds/molecules/BlocksEditor/extensions/ContentFileExtension.ts @@ -0,0 +1,120 @@ +import { Node, mergeAttributes } from '@tiptap/core' +import { ReactNodeViewRenderer } from '@tiptap/react' +import { ContentFileComponent } from './ContentFileComponent' + +export interface ContentFileOptions { + HTMLAttributes: Record +} + +declare module '@tiptap/core' { + interface Commands { + contentFile: { + /** + * Set a content file block + */ + setContentFile: (content: string, name?: string) => ReturnType + } + } +} + +export const ContentFileExtension = Node.create({ + name: 'contentFile', + + addOptions() { + return { + HTMLAttributes: {}, + } + }, + + group: 'block contentBlock', + + atom: true, + + addAttributes() { + return { + id: { + default: null, + parseHTML: element => element.getAttribute('data-id'), + renderHTML: attributes => { + if (!attributes.id) { + return {} + } + return { + 'data-id': attributes.id, + } + }, + }, + content: { + default: '', + parseHTML: element => element.getAttribute('data-content'), + renderHTML: attributes => ({ + 'data-content': attributes.content, + }), + }, + name: { + default: '', + parseHTML: element => element.getAttribute('data-name'), + renderHTML: attributes => ({ + 'data-name': attributes.name, + }), + }, + errors: { + default: null, + parseHTML: element => { + const errors = element.getAttribute('data-errors') + return errors ? JSON.parse(errors) : null + }, + renderHTML: attributes => { + if (!attributes.errors) { + return {} + } + return { + 'data-errors': JSON.stringify(attributes.errors), + } + }, + }, + } + }, + + parseHTML() { + return [ + { + tag: 'div[data-type="content-file"]', + }, + ] + }, + + renderHTML({ HTMLAttributes }) { + return [ + 'div', + mergeAttributes( + { + 'data-type': 'content-file', + }, + this.options.HTMLAttributes, + HTMLAttributes, + ), + ] + }, + + addNodeView() { + return ReactNodeViewRenderer(ContentFileComponent) + }, + + addCommands() { + return { + setContentFile: + (content: string, name?: string) => + ({ commands }) => { + return commands.insertContent({ + type: this.name, + attrs: { + id: `file_${Date.now()}`, + content, + name: name || '', + }, + }) + }, + } + }, +}) diff --git a/packages/web-ui/src/ds/molecules/BlocksEditor/extensions/ContentImageExtension.ts b/packages/web-ui/src/ds/molecules/BlocksEditor/extensions/ContentImageExtension.ts new file mode 100644 index 000000000..89110d163 --- /dev/null +++ b/packages/web-ui/src/ds/molecules/BlocksEditor/extensions/ContentImageExtension.ts @@ -0,0 +1,112 @@ +import { Node, mergeAttributes } from '@tiptap/core' +import { ReactNodeViewRenderer } from '@tiptap/react' +import { ContentImageComponent } from './ContentImageComponent' + +export interface ContentImageOptions { + HTMLAttributes: Record +} + +declare module '@tiptap/core' { + interface Commands { + contentImage: { + /** + * Set a content image block + */ + setContentImage: (content: string) => ReturnType + } + } +} + +export const ContentImageExtension = Node.create({ + name: 'contentImage', + + addOptions() { + return { + HTMLAttributes: {}, + } + }, + + group: 'block contentBlock', + + atom: true, + + addAttributes() { + return { + id: { + default: null, + parseHTML: element => element.getAttribute('data-id'), + renderHTML: attributes => { + if (!attributes.id) { + return {} + } + return { + 'data-id': attributes.id, + } + }, + }, + content: { + default: '', + parseHTML: element => element.getAttribute('data-content'), + renderHTML: attributes => ({ + 'data-content': attributes.content, + }), + }, + errors: { + default: null, + parseHTML: element => { + const errors = element.getAttribute('data-errors') + return errors ? JSON.parse(errors) : null + }, + renderHTML: attributes => { + if (!attributes.errors) { + return {} + } + return { + 'data-errors': JSON.stringify(attributes.errors), + } + }, + }, + } + }, + + parseHTML() { + return [ + { + tag: 'div[data-type="content-image"]', + }, + ] + }, + + renderHTML({ HTMLAttributes }) { + return [ + 'div', + mergeAttributes( + { + 'data-type': 'content-image', + }, + this.options.HTMLAttributes, + HTMLAttributes, + ), + ] + }, + + addNodeView() { + return ReactNodeViewRenderer(ContentImageComponent) + }, + + addCommands() { + return { + setContentImage: + (content: string) => + ({ commands }) => { + return commands.insertContent({ + type: this.name, + attrs: { + id: `image_${Date.now()}`, + content, + }, + }) + }, + } + }, +}) diff --git a/packages/web-ui/src/ds/molecules/BlocksEditor/extensions/DocumentExtension.ts b/packages/web-ui/src/ds/molecules/BlocksEditor/extensions/DocumentExtension.ts new file mode 100644 index 000000000..df11ca690 --- /dev/null +++ b/packages/web-ui/src/ds/molecules/BlocksEditor/extensions/DocumentExtension.ts @@ -0,0 +1,23 @@ +import { Node } from '@tiptap/core' + +/** + * Custom Document extension for our blocks editor + * This serves as the root node that can contain our block types + */ +export const DocumentExtension = Node.create({ + name: 'doc', + + topNode: true, + + content: 'block+', + + parseHTML() { + return [ + { tag: 'div[data-type="blocks-document"]' }, + ] + }, + + renderHTML() { + return ['div', { 'data-type': 'blocks-document' }, 0] + }, +}) diff --git a/packages/web-ui/src/ds/molecules/BlocksEditor/extensions/MessageBlockExtension.ts b/packages/web-ui/src/ds/molecules/BlocksEditor/extensions/MessageBlockExtension.ts new file mode 100644 index 000000000..8b7b0630e --- /dev/null +++ b/packages/web-ui/src/ds/molecules/BlocksEditor/extensions/MessageBlockExtension.ts @@ -0,0 +1,115 @@ +import { Node, mergeAttributes } from '@tiptap/core' +import { ReactNodeViewRenderer } from '@tiptap/react' +import { MessageBlockComponent } from './MessageBlockComponent' + +export interface MessageBlockOptions { + HTMLAttributes: Record +} + +declare module '@tiptap/core' { + interface Commands { + messageBlock: { + /** + * Set a message block + */ + setMessageBlock: (type: 'system' | 'user' | 'assistant' | 'developer') => ReturnType + } + } +} + +export const MessageBlockExtension = Node.create({ + name: 'messageBlock', + + addOptions() { + return { + HTMLAttributes: {}, + } + }, + + content: 'contentBlock+', + + group: 'block messageBlock', + + defining: true, + + addAttributes() { + return { + id: { + default: null, + parseHTML: element => element.getAttribute('data-id'), + renderHTML: attributes => { + if (!attributes.id) { + return {} + } + return { + 'data-id': attributes.id, + } + }, + }, + messageType: { + default: 'user', + parseHTML: element => element.getAttribute('data-message-type'), + renderHTML: attributes => ({ + 'data-message-type': attributes.messageType, + }), + }, + errors: { + default: null, + parseHTML: element => { + const errors = element.getAttribute('data-errors') + return errors ? JSON.parse(errors) : null + }, + renderHTML: attributes => { + if (!attributes.errors) { + return {} + } + return { + 'data-errors': JSON.stringify(attributes.errors), + } + }, + }, + } + }, + + parseHTML() { + return [ + { + tag: 'div[data-type="message-block"]', + }, + ] + }, + + renderHTML({ HTMLAttributes }) { + return [ + 'div', + mergeAttributes( + { + 'data-type': 'message-block', + }, + this.options.HTMLAttributes, + HTMLAttributes, + ), + 0, + ] + }, + + addNodeView() { + return ReactNodeViewRenderer(MessageBlockComponent) + }, + + addCommands() { + return { + setMessageBlock: + (type: 'system' | 'user' | 'assistant' | 'developer') => + ({ commands }) => { + return commands.insertContent({ + type: this.name, + attrs: { + id: `message_${Date.now()}`, + messageType: type, + }, + }) + }, + } + }, +}) diff --git a/packages/web-ui/src/ds/molecules/BlocksEditor/extensions/StepBlockExtension.ts b/packages/web-ui/src/ds/molecules/BlocksEditor/extensions/StepBlockExtension.ts new file mode 100644 index 000000000..86c91ac53 --- /dev/null +++ b/packages/web-ui/src/ds/molecules/BlocksEditor/extensions/StepBlockExtension.ts @@ -0,0 +1,132 @@ +import { Node, mergeAttributes } from '@tiptap/core' +import { ReactNodeViewRenderer } from '@tiptap/react' +import { StepBlockComponent } from './StepBlockComponent' + +export interface StepBlockOptions { + HTMLAttributes: Record +} + +declare module '@tiptap/core' { + interface Commands { + stepBlock: { + /** + * Set a step block + */ + setStepBlock: (attributes: { as?: string; isolated?: boolean }) => ReturnType + } + } +} + +export const StepBlockExtension = Node.create({ + name: 'stepBlock', + + addOptions() { + return { + HTMLAttributes: {}, + } + }, + + content: '(messageBlock | contentBlock)+', + + group: 'block', + + defining: true, + + addAttributes() { + return { + id: { + default: null, + parseHTML: element => element.getAttribute('data-id'), + renderHTML: attributes => { + if (!attributes.id) { + return {} + } + return { + 'data-id': attributes.id, + } + }, + }, + as: { + default: null, + parseHTML: element => element.getAttribute('data-as'), + renderHTML: attributes => { + if (!attributes.as) { + return {} + } + return { + 'data-as': attributes.as, + } + }, + }, + isolated: { + default: false, + parseHTML: element => element.getAttribute('data-isolated') === 'true', + renderHTML: attributes => { + if (!attributes.isolated) { + return {} + } + return { + 'data-isolated': 'true', + } + }, + }, + errors: { + default: null, + parseHTML: element => { + const errors = element.getAttribute('data-errors') + return errors ? JSON.parse(errors) : null + }, + renderHTML: attributes => { + if (!attributes.errors) { + return {} + } + return { + 'data-errors': JSON.stringify(attributes.errors), + } + }, + }, + } + }, + + parseHTML() { + return [ + { + tag: 'div[data-type="step-block"]', + }, + ] + }, + + renderHTML({ HTMLAttributes }) { + return [ + 'div', + mergeAttributes( + { + 'data-type': 'step-block', + }, + this.options.HTMLAttributes, + HTMLAttributes, + ), + 0, + ] + }, + + addNodeView() { + return ReactNodeViewRenderer(StepBlockComponent) + }, + + addCommands() { + return { + setStepBlock: + (attributes: { as?: string; isolated?: boolean }) => + ({ commands }) => { + return commands.insertContent({ + type: this.name, + attrs: { + id: `step_${Date.now()}`, + ...attributes, + }, + }) + }, + } + }, +}) diff --git a/packages/web-ui/src/ds/molecules/BlocksEditor/extensions/TextBlockComponent.tsx b/packages/web-ui/src/ds/molecules/BlocksEditor/extensions/TextBlockComponent.tsx new file mode 100644 index 000000000..be97053ce --- /dev/null +++ b/packages/web-ui/src/ds/molecules/BlocksEditor/extensions/TextBlockComponent.tsx @@ -0,0 +1,148 @@ +import React, { useCallback, useEffect, useRef, useState } from 'react' +import { NodeViewWrapper, NodeViewProps } from '@tiptap/react' +import { cn } from '../../../../lib/utils' + +export interface TextBlockComponentProps extends NodeViewProps { + // Additional props can be added here +} + +export const TextBlockComponent: React.FC = ({ + node, + updateAttributes, + editor, + getPos, + selected, +}) => { + const textareaRef = useRef(null) + const [content, setContent] = useState(node.attrs.content || '') + const [isFocused, setIsFocused] = useState(false) + const errors = node.attrs.errors || [] + const hasErrors = Array.isArray(errors) && errors.length > 0 + + // Sync content with node attributes + useEffect(() => { + setContent(node.attrs.content || '') + }, [node.attrs.content]) + + // Auto-resize textarea + const adjustTextareaHeight = useCallback(() => { + const textarea = textareaRef.current + if (textarea) { + textarea.style.height = 'auto' + textarea.style.height = `${textarea.scrollHeight}px` + } + }, []) + + useEffect(() => { + adjustTextareaHeight() + }, [content, adjustTextareaHeight]) + + const handleContentChange = useCallback((e: React.ChangeEvent) => { + const newContent = e.target.value + setContent(newContent) + + // Update the node attributes + updateAttributes({ + content: newContent, + }) + + // Update the editor content using the command + if (node.attrs.id) { + editor.commands.updateTextBlockContent(node.attrs.id, newContent) + } + }, [updateAttributes, editor, node.attrs.id]) + + const handleFocus = useCallback(() => { + setIsFocused(true) + }, []) + + const handleBlur = useCallback(() => { + setIsFocused(false) + }, []) + + const handleKeyDown = useCallback((e: React.KeyboardEvent) => { + // Handle special key behaviors + if (e.key === 'Enter' && (e.metaKey || e.ctrlKey)) { + // Cmd/Ctrl + Enter: Create new block + e.preventDefault() + const pos = getPos() + if (typeof pos === 'number') { + editor.commands.insertContentAt(pos + node.nodeSize, { + type: 'textBlock', + attrs: { + id: `text_${Date.now()}`, + content: '', + }, + }) + } + } else if (e.key === 'Backspace' && content === '' && !e.shiftKey) { + // Delete empty block + e.preventDefault() + const pos = getPos() + if (typeof pos === 'number') { + editor.commands.deleteRange({ from: pos, to: pos + node.nodeSize }) + } + } + }, [content, editor, getPos, node.nodeSize]) + + return ( + +
+