From c58a9bbe93cc4f7efc95599c2263c6818fbc68b7 Mon Sep 17 00:00:00 2001 From: Daniel Avila Date: Thu, 23 Feb 2023 23:56:55 -0500 Subject: [PATCH] finish highlight.js styling (for chatgpt) --- README.md | 4 +- package-lock.json | 109 +++-- package.json | 2 +- src/atom-one-dark.css | 72 ++- src/components/Messages/CodeWrapper.jsx | 55 --- src/components/Messages/Embed.jsx | 24 +- src/components/Messages/Highlight.jsx | 16 + src/components/Messages/Message.jsx | 20 +- src/components/Messages/TextWrapper.jsx | 87 ++++ src/style.css | 584 +++++++++++++++++++++++- src/utils/regexSplit.js | 20 + tailwind.config.js | 8 +- 12 files changed, 851 insertions(+), 150 deletions(-) delete mode 100644 src/components/Messages/CodeWrapper.jsx create mode 100644 src/components/Messages/Highlight.jsx create mode 100644 src/components/Messages/TextWrapper.jsx create mode 100644 src/utils/regexSplit.js diff --git a/README.md b/README.md index 57857e023c..22bc266040 100644 --- a/README.md +++ b/README.md @@ -36,9 +36,9 @@ Currently, this project is only functional with the `text-davinci-003` model. - [x] UI Error handling - [x] AI Model Selection - [x] Bing AI integration -- [ ] Remember last selected model -- [ ] AI model change handling (whether to pseudo-persist convos or start new convos within existing convo) +- [x] Remember last selected model - [ ] Highlight.js for code blocks +- [ ] AI model change handling (whether to pseudo-persist convos or start new convos within existing convo) - [ ] Server convo pagination (limit fetch and load more with 'show more' button) - [ ] Prompt Templates - [ ] Bing AI Styling (for suggested responses, convo end, etc.) diff --git a/package-lock.json b/package-lock.json index 667b3488ff..023d04e1cd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -28,7 +28,6 @@ "openai": "^3.1.0", "react": "^18.2.0", "react-dom": "^18.2.0", - "react-highlight": "^0.15.0", "react-redux": "^8.0.5", "react-textarea-autosize": "^8.4.0", "react-transition-group": "^4.4.5", @@ -45,6 +44,7 @@ "@babel/preset-env": "^7.20.2", "@babel/preset-react": "^7.18.6", "@babel/runtime": "^7.20.13", + "@tailwindcss/typography": "^0.5.9", "autoprefixer": "^10.4.13", "babel-loader": "^9.1.2", "babel-plugin-root-import": "^6.6.0", @@ -4096,6 +4096,34 @@ } } }, + "node_modules/@tailwindcss/typography": { + "version": "0.5.9", + "resolved": "https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.5.9.tgz", + "integrity": "sha512-t8Sg3DyynFysV9f4JDOVISGsjazNb48AeIYQwcL+Bsq5uf4RYL75C1giZ43KISjeDGBaTN3Kxh7Xj/vRSMJUUg==", + "dev": true, + "dependencies": { + "lodash.castarray": "^4.4.0", + "lodash.isplainobject": "^4.0.6", + "lodash.merge": "^4.6.2", + "postcss-selector-parser": "6.0.10" + }, + "peerDependencies": { + "tailwindcss": ">=3.0.0 || insiders" + } + }, + "node_modules/@tailwindcss/typography/node_modules/postcss-selector-parser": { + "version": "6.0.10", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz", + "integrity": "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==", + "dev": true, + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/@types/body-parser": { "version": "1.19.2", "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", @@ -9879,12 +9907,24 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, + "node_modules/lodash.castarray": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.castarray/-/lodash.castarray-4.4.0.tgz", + "integrity": "sha512-aVx8ztPv7/2ULbArGJ2Y42bG1mEQ5mGjpdvrbJcJFU3TbYybe+QlLS4pst9zV52ymy2in1KpFPiZnAOATxD4+Q==", + "dev": true + }, "node_modules/lodash.debounce": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", "dev": true }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "dev": true + }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", @@ -12221,22 +12261,6 @@ "react": "^18.2.0" } }, - "node_modules/react-highlight": { - "version": "0.15.0", - "resolved": "https://registry.npmjs.org/react-highlight/-/react-highlight-0.15.0.tgz", - "integrity": "sha512-5uV/b/N4Z421GSVVe05fz+OfTsJtFzx/fJBdafZyw4LS70XjIZwgEx3Lrkfc01W/RzZ2Dtfb0DApoaJFAIKBtA==", - "dependencies": { - "highlight.js": "^10.5.0" - } - }, - "node_modules/react-highlight/node_modules/highlight.js": { - "version": "10.7.3", - "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz", - "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==", - "engines": { - "node": "*" - } - }, "node_modules/react-is": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", @@ -17948,6 +17972,30 @@ "reselect": "^4.1.7" } }, + "@tailwindcss/typography": { + "version": "0.5.9", + "resolved": "https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.5.9.tgz", + "integrity": "sha512-t8Sg3DyynFysV9f4JDOVISGsjazNb48AeIYQwcL+Bsq5uf4RYL75C1giZ43KISjeDGBaTN3Kxh7Xj/vRSMJUUg==", + "dev": true, + "requires": { + "lodash.castarray": "^4.4.0", + "lodash.isplainobject": "^4.0.6", + "lodash.merge": "^4.6.2", + "postcss-selector-parser": "6.0.10" + }, + "dependencies": { + "postcss-selector-parser": { + "version": "6.0.10", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz", + "integrity": "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==", + "dev": true, + "requires": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + } + } + } + }, "@types/body-parser": { "version": "1.19.2", "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", @@ -22352,12 +22400,24 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, + "lodash.castarray": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.castarray/-/lodash.castarray-4.4.0.tgz", + "integrity": "sha512-aVx8ztPv7/2ULbArGJ2Y42bG1mEQ5mGjpdvrbJcJFU3TbYybe+QlLS4pst9zV52ymy2in1KpFPiZnAOATxD4+Q==", + "dev": true + }, "lodash.debounce": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", "dev": true }, + "lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "dev": true + }, "lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", @@ -23916,21 +23976,6 @@ "scheduler": "^0.23.0" } }, - "react-highlight": { - "version": "0.15.0", - "resolved": "https://registry.npmjs.org/react-highlight/-/react-highlight-0.15.0.tgz", - "integrity": "sha512-5uV/b/N4Z421GSVVe05fz+OfTsJtFzx/fJBdafZyw4LS70XjIZwgEx3Lrkfc01W/RzZ2Dtfb0DApoaJFAIKBtA==", - "requires": { - "highlight.js": "^10.5.0" - }, - "dependencies": { - "highlight.js": { - "version": "10.7.3", - "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz", - "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==" - } - } - }, "react-is": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", diff --git a/package.json b/package.json index c204baa719..7baf31855a 100644 --- a/package.json +++ b/package.json @@ -40,7 +40,6 @@ "openai": "^3.1.0", "react": "^18.2.0", "react-dom": "^18.2.0", - "react-highlight": "^0.15.0", "react-redux": "^8.0.5", "react-textarea-autosize": "^8.4.0", "react-transition-group": "^4.4.5", @@ -57,6 +56,7 @@ "@babel/preset-env": "^7.20.2", "@babel/preset-react": "^7.18.6", "@babel/runtime": "^7.20.13", + "@tailwindcss/typography": "^0.5.9", "autoprefixer": "^10.4.13", "babel-loader": "^9.1.2", "babel-plugin-root-import": "^6.6.0", diff --git a/src/atom-one-dark.css b/src/atom-one-dark.css index 5344ee3819..5f1f7db88f 100644 --- a/src/atom-one-dark.css +++ b/src/atom-one-dark.css @@ -1 +1,71 @@ -pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{color:#abb2bf;background:#282c34}.hljs-comment,.hljs-quote{color:#5c6370;font-style:italic}.hljs-doctag,.hljs-formula,.hljs-keyword{color:#c678dd}.hljs-deletion,.hljs-name,.hljs-section,.hljs-selector-tag,.hljs-subst{color:#e06c75}.hljs-literal{color:#56b6c2}.hljs-addition,.hljs-attribute,.hljs-meta .hljs-string,.hljs-regexp,.hljs-string{color:#98c379}.hljs-attr,.hljs-number,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-pseudo,.hljs-template-variable,.hljs-type,.hljs-variable{color:#d19a66}.hljs-bullet,.hljs-link,.hljs-meta,.hljs-selector-id,.hljs-symbol,.hljs-title{color:#61aeee}.hljs-built_in,.hljs-class .hljs-title,.hljs-title.class_{color:#e6c07b}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700}.hljs-link{text-decoration:underline} \ No newline at end of file +pre code.hljs { + display: block; + overflow-x: auto; + padding: 1em; +} +code.hljs { + padding: 3px 5px; +} +.hljs { + color: #abb2bf; + background: #282c34; +} +.hljs-comment, +.hljs-quote { + color: #5c6370; + font-style: italic; +} +.hljs-doctag, +.hljs-formula, +.hljs-keyword { + color: #c678dd; +} +.hljs-deletion, +.hljs-name, +.hljs-section, +.hljs-selector-tag, +.hljs-subst { + color: #e06c75; +} +.hljs-literal { + color: #56b6c2; +} +.hljs-addition, +.hljs-attribute, +.hljs-meta .hljs-string, +.hljs-regexp, +.hljs-string { + color: #98c379; +} +.hljs-attr, +.hljs-number, +.hljs-selector-attr, +.hljs-selector-class, +.hljs-selector-pseudo, +.hljs-template-variable, +.hljs-type, +.hljs-variable { + color: #d19a66; +} +.hljs-bullet, +.hljs-link, +.hljs-meta, +.hljs-selector-id, +.hljs-symbol, +.hljs-title { + color: #61aeee; +} +.hljs-built_in, +.hljs-class .hljs-title, +.hljs-title.class_ { + color: #e6c07b; +} +.hljs-emphasis { + font-style: italic; +} +.hljs-strong { + font-weight: 700; +} +.hljs-link { + text-decoration: underline; +} diff --git a/src/components/Messages/CodeWrapper.jsx b/src/components/Messages/CodeWrapper.jsx deleted file mode 100644 index ef28bf40af..0000000000 --- a/src/components/Messages/CodeWrapper.jsx +++ /dev/null @@ -1,55 +0,0 @@ -import React from 'react'; -import Embed from './Embed'; -import hljs from 'highlight.js'; -import Highlight from 'react-highlight'; - -export default function CodeWrapper({ text }) { - if (text.includes('```')) { - const codeRegex = /(```[^`]+?```)/g; - const inLineRegex = /(`[^`]+?`)/g; - const parts = text.split(codeRegex); - // console.log(parts); - const codeParts = parts.map((part, i) => { - if (part.match(codeRegex)) { - return ( - - {hljs.highlightAuto(part.slice(1, -1)).value} - {/* {part.slice(1, -1)} */} - - - ); - } else if (part.match(inLineRegex)) { - const innerParts = part.split(inLineRegex); - return innerParts.map((part, i) => { - if (part.match(inLineRegex)) { - return {part.slice(1, -1)}; - } else { - return part; - } - }); - } else { - return part; - } - }); - - return <>{codeParts}; // return the wrapped text - } else { - const matchRegex = /(`[^`]+?`)/g; - const parts = text.split(matchRegex); - // console.log('parts', parts); - - // map over the parts and wrap any text between tildes with tags - const codeParts = parts.map((part, i) => { - if (part.match(matchRegex)) { - return {part.slice(1, -1)}; - } else { - return part; - } - }); - - return <>{codeParts}; // return the wrapped text - } -} diff --git a/src/components/Messages/Embed.jsx b/src/components/Messages/Embed.jsx index 004d4a1e16..e9067addf2 100644 --- a/src/components/Messages/Embed.jsx +++ b/src/components/Messages/Embed.jsx @@ -4,8 +4,8 @@ import React from 'react'; export default function Embed({ children, language = ''}) { return (
-      
-
+
+
{ language }
diff --git a/src/components/Messages/Highlight.jsx b/src/components/Messages/Highlight.jsx new file mode 100644 index 0000000000..81a9a9e582 --- /dev/null +++ b/src/components/Messages/Highlight.jsx @@ -0,0 +1,16 @@ +import React, { useState, useEffect } from 'react'; +import hljs from 'highlight.js'; + +export default function Highlight({language, code}) { + const [highlightedCode, setHighlightedCode] = useState(code); + + useEffect(() => { + setHighlightedCode(hljs.highlight(code, { language }).value); + }, [code, language]); + + return ( +
+      
+    
+ ); +} \ No newline at end of file diff --git a/src/components/Messages/Message.jsx b/src/components/Messages/Message.jsx index a749be133a..c9527c560f 100644 --- a/src/components/Messages/Message.jsx +++ b/src/components/Messages/Message.jsx @@ -1,5 +1,5 @@ import React, { useState, useEffect } from 'react'; -import CodeWrapper from './CodeWrapper'; +import TextWrapper from './TextWrapper'; import { useSelector } from 'react-redux'; import GPTIcon from '../svg/GPTIcon'; import BingIcon from '../svg/BingIcon'; @@ -13,7 +13,8 @@ export default function Message({ }) { const { isSubmitting } = useSelector((state) => state.submit); const [abortScroll, setAbort] = useState(false); - const blinker = isSubmitting && last && sender.toLowerCase() !== 'user'; + const notUser = sender.toLowerCase() !== 'user'; + const blinker = isSubmitting && last && notUser; useEffect(() => { if (blinker && !abortScroll) { @@ -34,15 +35,12 @@ export default function Message({ 'w-full border-b border-black/10 dark:border-gray-900/50 text-gray-800 dark:text-gray-100 group dark:bg-gray-800' }; - if (sender.toLowerCase() !== 'user') { - props.className = - 'w-full border-b border-black/10 dark:border-gray-900/50 text-gray-800 dark:text-gray-100 group bg-gray-100 dark:bg-[#444654]'; - } - let icon = `${sender}:`; const isGPT = sender === 'chatgpt' || sender === 'davinci' || sender === 'GPT'; - if (sender.toLowerCase() !== 'user') { + if (notUser) { + props.className = + 'w-full border-b border-black/10 dark:border-gray-900/50 text-gray-800 dark:text-gray-100 group bg-gray-100 dark:bg-[#444654]'; icon = (
; + const wrapText = (text) => ; return (
- {wrapText(text)} + {text}
) : (
- {wrapText(text)} + {notUser ? wrapText(text) : text} {blinker && }
diff --git a/src/components/Messages/TextWrapper.jsx b/src/components/Messages/TextWrapper.jsx new file mode 100644 index 0000000000..21833cbbbd --- /dev/null +++ b/src/components/Messages/TextWrapper.jsx @@ -0,0 +1,87 @@ +import React from 'react'; +import Embed from './Embed'; +import Highlight from './Highlight'; +import regexSplit from '~/utils/regexSplit'; +// const codeRegex = /(```[^`]+?```)/g; +const codeRegex = /(```[\s\S]*?```)/g; +const inLineRegex = /(`[^`]+?`)/g; +const matchRegex = /(`[^`]+?`)/g; +const languageMatch = /^```(\w+)/; +const newLineMatch = /^```(\n+)/; +const languages = [ + 'java', + 'c', + 'python', + 'c++', + 'javascript', + 'csharp', + 'php', + 'typescript', + 'swift', + 'objectivec', + 'sql', + 'r', + 'kotlin', + 'ruby', + 'go', + 'x86asm', + 'matlab', + 'perl', + 'pascal' +]; + +const inLineWrap = (parts) => + parts.map((part, i) => { + if (part.match(matchRegex)) { + return {part.slice(1, -1)}; + } else { + // return

{part}

; + return part; + } + }); + +export default function CodeWrapper({ text }) { + if (text.includes('```')) { + // const parts = text.split(codeRegex); + const parts = regexSplit(text); + console.log(parts); + const codeParts = parts.map((part, i) => { + if (part.match(codeRegex)) { + let language = 'javascript'; + + if (part.match(languageMatch)) { + language = part.match(languageMatch)[1].toLowerCase(); + const validLanguage = languages.some((lang) => language === lang); + part = validLanguage ? part.replace(languageMatch, '```') : part; + language = validLanguage ? language : 'javascript'; + } + + part = part.replace(newLineMatch, '```'); + + return ( + + + + ); + } else if (part.match(inLineRegex)) { + const innerParts = part.split(inLineRegex); + return inLineWrap(innerParts); + } else { + return part; + } + }); + + return <>{codeParts}; // return the wrapped text + } else { + // map over the parts and wrap any text between tildes with tags + const parts = text.split(matchRegex); + const codeParts = inLineWrap(parts); + return <>{codeParts}; // return the wrapped text + } +} diff --git a/src/style.css b/src/style.css index 56af6f81e1..2b783c3fa2 100644 --- a/src/style.css +++ b/src/style.css @@ -6,28 +6,6 @@ box-sizing: border-box; outline: 1px solid limegreen !important; } */ -.prose :where(code):not(:where([class~="not-prose"] *)) { - color: var(--tw-prose-code); - font-size: .875em; - font-weight: 600; -} - -.prose :where(code):not(:where([class~="not-prose"] *))::before { - content: "`"; -} - -.prose :where(code):not(:where([class~="not-prose"] *))::after { - content: "`"; -} - -code, pre { - font-family: Söhne Mono,Monaco,Andale Mono,Ubuntu Mono,monospace !important; -} - -code, kbd, pre, samp { - font-family: Söhne Mono,Monaco,Andale Mono,Ubuntu Mono,monospace; - font-size: 1em; -} .scroll-down-enter { opacity: 0; @@ -101,6 +79,568 @@ code, kbd, pre, samp { } } +.prose { + color:var(--tw-prose-body); + max-width:65ch + } + .prose :where([class~=lead]):not(:where([class~=not-prose] *)) { + color:var(--tw-prose-lead); + font-size:1.25em; + line-height:1.6; + margin-bottom:1.2em; + margin-top:1.2em + } + .prose :where(a):not(:where([class~=not-prose] *)) { + color:var(--tw-prose-links); + font-weight:500; + text-decoration:underline + } + .prose :where(strong):not(:where([class~=not-prose] *)) { + color:var(--tw-prose-bold); + font-weight:600 + } + .prose :where(a strong):not(:where([class~=not-prose] *)) { + color:inherit + } + .prose :where(blockquote strong):not(:where([class~=not-prose] *)) { + color:inherit + } + .prose :where(thead th strong):not(:where([class~=not-prose] *)) { + color:inherit + } + .prose :where(ol):not(:where([class~=not-prose] *)) { + list-style-type:decimal; + margin-bottom:1.25em; + margin-top:1.25em; + padding-left:1.625em + } + .prose :where(ol[type=A]):not(:where([class~=not-prose] *)) { + list-style-type:upper-alpha + } + .prose :where(ol[type=a]):not(:where([class~=not-prose] *)) { + list-style-type:lower-alpha + } + .prose :where(ol[type=A s]):not(:where([class~=not-prose] *)) { + list-style-type:upper-alpha + } + .prose :where(ol[type=a s]):not(:where([class~=not-prose] *)) { + list-style-type:lower-alpha + } + .prose :where(ol[type=I]):not(:where([class~=not-prose] *)) { + list-style-type:upper-roman + } + .prose :where(ol[type=i]):not(:where([class~=not-prose] *)) { + list-style-type:lower-roman + } + .prose :where(ol[type=I s]):not(:where([class~=not-prose] *)) { + list-style-type:upper-roman + } + .prose :where(ol[type=i s]):not(:where([class~=not-prose] *)) { + list-style-type:lower-roman + } + .prose :where(ol[type="1"]):not(:where([class~=not-prose] *)) { + list-style-type:decimal + } + .prose :where(ul):not(:where([class~=not-prose] *)) { + list-style-type:disc; + margin-bottom:1.25em; + margin-top:1.25em; + padding-left:1.625em + } + .prose :where(ol>li):not(:where([class~=not-prose] *))::marker { + color:var(--tw-prose-counters); + font-weight:400 + } + .prose :where(ul>li):not(:where([class~=not-prose] *))::marker { + color:var(--tw-prose-bullets) + } + .prose :where(hr):not(:where([class~=not-prose] *)) { + border-color:var(--tw-prose-hr); + border-top-width:1px; + margin-bottom:3em; + margin-top:3em + } + .prose :where(blockquote):not(:where([class~=not-prose] *)) { + border-left-color:var(--tw-prose-quote-borders); + border-left-width:.25rem; + color:var(--tw-prose-quotes); + font-style:italic; + font-style:normal; + font-weight:500; + margin-bottom:1.6em; + margin-top:1.6em; + padding-left:1em; + quotes:"\201C""\201D""\2018""\2019" + } + .prose :where(blockquote p:first-of-type):not(:where([class~=not-prose] *)):before { + content:open-quote + } + .prose :where(blockquote p:last-of-type):not(:where([class~=not-prose] *)):after { + content:close-quote + } + .prose :where(h1):not(:where([class~=not-prose] *)) { + color:var(--tw-prose-headings); + font-size:2.25em; + font-weight:800; + line-height:1.1111111; + margin-bottom:.8888889em; + margin-top:0 + } + .prose :where(h1 strong):not(:where([class~=not-prose] *)) { + color:inherit; + font-weight:900 + } + .prose :where(h2):not(:where([class~=not-prose] *)) { + color:var(--tw-prose-headings); + font-size:1.5em; + font-weight:700; + line-height:1.3333333; + margin-bottom:1em; + margin-top:2em + } + .prose :where(h2 strong):not(:where([class~=not-prose] *)) { + color:inherit; + font-weight:800 + } + .prose :where(h3):not(:where([class~=not-prose] *)) { + color:var(--tw-prose-headings); + font-size:1.25em; + font-weight:600; + line-height:1.6; + margin-bottom:.6em; + margin-top:1.6em + } + .prose :where(h3 strong):not(:where([class~=not-prose] *)) { + color:inherit; + font-weight:700 + } + .prose :where(h4):not(:where([class~=not-prose] *)) { + color:var(--tw-prose-headings); + font-weight:600; + line-height:1.5; + margin-bottom:.5em; + margin-top:1.5em + } + .prose :where(h4 strong):not(:where([class~=not-prose] *)) { + color:inherit; + font-weight:700 + } + .prose :where(img):not(:where([class~=not-prose] *)) { + margin-bottom:2em; + margin-top:2em + } + .prose :where(figure>*):not(:where([class~=not-prose] *)) { + margin-bottom:0; + margin-top:0 + } + .prose :where(figcaption):not(:where([class~=not-prose] *)) { + color:var(--tw-prose-captions); + font-size:.875em; + line-height:1.4285714; + margin-top:.8571429em + } + .prose :where(code):not(:where([class~=not-prose] *)) { + color:var(--tw-prose-code); + font-size:.875em; + font-weight:600 + } + .prose :where(code):not(:where([class~=not-prose] *)):before { + content:"`" + } + .prose :where(code):not(:where([class~=not-prose] *)):after { + content:"`" + } + .prose :where(a code):not(:where([class~=not-prose] *)) { + color:inherit + } + .prose :where(h1 code):not(:where([class~=not-prose] *)) { + color:inherit + } + .prose :where(h2 code):not(:where([class~=not-prose] *)) { + color:inherit; + font-size:.875em + } + .prose :where(h3 code):not(:where([class~=not-prose] *)) { + color:inherit; + font-size:.9em + } + .prose :where(h4 code):not(:where([class~=not-prose] *)) { + color:inherit + } + .prose :where(blockquote code):not(:where([class~=not-prose] *)) { + color:inherit + } + .prose :where(thead th code):not(:where([class~=not-prose] *)) { + color:inherit + } + .prose :where(pre):not(:where([class~=not-prose] *)) { + background-color:transparent; + border-radius:.375rem; + color:currentColor; + font-size:.875em; + font-weight:400; + line-height:1.7142857; + margin:0; + overflow-x:auto; + padding:0 + } + .prose :where(pre code):not(:where([class~=not-prose] *)) { + background-color:transparent; + border-radius:0; + border-width:0; + color:inherit; + font-family:inherit; + font-size:inherit; + font-weight:inherit; + line-height:inherit; + padding:0 + } + .prose :where(pre code):not(:where([class~=not-prose] *)):before { + content:none + } + .prose :where(pre code):not(:where([class~=not-prose] *)):after { + content:none + } + .prose :where(table):not(:where([class~=not-prose] *)) { + font-size:.875em; + line-height:1.7142857; + margin-bottom:2em; + margin-top:2em; + table-layout:auto; + text-align:left; + width:100% + } + .prose :where(thead):not(:where([class~=not-prose] *)) { + border-bottom-color:var(--tw-prose-th-borders); + border-bottom-width:1px + } + .prose :where(thead th):not(:where([class~=not-prose] *)) { + color:var(--tw-prose-headings); + font-weight:600; + padding-bottom:.5714286em; + padding-left:.5714286em; + padding-right:.5714286em; + vertical-align:bottom + } + .prose :where(tbody tr):not(:where([class~=not-prose] *)) { + border-bottom-color:var(--tw-prose-td-borders); + border-bottom-width:1px + } + .prose :where(tbody tr:last-child):not(:where([class~=not-prose] *)) { + border-bottom-width:0 + } + .prose :where(tbody td):not(:where([class~=not-prose] *)) { + vertical-align:baseline + } + .prose :where(tfoot):not(:where([class~=not-prose] *)) { + border-top-color:var(--tw-prose-th-borders); + border-top-width:1px + } + .prose :where(tfoot td):not(:where([class~=not-prose] *)) { + vertical-align:top + } + .prose { + --tw-prose-body:#374151; + --tw-prose-headings:#111827; + --tw-prose-lead:#4b5563; + --tw-prose-links:#111827; + --tw-prose-bold:#111827; + --tw-prose-counters:#6b7280; + --tw-prose-bullets:#d1d5db; + --tw-prose-hr:#e5e7eb; + --tw-prose-quotes:#111827; + --tw-prose-quote-borders:#e5e7eb; + --tw-prose-captions:#6b7280; + --tw-prose-code:#111827; + --tw-prose-pre-code:#e5e7eb; + --tw-prose-pre-bg:#1f2937; + --tw-prose-th-borders:#d1d5db; + --tw-prose-td-borders:#e5e7eb; + --tw-prose-invert-body:#d1d5db; + --tw-prose-invert-headings:#fff; + --tw-prose-invert-lead:#9ca3af; + --tw-prose-invert-links:#fff; + --tw-prose-invert-bold:#fff; + --tw-prose-invert-counters:#9ca3af; + --tw-prose-invert-bullets:#4b5563; + --tw-prose-invert-hr:#374151; + --tw-prose-invert-quotes:#f3f4f6; + --tw-prose-invert-quote-borders:#374151; + --tw-prose-invert-captions:#9ca3af; + --tw-prose-invert-code:#fff; + --tw-prose-invert-pre-code:#d1d5db; + --tw-prose-invert-pre-bg:rgba(0,0,0,.5); + --tw-prose-invert-th-borders:#4b5563; + --tw-prose-invert-td-borders:#374151; + font-size:1rem; + line-height:1.75 + } + .prose :where(p):not(:where([class~=not-prose] *)) { + margin-bottom:1.25em; + margin-top:1.25em + } + .prose :where(video):not(:where([class~=not-prose] *)) { + margin-bottom:2em; + margin-top:2em + } + .prose :where(figure):not(:where([class~=not-prose] *)) { + margin-bottom:2em; + margin-top:2em + } + .prose :where(li):not(:where([class~=not-prose] *)) { + margin-bottom:.5em; + margin-top:.5em + } + .prose :where(ol>li):not(:where([class~=not-prose] *)) { + padding-left:.375em + } + .prose :where(ul>li):not(:where([class~=not-prose] *)) { + padding-left:.375em + } + .prose :where(.prose>ul>li p):not(:where([class~=not-prose] *)) { + margin-bottom:.75em; + margin-top:.75em + } + .prose :where(.prose>ul>li>:first-child):not(:where([class~=not-prose] *)) { + margin-top:1.25em + } + .prose :where(.prose>ul>li>:last-child):not(:where([class~=not-prose] *)) { + margin-bottom:1.25em + } + .prose :where(.prose>ol>li>:first-child):not(:where([class~=not-prose] *)) { + margin-top:1.25em + } + .prose :where(.prose>ol>li>:last-child):not(:where([class~=not-prose] *)) { + margin-bottom:1.25em + } + .prose :where(ul ul, + ul ol, + ol ul, + ol ol):not(:where([class~=not-prose] *)) { + margin-bottom:.75em; + margin-top:.75em + } + .prose :where(hr+*):not(:where([class~=not-prose] *)) { + margin-top:0 + } + .prose :where(h2+*):not(:where([class~=not-prose] *)) { + margin-top:0 + } + .prose :where(h3+*):not(:where([class~=not-prose] *)) { + margin-top:0 + } + .prose :where(h4+*):not(:where([class~=not-prose] *)) { + margin-top:0 + } + .prose :where(thead th:first-child):not(:where([class~=not-prose] *)) { + padding-left:0 + } + .prose :where(thead th:last-child):not(:where([class~=not-prose] *)) { + padding-right:0 + } + .prose :where(tbody td, + tfoot td):not(:where([class~=not-prose] *)) { + padding:.5714286em + } + .prose :where(tbody td:first-child, + tfoot td:first-child):not(:where([class~=not-prose] *)) { + padding-left:0 + } + .prose :where(tbody td:last-child, + tfoot td:last-child):not(:where([class~=not-prose] *)) { + padding-right:0 + } + .prose :where(.prose>:first-child):not(:where([class~=not-prose] *)) { + margin-top:0 + } + .prose :where(.prose>:last-child):not(:where([class~=not-prose] *)) { + margin-bottom:0 + } + .prose-sm :where(.prose>ul>li p):not(:where([class~=not-prose] *)) { + margin-bottom:.5714286em; + margin-top:.5714286em + } + .prose-sm :where(.prose>ul>li>:first-child):not(:where([class~=not-prose] *)) { + margin-top:1.1428571em + } + .prose-sm :where(.prose>ul>li>:last-child):not(:where([class~=not-prose] *)) { + margin-bottom:1.1428571em + } + .prose-sm :where(.prose>ol>li>:first-child):not(:where([class~=not-prose] *)) { + margin-top:1.1428571em + } + .prose-sm :where(.prose>ol>li>:last-child):not(:where([class~=not-prose] *)) { + margin-bottom:1.1428571em + } + .prose-sm :where(.prose>:first-child):not(:where([class~=not-prose] *)) { + margin-top:0 + } + .prose-sm :where(.prose>:last-child):not(:where([class~=not-prose] *)) { + margin-bottom:0 + } + .prose-base :where(.prose>ul>li p):not(:where([class~=not-prose] *)) { + margin-bottom:.75em; + margin-top:.75em + } + .prose-base :where(.prose>ul>li>:first-child):not(:where([class~=not-prose] *)) { + margin-top:1.25em + } + .prose-base :where(.prose>ul>li>:last-child):not(:where([class~=not-prose] *)) { + margin-bottom:1.25em + } + .prose-base :where(.prose>ol>li>:first-child):not(:where([class~=not-prose] *)) { + margin-top:1.25em + } + .prose-base :where(.prose>ol>li>:last-child):not(:where([class~=not-prose] *)) { + margin-bottom:1.25em + } + .prose-base :where(.prose>:first-child):not(:where([class~=not-prose] *)) { + margin-top:0 + } + .prose-base :where(.prose>:last-child):not(:where([class~=not-prose] *)) { + margin-bottom:0 + } + .prose-lg :where(.prose>ul>li p):not(:where([class~=not-prose] *)) { + margin-bottom:.8888889em; + margin-top:.8888889em + } + .prose-lg :where(.prose>ul>li>:first-child):not(:where([class~=not-prose] *)) { + margin-top:1.3333333em + } + .prose-lg :where(.prose>ul>li>:last-child):not(:where([class~=not-prose] *)) { + margin-bottom:1.3333333em + } + .prose-lg :where(.prose>ol>li>:first-child):not(:where([class~=not-prose] *)) { + margin-top:1.3333333em + } + .prose-lg :where(.prose>ol>li>:last-child):not(:where([class~=not-prose] *)) { + margin-bottom:1.3333333em + } + .prose-lg :where(.prose>:first-child):not(:where([class~=not-prose] *)) { + margin-top:0 + } + .prose-lg :where(.prose>:last-child):not(:where([class~=not-prose] *)) { + margin-bottom:0 + } + .prose-xl :where(.prose>ul>li p):not(:where([class~=not-prose] *)) { + margin-bottom:.8em; + margin-top:.8em + } + .prose-xl :where(.prose>ul>li>:first-child):not(:where([class~=not-prose] *)) { + margin-top:1.2em + } + .prose-xl :where(.prose>ul>li>:last-child):not(:where([class~=not-prose] *)) { + margin-bottom:1.2em + } + .prose-xl :where(.prose>ol>li>:first-child):not(:where([class~=not-prose] *)) { + margin-top:1.2em + } + .prose-xl :where(.prose>ol>li>:last-child):not(:where([class~=not-prose] *)) { + margin-bottom:1.2em + } + .prose-xl :where(.prose>:first-child):not(:where([class~=not-prose] *)) { + margin-top:0 + } + .prose-xl :where(.prose>:last-child):not(:where([class~=not-prose] *)) { + margin-bottom:0 + } + .prose-2xl :where(.prose>ul>li p):not(:where([class~=not-prose] *)) { + margin-bottom:.8333333em; + margin-top:.8333333em + } + .prose-2xl :where(.prose>ul>li>:first-child):not(:where([class~=not-prose] *)) { + margin-top:1.3333333em + } + .prose-2xl :where(.prose>ul>li>:last-child):not(:where([class~=not-prose] *)) { + margin-bottom:1.3333333em + } + .prose-2xl :where(.prose>ol>li>:first-child):not(:where([class~=not-prose] *)) { + margin-top:1.3333333em + } + .prose-2xl :where(.prose>ol>li>:last-child):not(:where([class~=not-prose] *)) { + margin-bottom:1.3333333em + } + .prose-2xl :where(.prose>:first-child):not(:where([class~=not-prose] *)) { + margin-top:0 + } + .prose-2xl :where(.prose>:last-child):not(:where([class~=not-prose] *)) { + margin-bottom:0 + } + +code, +pre { + font-family: Söhne Mono, Monaco, Andale Mono, Ubuntu Mono, monospace !important; +} +code[class='language-plaintext'] { + white-space: pre-line; +} +code.hljs, +code[class*='language-'], +pre[class*='language-'] { + word-wrap: normal; + background: none; + color: #fff; + -webkit-hyphens: none; + hyphens: none; + font-size: .85rem; + line-height: 1.5; + tab-size: 4; + text-align: left; + white-space: pre; + word-break: normal; + word-spacing: normal; +} +pre[class*='language-'] { + border-radius: 0.3em; + overflow: auto; +} +:not(pre) > code.hljs, +:not(pre) > code[class*='language-'] { + border-radius: 0.3em; + padding: 0.1em; + white-space: normal; +} +.hljs-comment { + color: hsla(0, 0%, 100%, 0.5); +} +.hljs-meta { + color: hsla(0, 0%, 100%, 0.6); +} +.hljs-built_in, +.hljs-class .hljs-title { + color: #e9950c; +} +.hljs-doctag, +.hljs-formula, +.hljs-keyword, +.hljs-literal { + color: #2e95d3; +} +.hljs-addition, +.hljs-attribute, +.hljs-meta-string, +.hljs-regexp, +.hljs-string { + color: #00a67d; +} +.hljs-attr, +.hljs-number, +.hljs-selector-attr, +.hljs-selector-class, +.hljs-selector-pseudo, +.hljs-template-variable, +.hljs-type, +.hljs-variable { + color: #df3079; +} +.hljs-bullet, +.hljs-link, +.hljs-selector-id, +.hljs-symbol, +.hljs-title { + color: #f22c3d; +} + .dark .dark\:md\:bg-vert-dark-gradient { background-image: linear-gradient(180deg, rgba(53, 55, 64, 0), #353740 58.85%); } diff --git a/src/utils/regexSplit.js b/src/utils/regexSplit.js new file mode 100644 index 0000000000..22ea09936d --- /dev/null +++ b/src/utils/regexSplit.js @@ -0,0 +1,20 @@ +// const string = 'Some text before\n```python\nThis is some `Python` code with ```backticks``` inside the enclosure\n```\nSome text after\n```javascript\nThis is some JavaScript code\n```\n'; +const regex = /```([^`\n]*?)\n([\s\S]*?)\n```/g; + +export default function regexSplit(string) { + const matches = [...string.matchAll(regex)]; + const output = [matches[0].input.slice(0, matches[0].index)]; + + for (let i = 0; i < matches.length; i++) { + const [fullMatch, language, code] = matches[i]; + // const formattedCode = code.replace(/`+/g, '\\`'); + const formattedCode = code; + output.push(`\`\`\`${language}\n${formattedCode}\n\`\`\``); + if (i < matches.length - 1) { + const nextText = string.slice(matches[i].index + fullMatch.length, matches[i + 1].index); + output.push(nextText.trim()); + } + } + + return output; +} diff --git a/tailwind.config.js b/tailwind.config.js index fdb432eb34..3fdf5b308c 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -5,9 +5,6 @@ module.exports = { content: ['./src/**/*.{js,jsx,ts,tsx}'], // darkMode: 'class', darkMode: ['class'], - theme: { - extend: {} - }, theme: { extend: { // fontFamily: { @@ -29,5 +26,8 @@ module.exports = { } } }, - plugins: [require('tailwindcss-animate')] + plugins: [ + require('tailwindcss-animate'), + // require('@tailwindcss/typography'), + ] };