From ac7ea965ecac93c6d62776fc4732d9a794bfd1ae Mon Sep 17 00:00:00 2001 From: Jing Hua Date: Sat, 11 Mar 2023 02:35:55 +0800 Subject: [PATCH] feat: download chat as pdf / image / markdown fixes #26 --- package.json | 2 + src/assets/icons/ImageIcon.tsx | 17 +++ src/assets/icons/MarkdownIcon.tsx | 17 +++ src/assets/icons/PdfIcon.tsx | 17 +++ .../Chat/ChatContent/ChatContent.tsx | 40 +++--- .../Chat/ChatContent/DownloadChat.tsx | 112 ++++++++++++++++ src/main.css | 4 + src/utils/chat.ts | 74 +++++++++++ yarn.lock | 124 +++++++++++++++++- 9 files changed, 390 insertions(+), 17 deletions(-) create mode 100644 src/assets/icons/ImageIcon.tsx create mode 100644 src/assets/icons/MarkdownIcon.tsx create mode 100644 src/assets/icons/PdfIcon.tsx create mode 100644 src/components/Chat/ChatContent/DownloadChat.tsx diff --git a/package.json b/package.json index e27b81e..63327cc 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,8 @@ "dependencies": { "dompurify": "^3.0.1", "highlight.js": "^11.7.0", + "html2canvas": "^1.4.1", + "jspdf": "^2.5.1", "react": "^18.2.0", "react-dom": "^18.2.0", "react-markdown": "^8.0.5", diff --git a/src/assets/icons/ImageIcon.tsx b/src/assets/icons/ImageIcon.tsx new file mode 100644 index 0000000..d33ac2d --- /dev/null +++ b/src/assets/icons/ImageIcon.tsx @@ -0,0 +1,17 @@ +import React from 'react'; + +const ImageIcon = (props: React.SVGProps) => { + return ( + + + + ); +}; + +export default ImageIcon; diff --git a/src/assets/icons/MarkdownIcon.tsx b/src/assets/icons/MarkdownIcon.tsx new file mode 100644 index 0000000..c2551a0 --- /dev/null +++ b/src/assets/icons/MarkdownIcon.tsx @@ -0,0 +1,17 @@ +import React from 'react'; + +const MarkdownIcon = (props: React.SVGProps) => { + return ( + + + + ); +}; + +export default MarkdownIcon; diff --git a/src/assets/icons/PdfIcon.tsx b/src/assets/icons/PdfIcon.tsx new file mode 100644 index 0000000..6a10fed --- /dev/null +++ b/src/assets/icons/PdfIcon.tsx @@ -0,0 +1,17 @@ +import React from 'react'; + +const PdfIcon = (props: React.SVGProps) => { + return ( + + + + ); +}; + +export default PdfIcon; diff --git a/src/components/Chat/ChatContent/ChatContent.tsx b/src/components/Chat/ChatContent/ChatContent.tsx index 5b9574e..eb2a261 100644 --- a/src/components/Chat/ChatContent/ChatContent.tsx +++ b/src/components/Chat/ChatContent/ChatContent.tsx @@ -1,4 +1,4 @@ -import React, { useEffect } from 'react'; +import React, { useEffect, useRef } from 'react'; import ScrollToBottom from 'react-scroll-to-bottom'; import useStore from '@store/store'; @@ -9,6 +9,7 @@ import NewMessageButton from './Message/NewMessageButton'; import CrossIcon from '@icon/CrossIcon'; import useSubmit from '@hooks/useSubmit'; +import DownloadChat from './DownloadChat'; const ChatContent = () => { const inputRole = useStore((state) => state.inputRole); @@ -31,6 +32,8 @@ const ChatContent = () => { ); const generating = useStore.getState().generating; + const saveRef = useRef(null); + // clear error at the start of generating new messages useEffect(() => { if (generating) { @@ -48,25 +51,30 @@ const ChatContent = () => { >
- - {messages?.length === 0 && } - {messages?.map((message, index) => ( - <> - - - - ))} +
+ + {messages?.length === 0 && } + {messages?.map((message, index) => ( + <> + + + + ))} +
+ - {error !== '' && (
@@ -82,7 +90,9 @@ const ChatContent = () => {
)} - +
+ +
diff --git a/src/components/Chat/ChatContent/DownloadChat.tsx b/src/components/Chat/ChatContent/DownloadChat.tsx new file mode 100644 index 0000000..2464e13 --- /dev/null +++ b/src/components/Chat/ChatContent/DownloadChat.tsx @@ -0,0 +1,112 @@ +import React, { useState } from 'react'; +import useStore from '@store/store'; +import PopupModal from '@components/PopupModal'; +import { + chatToMarkdown, + downloadImg, + downloadMarkdown, + downloadPDF, + htmlToImg, +} from '@utils/chat'; +import ImageIcon from '@icon/ImageIcon'; +import PdfIcon from '@icon/PdfIcon'; +import MarkdownIcon from '@icon/MarkdownIcon'; + +const DownloadChat = ({ + saveRef, +}: { + saveRef: React.RefObject; +}) => { + const [isModalOpen, setIsModalOpen] = useState(false); + return ( + <> + + {isModalOpen && ( + +
+ + + +
+
+ )} + + ); +}; + +export default DownloadChat; diff --git a/src/main.css b/src/main.css index 27a1c7a..f2f9d72 100644 --- a/src/main.css +++ b/src/main.css @@ -27,6 +27,10 @@ height: 100%; } + img { + @apply inline-block; + } + input[type='range']::-webkit-slider-thumb { -webkit-appearance: none; @apply w-4; diff --git a/src/utils/chat.ts b/src/utils/chat.ts index 2f84a53..9bf3b0f 100644 --- a/src/utils/chat.ts +++ b/src/utils/chat.ts @@ -1,5 +1,9 @@ +import html2canvas from 'html2canvas'; +import useStore from '@store/store'; +import jsPDF from 'jspdf'; import { ChatInterface } from '@type/chat'; import { roles } from '@type/chat'; +import { Theme } from '@type/theme'; export const isChats = (chats: any): chats is ChatInterface[] => { if (!Array.isArray(chats)) return false; @@ -22,3 +26,73 @@ export const isChats = (chats: any): chats is ChatInterface[] => { return true; }; + +export const htmlToImg = async (html: HTMLDivElement) => { + const initialWidth = html.style.width; + html.style.width = '1023px'; + const canvas = await html2canvas(html); + html.style.width = initialWidth; + const dataURL = canvas.toDataURL('image/png'); + return dataURL; +}; + +export const downloadImg = (imgData: string, fileName: string) => { + const link = document.createElement('a'); + link.href = imgData; + link.download = fileName; + link.click(); + link.remove(); +}; + +export const downloadPDF = ( + imageData: string, + theme: Theme, + fileName: string +) => { + const pdf = new jsPDF('p', 'mm'); + const imageProps = pdf.getImageProperties(imageData); + const pageHeight = pdf.internal.pageSize.getHeight(); + const pageWidth = pdf.internal.pageSize.getWidth(); + const imgHeight = (imageProps.height * pageWidth) / imageProps.width; + let heightLeft = imgHeight; + let position = 0; + + pdf.addImage(imageData, 'PNG', 0, position, pageWidth, imgHeight); + heightLeft -= pageHeight; + + while (heightLeft >= 0) { + position -= pageHeight; + heightLeft -= pageHeight; + pdf.addPage(); + pdf.addImage(imageData, 'PNG', 0, position, pageWidth, imgHeight); + } + + if (heightLeft < 0) { + heightLeft = -heightLeft; + if (theme === 'dark') { + pdf.setFillColor(52, 53, 65); + } else { + pdf.setFillColor(255, 255, 255); + } + pdf.rect(0, pageHeight - heightLeft - 3, pageWidth, heightLeft + 3, 'F'); + } + + pdf.save(fileName); +}; + +export const chatToMarkdown = (chat: ChatInterface) => { + let markdown = `# ${chat.title}\n\n`; + chat.messages.forEach((message) => { + markdown += `### **${message.role}**:\n\n${message.content}\n\n---\n\n`; + }); + return markdown; +}; + +export const downloadMarkdown = (markdown: string, fileName: string) => { + const link = document.createElement('a'); + const markdownFile = new Blob([markdown], { type: 'text/markdown' }); + link.href = URL.createObjectURL(markdownFile); + link.download = fileName; + link.click(); + link.remove(); +}; diff --git a/yarn.lock b/yarn.lock index 2b0bcab..6f6e1b2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -43,7 +43,7 @@ core-js-pure "^3.25.1" regenerator-runtime "^0.13.11" -"@babel/runtime@^7.12.5", "@babel/runtime@^7.18.3": +"@babel/runtime@^7.12.5", "@babel/runtime@^7.14.0", "@babel/runtime@^7.18.3": version "7.21.0" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.21.0.tgz#5b55c9d394e5fcf304909a8b00c07dc217b56673" integrity sha512-xwII0//EObnq89Ji5AKYQaRYiW/nZ3llSv29d49IuxPhKbtJoLP+9QUUZ4nVragQVtaVGeZrpB+ZtG/Pdy/POw== @@ -394,6 +394,11 @@ resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.5.tgz#5f19d2b85a98e9558036f6a3cacc8819420f05cf" integrity sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w== +"@types/raf@^3.4.0": + version "3.4.0" + resolved "https://registry.yarnpkg.com/@types/raf/-/raf-3.4.0.tgz#2b72cbd55405e071f1c4d29992638e022b20acc2" + integrity sha512-taW5/WYqo36N7V39oYyHP9Ipfd5pNFvGTIQsNGj86xV88YQ7GnI30/yMfKDF7Zgin0m3e+ikX88FvImnK4RjGw== + "@types/react-dom@^18.0.10": version "18.0.11" resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.0.11.tgz#321351c1459bc9ca3d216aefc8a167beec334e33" @@ -478,6 +483,11 @@ arg@^5.0.2: resolved "https://registry.yarnpkg.com/arg/-/arg-5.0.2.tgz#c81433cc427c92c4dcf4865142dbca6f15acd59c" integrity sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg== +atob@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" + integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== + autoprefixer@^10.4.13: version "10.4.13" resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.4.13.tgz#b5136b59930209a321e9fa3dca2e7c4d223e83a8" @@ -504,6 +514,11 @@ bail@^2.0.0: resolved "https://registry.yarnpkg.com/bail/-/bail-2.0.2.tgz#d26f5cd8fe5d6f832a31517b9f7c356040ba6d5d" integrity sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw== +base64-arraybuffer@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz#1c37589a7c4b0746e34bd1feb951da2df01c1bdc" + integrity sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ== + binary-extensions@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" @@ -526,6 +541,11 @@ browserslist@^4.21.4: node-releases "^2.0.8" update-browserslist-db "^1.0.10" +btoa@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/btoa/-/btoa-1.2.1.tgz#01a9909f8b2c93f6bf680ba26131eb30f7fa3d73" + integrity sha512-SB4/MIGlsiVkMcHmT+pSmIPoNDoHg+7cMzmt3Uxt628MTz2487DKSqK/fuhFBrkuqrYv5UCEnACpF4dTFNKc/g== + callsites@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" @@ -541,6 +561,20 @@ caniuse-lite@^1.0.30001426, caniuse-lite@^1.0.30001449: resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001458.tgz#871e35866b4654a7d25eccca86864f411825540c" integrity sha512-lQ1VlUUq5q9ro9X+5gOEyH7i3vm+AYVT1WDCVB69XOZ17KZRhnZ9J0Sqz7wTHQaLBJccNCHq8/Ww5LlOIZbB0w== +canvg@^3.0.6: + version "3.0.10" + resolved "https://registry.yarnpkg.com/canvg/-/canvg-3.0.10.tgz#8e52a2d088b6ffa23ac78970b2a9eebfae0ef4b3" + integrity sha512-qwR2FRNO9NlzTeKIPIKpnTY6fqwuYSequ8Ru8c0YkYU7U0oW+hLUvWadLvAu1Rl72OMNiFhoLu4f8eUjQ7l/+Q== + dependencies: + "@babel/runtime" "^7.12.5" + "@types/raf" "^3.4.0" + core-js "^3.8.3" + raf "^3.4.1" + regenerator-runtime "^0.13.7" + rgbcolor "^1.0.1" + stackblur-canvas "^2.0.0" + svg-pathdata "^6.0.3" + ccount@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/ccount/-/ccount-2.0.1.tgz#17a3bf82302e0870d6da43a01311a8bc02a3ecf5" @@ -622,6 +656,11 @@ core-js@3.18.3: resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.18.3.tgz#86a0bba2d8ec3df860fefcc07a8d119779f01509" integrity sha512-tReEhtMReZaPFVw7dajMx0vlsz3oOb8ajgPoHVYGxr8ErnZ6PcYEvvmjGmXlfpnxpkYSdOQttjB+MvVbCGfvLw== +core-js@^3.6.0, core-js@^3.8.3: + version "3.29.0" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.29.0.tgz#0273e142b67761058bcde5615c503c7406b572d6" + integrity sha512-VG23vuEisJNkGl6XQmFJd3rEG/so/CNatqeE+7uZAwTSwFeB/qaO0be8xZYUNWprJ/GIwL8aMt9cj1kvbpTZhg== + cosmiconfig@^7.0.0: version "7.1.0" resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-7.1.0.tgz#1443b9afa596b670082ea46cbd8f6a62b84635f6" @@ -633,6 +672,13 @@ cosmiconfig@^7.0.0: path-type "^4.0.0" yaml "^1.10.0" +css-line-break@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/css-line-break/-/css-line-break-2.1.0.tgz#bfef660dfa6f5397ea54116bb3cb4873edbc4fa0" + integrity sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w== + dependencies: + utrie "^1.0.2" + cssesc@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee" @@ -691,6 +737,11 @@ dlv@^1.1.3: resolved "https://registry.yarnpkg.com/dlv/-/dlv-1.1.3.tgz#5c198a8a11453596e751494d49874bc7732f2e79" integrity sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA== +dompurify@^2.2.0: + version "2.4.5" + resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-2.4.5.tgz#0e89a27601f0bad978f9a924e7a05d5d2cccdd87" + integrity sha512-jggCCd+8Iqp4Tsz0nIvpcb22InKEBrGz5dw3EQJMs8HPJDsKbFIO3STYtAvCfDx26Muevn1MHVI0XxjgFfmiSA== + dompurify@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-3.0.1.tgz#a0933f38931b3238934dd632043b727e53004289" @@ -779,6 +830,11 @@ fastq@^1.6.0: dependencies: reusify "^1.0.4" +fflate@^0.4.8: + version "0.4.8" + resolved "https://registry.yarnpkg.com/fflate/-/fflate-0.4.8.tgz#f90b82aefbd8ac174213abb338bd7ef848f0f5ae" + integrity sha512-FJqqoDBR00Mdj9ppamLa/Y7vxm+PRmNWA67N846RvsoYVMKB4q3y/de5PA7gUmRMYK/8CMz2GDZQmCRN1wBcWA== + fill-range@^7.0.1: version "7.0.1" resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" @@ -891,6 +947,14 @@ highlight.js@^11.7.0: resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-11.7.0.tgz#3ff0165bc843f8c9bce1fd89e2fda9143d24b11e" integrity sha512-1rRqesRFhMO/PRF+G86evnyJkCgaZFOI+Z6kdj15TA18funfoqJXvgPCLSf0SWq3SRfg1j3HlDs8o4s3EGq1oQ== +html2canvas@^1.0.0-rc.5, html2canvas@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/html2canvas/-/html2canvas-1.4.1.tgz#7cef1888311b5011d507794a066041b14669a543" + integrity sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA== + dependencies: + css-line-break "^2.1.0" + text-segmentation "^1.0.3" + import-fresh@^3.2.1: version "3.3.0" resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" @@ -960,6 +1024,21 @@ json-parse-even-better-errors@^2.3.0: resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== +jspdf@^2.5.1: + version "2.5.1" + resolved "https://registry.yarnpkg.com/jspdf/-/jspdf-2.5.1.tgz#00c85250abf5447a05f3b32ab9935ab4a56592cc" + integrity sha512-hXObxz7ZqoyhxET78+XR34Xu2qFGrJJ2I2bE5w4SM8eFaFEkW2xcGRVUss360fYelwRSid/jT078kbNvmoW0QA== + dependencies: + "@babel/runtime" "^7.14.0" + atob "^2.1.2" + btoa "^1.2.1" + fflate "^0.4.8" + optionalDependencies: + canvg "^3.0.6" + core-js "^3.6.0" + dompurify "^2.2.0" + html2canvas "^1.0.0-rc.5" + katex@^0.13.0: version "0.13.24" resolved "https://registry.yarnpkg.com/katex/-/katex-0.13.24.tgz#fe55455eb455698cb24b911a353d16a3c855d905" @@ -1551,6 +1630,11 @@ path-type@^4.0.0: resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== +performance-now@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" + integrity sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow== + picocolors@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" @@ -1660,6 +1744,13 @@ quick-lru@^5.1.1: resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-5.1.1.tgz#366493e6b3e42a3a6885e2e99d18f80fb7a8c932" integrity sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA== +raf@^3.4.1: + version "3.4.1" + resolved "https://registry.yarnpkg.com/raf/-/raf-3.4.1.tgz#0742e99a4a6552f445d73e3ee0328af0ff1ede39" + integrity sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA== + dependencies: + performance-now "^2.1.0" + react-dom@^18.2.0: version "18.2.0" resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.2.0.tgz#22aaf38708db2674ed9ada224ca4aa708d821e3d" @@ -1733,7 +1824,7 @@ readdirp@~3.6.0: dependencies: picomatch "^2.2.1" -regenerator-runtime@^0.13.11: +regenerator-runtime@^0.13.11, regenerator-runtime@^0.13.7: version "0.13.11" resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz#f6dca3e7ceec20590d07ada785636a90cdca17f9" integrity sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg== @@ -1820,6 +1911,11 @@ reusify@^1.0.4: resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== +rgbcolor@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/rgbcolor/-/rgbcolor-1.0.1.tgz#d6505ecdb304a6595da26fa4b43307306775945d" + integrity sha512-9aZLIrhRaD97sgVhtJOW6ckOEh6/GnvQtdVNfdZ6s67+3/XwLS9lBcQYzEEhYVeUowN7pRzMLsyGhK2i/xvWbw== + rollup@^3.10.0: version "3.18.0" resolved "https://registry.yarnpkg.com/rollup/-/rollup-3.18.0.tgz#2354ba63ba66d6a09c652c3ea0dbcd9dad72bbde" @@ -1868,6 +1964,11 @@ space-separated-tokens@^2.0.0: resolved "https://registry.yarnpkg.com/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz#1ecd9d2350a3844572c3f4a312bceb018348859f" integrity sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q== +stackblur-canvas@^2.0.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/stackblur-canvas/-/stackblur-canvas-2.5.0.tgz#aa87bbed1560fdcd3138fff344fc6a1c413ebac4" + integrity sha512-EeNzTVfj+1In7aSLPKDD03F/ly4RxEuF/EX0YcOG0cKoPXs+SLZxDawQbexQDBzwROs4VKLWTOaZQlZkGBFEIQ== + style-to-object@^0.4.0: version "0.4.1" resolved "https://registry.yarnpkg.com/style-to-object/-/style-to-object-0.4.1.tgz#53cf856f7cf7f172d72939d9679556469ba5de37" @@ -1892,6 +1993,11 @@ supports-preserve-symlinks-flag@^1.0.0: resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== +svg-pathdata@^6.0.3: + version "6.0.3" + resolved "https://registry.yarnpkg.com/svg-pathdata/-/svg-pathdata-6.0.3.tgz#80b0e0283b652ccbafb69ad4f8f73e8d3fbf2cac" + integrity sha512-qsjeeq5YjBZ5eMdFuUa4ZosMLxgr5RZ+F+Y1OrDhuOCEInRMA3x74XdBtggJcj9kOeInz0WE+LgCPDkZFlBYJw== + tailwindcss@^3.2.7: version "3.2.7" resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-3.2.7.tgz#5936dd08c250b05180f0944500c01dce19188c07" @@ -1921,6 +2027,13 @@ tailwindcss@^3.2.7: quick-lru "^5.1.1" resolve "^1.22.1" +text-segmentation@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/text-segmentation/-/text-segmentation-1.0.3.tgz#52a388159efffe746b24a63ba311b6ac9f2d7943" + integrity sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw== + dependencies: + utrie "^1.0.2" + to-fast-properties@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" @@ -2038,6 +2151,13 @@ util-deprecate@^1.0.2: resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== +utrie@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/utrie/-/utrie-1.0.2.tgz#d42fe44de9bc0119c25de7f564a6ed1b2c87a645" + integrity sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw== + dependencies: + base64-arraybuffer "^1.0.2" + uvu@^0.5.0: version "0.5.6" resolved "https://registry.yarnpkg.com/uvu/-/uvu-0.5.6.tgz#2754ca20bcb0bb59b64e9985e84d2e81058502df"