finish highlight.js styling (for chatgpt)

This commit is contained in:
Daniel Avila 2023-02-23 23:56:55 -05:00
parent e95e22de15
commit c58a9bbe93
12 changed files with 851 additions and 150 deletions

View file

@ -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.)

109
package-lock.json generated
View file

@ -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",

View file

@ -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",

View file

@ -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}
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;
}

View file

@ -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 (
<Embed
key={i}
language="javascript"
>
{hljs.highlightAuto(part.slice(1, -1)).value}
{/* <Highlight className="!whitespace-pre">{part.slice(1, -1)}</Highlight> */}
</Embed>
);
} else if (part.match(inLineRegex)) {
const innerParts = part.split(inLineRegex);
return innerParts.map((part, i) => {
if (part.match(inLineRegex)) {
return <code key={i}>{part.slice(1, -1)}</code>;
} 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 <code> tags
const codeParts = parts.map((part, i) => {
if (part.match(matchRegex)) {
return <code key={i}>{part.slice(1, -1)}</code>;
} else {
return part;
}
});
return <>{codeParts}</>; // return the wrapped text
}
}

View file

@ -4,8 +4,8 @@ import React from 'react';
export default function Embed({ children, language = ''}) {
return (
<pre>
<div className="mb-4 rounded-md bg-black">
<div className="relative flex items-center bg-gray-800 px-4 py-2 font-sans text-xs text-gray-200">
<div className="mb-2 rounded-md bg-black">
<div className="relative flex items-center bg-gray-800 px-4 py-2 font-sans text-xs text-gray-200 rounded-tl-md rounded-tr-md">
<span className="">{ language }</span>
<button className="ml-auto flex gap-2">
<svg
@ -35,26 +35,6 @@ export default function Embed({ children, language = ''}) {
</div>
<div className="overflow-y-auto p-4">
{ children }
{/* <span className="hljs-keyword">export</span> <span className="hljs-keyword">default</span> <span className="hljs-keyword">function</span> <span className="hljs-title function_">CodeWrapper</span>(<span className="hljs-params">{ text }</span>) {
<span className="hljs-keyword">const</span> matchRegex = <span className="hljs-regexp">/(`[^`]+?`)/g</span>; <span className="hljs-comment">// regex to match backticks and text between them</span>
<span className="hljs-keyword">const</span> parts = text.<span className="hljs-title function_">split</span>(matchRegex);
<span className="hljs-variable language_">console</span>.<span className="hljs-title function_">log</span>(<span className="hljs-string">'parts'</span>, parts);
<span className="hljs-comment">// map over the parts and wrap any backticked text with &lt;code&gt; tags</span>
<span className="hljs-keyword">const</span> codeParts = parts.<span className="hljs-title function_">map</span>(<span className="hljs-function">(<span className="hljs-params">part, index</span>) =&gt;</span> {
<span className="hljs-keyword">if</span> (part.<span className="hljs-title function_">match</span>(matchRegex)) {
<>
<span className="hljs-keyword">return</span> <span className="xml"><span className="hljs-tag">&lt;<span className="hljs-name">code</span> <span className="hljs-attr">key</span>=<span className="hljs-string">{index}</span>&gt;</span>{part}<span className="hljs-tag">&lt;/<span className="hljs-name">code</span>&gt;</span></span>;
</>
} <span className="hljs-keyword">else</span> {
<>
<span className="hljs-keyword">return</span> part.<span className="hljs-title function_">trim</span>(); <span className="hljs-comment">// remove leading/trailing whitespace from non-backticked text</span>
</>
}
});
<span className="hljs-keyword">return</span> <span className="xml"><span className="hljs-tag">&lt;&gt;</span>{codeParts}<span className="hljs-tag">&lt;/&gt;</span></span>; <span className="hljs-comment">// return the wrapped text</span>
} */}
</div>
</div>
</pre>

View file

@ -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 (
<pre>
<code className={`language-${language}`} dangerouslySetInnerHTML={{__html: highlightedCode}}/>
</pre>
);
}

View file

@ -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 = (
<div
style={isGPT ? { backgroundColor: 'rgb(16, 163, 127)' } : {}}
@ -53,7 +51,7 @@ export default function Message({
);
}
const wrapText = (text) => <CodeWrapper text={text} />;
const wrapText = (text) => <TextWrapper text={text} />;
return (
<div
@ -67,13 +65,13 @@ export default function Message({
{error ? (
<div className="flex flex min-h-[20px] flex-row flex-col items-start gap-4 gap-2 whitespace-pre-wrap text-red-500">
<div className="rounded-md border border-red-500 bg-red-500/10 py-2 px-3 text-sm text-gray-600 dark:text-gray-100">
{wrapText(text)}
{text}
</div>
</div>
) : (
<div className="flex min-h-[20px] flex-col items-start gap-4 whitespace-pre-wrap">
<div className="markdown prose dark:prose-invert light w-full break-words">
{wrapText(text)}
{notUser ? wrapText(text) : text}
{blinker && <span className="result-streaming"></span>}
</div>
</div>

View file

@ -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 <code key={i}>{part.slice(1, -1)}</code>;
} else {
// return <p key={i}>{part}</p>;
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 (
<Embed
key={i}
language={language}
>
<Highlight
code={part.slice(3, -3)}
language={language}
/>
</Embed>
);
} 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 <code> tags
const parts = text.split(matchRegex);
const codeParts = inLineWrap(parts);
return <>{codeParts}</>; // return the wrapped text
}
}

View file

@ -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%);
}

20
src/utils/regexSplit.js Normal file
View file

@ -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;
}

View file

@ -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'),
]
};