markdown library change

This commit is contained in:
Daniel Avila 2023-03-19 01:14:19 -04:00
parent d56aa2edef
commit 0b47218cd5
13 changed files with 4838 additions and 63 deletions

View file

@ -30,7 +30,7 @@ const createOnProgress = () => {
tokens = citeText(tokens, true);
}
sendMessage(res, { text: tokens, message: true, initial: i === 0, ...rest });
sendMessage(res, { text: tokens + '█', message: true, initial: i === 0, ...rest });
i++;
};

2528
client/package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -36,9 +36,14 @@
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-lazy-load": "^4.0.1",
"react-markdown": "^8.0.5",
"react-redux": "^8.0.5",
"react-textarea-autosize": "^8.4.0",
"react-transition-group": "^4.4.5",
"rehype-highlight": "^6.0.0",
"rehype-katex": "^6.0.2",
"remark-gfm": "^3.0.1",
"remark-math": "^5.1.1",
"swr": "^2.0.3",
"tailwind-merge": "^1.9.1",
"tailwindcss-animate": "^1.0.5",

View file

@ -45,7 +45,7 @@ const App = () => {
<div className="flex h-full w-full flex-1 flex-col bg-gray-50 md:pl-[260px]">
<div className="transition-width relative flex h-full w-full flex-1 flex-col items-stretch overflow-hidden bg-white dark:bg-gray-800">
<MobileNav setNavVisible={setNavVisible} />
{messages.length === 0 ? (
{messages.length === 0 && title.toLowerCase() === 'chatgpt clone' ? (
<Landing title={title} />
) : (
<Messages

View file

@ -0,0 +1,57 @@
import React, { useRef, useState } from 'react';
import Clipboard from '../svg/Clipboard';
import CheckMark from '../svg/CheckMark';
const CodeBlock = ({ lang, codeChildren }) => {
const codeRef = useRef(null);
return (
<div className="rounded-md bg-black">
<CodeBar
lang={lang}
codeRef={codeRef}
/>
<div className="overflow-y-auto p-4">
<code
ref={codeRef}
className={`hljs !whitespace-pre language-${lang}`}
>
{codeChildren}
</code>
</div>
</div>
);
};
const CodeBar = React.memo(({ lang, codeRef }) => {
const [isCopied, setIsCopied] = useState(false);
return (
<div className="relative flex items-center rounded-tl-md rounded-tr-md bg-gray-800 px-4 py-2 font-sans text-xs text-gray-200">
<span className="">{lang}</span>
<button
className="ml-auto flex gap-2"
onClick={async () => {
const codeString = codeRef.current?.textContent;
if (codeString)
navigator.clipboard.writeText(codeString).then(() => {
setIsCopied(true);
setTimeout(() => setIsCopied(false), 3000);
});
}}
>
{isCopied ? (
<>
<CheckMark />
Copied!
</>
) : (
<>
<Clipboard />
Copy code
</>
)}
</button>
</div>
);
});
export default CodeBlock;

View file

@ -0,0 +1,71 @@
import React from 'react';
import ReactMarkdown from 'react-markdown';
import rehypeKatex from 'rehype-katex';
import rehypeHighlight from 'rehype-highlight';
import remarkMath from 'remark-math';
import remarkGfm from 'remark-gfm';
import CodeBlock from './CodeBlock';
import { langSubset } from '~/utils/languages';
const Content = React.memo(({ content }) => {
return (
<>
<ReactMarkdown
remarkPlugins={[remarkGfm, [remarkMath, { singleDollarTextMath: false }]]}
rehypePlugins={[
[rehypeKatex, { output: 'mathml' }],
[
rehypeHighlight,
{
detect: true,
ignoreMissing: true,
subset: langSubset
}
]
]}
linkTarget="_new"
components={{
code,
p,
text: blinker,
// li,
// ul,
// ol
}}
>
{content}
</ReactMarkdown>
</>
);
});
const code = React.memo((props) => {
const { inline, className, children } = props;
const match = /language-(\w+)/.exec(className || '');
const lang = match && match[1];
if (inline) {
return <code className={className}>{children}</code>;
} else {
return (
<CodeBlock
lang={lang || 'text'}
codeChildren={children}
/>
);
}
});
const p = React.memo((props) => {
return <p className="whitespace-pre-wrap ">{props?.children}</p>;
});
const blinker = ({ node }) => {
if (node.type === 'text' && node.value === '█') {
return <span className="result-streaming">{node.value}</span>
}
return null;
}
export default Content;

View file

@ -2,7 +2,7 @@ import React, { useState } from 'react';
import Clipboard from '../svg/Clipboard';
import CheckMark from '../svg/CheckMark';
export default function Embed({ children, language = '', code, matched }) {
export default function Embed({ children, lang = '', code, matched }) {
const [buttonText, setButtonText] = useState('Copy code');
const isClicked = buttonText === 'Copy code';
@ -18,7 +18,7 @@ export default function Embed({ children, language = '', code, matched }) {
<pre>
<div className="mb-4 rounded-md bg-black">
<div className="relative flex items-center rounded-tl-md rounded-tr-md bg-gray-800 px-4 py-2 font-sans text-xs text-gray-200">
<span className="">{language === 'javascript' && !matched ? '' : language}</span>
<span className="">{lang === 'javascript' && !matched ? '' : lang}</span>
<button
className="ml-auto flex gap-2"
onClick={clickHandler}

View file

@ -1,6 +1,6 @@
import React, { useState, useEffect } from 'react';
import hljs from 'highlight.js';
import languages from '~/utils/languages';
import { languages } from '~/utils/languages';
export default function Highlight({language, code}) {
const [highlightedCode, setHighlightedCode] = useState(code);

View file

@ -1,5 +1,6 @@
import React, { useState, useEffect, useRef, useCallback } from 'react';
import TextWrapper from './TextWrapper';
import Content from './Content';
import MultiMessage from './MultiMessage';
import { useSelector, useDispatch } from 'react-redux';
import HoverButtons from './HoverButtons';
@ -131,7 +132,7 @@ export default function Message({
<div
{...props}
onWheel={handleWheel}
// onClick={clickSearchResult}
onClick={clickSearchResult}
>
<div className="relative m-auto flex gap-4 p-4 text-base md:max-w-2xl md:gap-6 md:py-6 lg:max-w-2xl lg:px-0 xl:max-w-3xl">
<div className="relative flex h-[30px] w-[30px] flex-col items-end text-right text-xs md:text-sm">
@ -188,14 +189,15 @@ export default function Message({
<div className="flex min-h-[20px] flex-grow flex-col items-start gap-4 whitespace-pre-wrap">
{/* <div className={`${blinker ? 'result-streaming' : ''} markdown prose dark:prose-invert light w-full break-words`}> */}
<div className="markdown prose dark:prose-invert light w-full break-words">
{!isCreatedByUser && !searchResult ? (
{/* {!isCreatedByUser && !searchResult ? (
<TextWrapper
text={text}
generateCursor={generateCursor}
/>
) : (
text
)}
)} */}
<Content content={text} generateCursor={generateCursor}/>
</div>
</div>
)}

View file

@ -73,7 +73,7 @@ export default function Messages({ messages, messageTree }) {
<div className="flex w-full items-center justify-center gap-1 border-b border-black/10 bg-gray-50 p-3 text-sm text-gray-500 dark:border-gray-900/50 dark:bg-gray-700 dark:text-gray-300">
Model: {modelName} {customModel ? `(${customModel})` : null}
</div>
{messageTree.length === 0 ? (
{(messageTree.length === 0) ? (
<Spinner />
) : (
<>

1885
client/src/style copy.css Normal file

File diff suppressed because it is too large Load diff

View file

@ -118,6 +118,237 @@ blockquote, dd, dl, fieldset, figure, h1, h2, h3, h4, h5, h6, hr, p, pre {
}
}
@layer base {
* {
box-sizing: border-box;
}
body,
html {
height: 100%;
}
body {
line-height: inherit;
margin: 0;
}
.dark body,
.dark html {
--tw-bg-opacity: 1;
background-color: rgba(52, 53, 65, var(--tw-bg-opacity));
}
#root {
height: 100%;
}
img {
@apply inline-block;
}
input[type='range']::-webkit-slider-thumb {
-webkit-appearance: none;
@apply w-4;
@apply h-4;
@apply rounded-full;
background: rgba(16, 163, 127);
}
::-webkit-scrollbar {
height: 1rem;
width: 0.5rem;
}
@media screen and (min-width: 768px) {
.scrollbar-trigger ::-webkit-scrollbar-thumb {
visibility: hidden;
}
}
@media screen and (max-width: 768px) {
::-webkit-scrollbar {
display: none;
scrollbar-width: none; /* Firefox */
}
}
.hide-scroll-bar::-webkit-scrollbar {
display: none;
scrollbar-width: none; /* Firefox */
}
.dark::-webkit-scrollbar-thumb {
--tw-bg-opacity: 1;
background-color: rgba(86, 88, 105, var(--tw-bg-opacity));
}
::-webkit-scrollbar-thumb {
--tw-border-opacity: 1;
background-color: rgba(217, 217, 227, 0.8);
border-color: rgba(255, 255, 255, var(--tw-border-opacity));
border-radius: 9999px;
border-width: 1px;
}
::-webkit-scrollbar-track {
background-color: transparent;
border-radius: 9999px;
}
pre ::-webkit-scrollbar-thumb {
display: none;
}
pre {
scrollbar-width: 0;
}
textarea:focus {
outline: none;
}
a.link {
@apply underline dark:hover:text-white hover:text-black;
}
}
@layer components {
/* .markdown ol,
.markdown ul {
display: flex;
flex-direction: column;
padding-left: 1rem;
} */
.markdown ol li,
.markdown ol li > p,
.markdown ol ol,
.markdown ol ul,
.markdown ul li,
.markdown ul li > p,
.markdown ul ol,
.markdown ul ul {
margin: 0;
}
.markdown ul li:before {
content: '•';
font-size: 0.875rem;
line-height: 1.25rem;
margin-left: -1rem;
margin-top: 1.55rem;
/* position: absolute; */
}
}
.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
}
/* :not(pre) > code.hljs,
:not(pre) > code[class*='language-'] {
border-radius: 0.3em;
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;
} */
.prose {
color:var(--tw-prose-body);
max-width:65ch
@ -125,7 +356,7 @@ blockquote, dd, dl, fieldset, figure, h1, h2, h3, h4, h5, h6, hr, p, pre {
.prose :where([class~=lead]):not(:where([class~=not-prose] *)) {
color:var(--tw-prose-lead);
font-size:1.25em;
line-height:1.6;
/* line-height:1.6; */
margin-bottom:1.2em;
margin-top:1.2em
}
@ -186,13 +417,13 @@ blockquote, dd, dl, fieldset, figure, h1, h2, h3, h4, h5, h6, hr, p, pre {
margin-top:1.25em;
padding-left:1.625em
}
.prose :where(ol>li):not(:where([class~=not-prose] *))::marker {
/* .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;
@ -245,7 +476,7 @@ blockquote, dd, dl, fieldset, figure, h1, h2, h3, h4, h5, h6, hr, p, pre {
color:var(--tw-prose-headings);
font-size:1.25em;
font-weight:600;
line-height:1.6;
/* line-height:1.6; */
margin-bottom:.6em;
margin-top:1.6em
}
@ -256,7 +487,7 @@ blockquote, dd, dl, fieldset, figure, h1, h2, h3, h4, h5, h6, hr, p, pre {
.prose :where(h4):not(:where([class~=not-prose] *)) {
color:var(--tw-prose-headings);
font-weight:600;
line-height:1.5;
/* line-height:1.5; */
margin-bottom:.5em;
margin-top:1.5em
}
@ -275,7 +506,7 @@ blockquote, dd, dl, fieldset, figure, h1, h2, h3, h4, h5, h6, hr, p, pre {
.prose :where(figcaption):not(:where([class~=not-prose] *)) {
color:var(--tw-prose-captions);
font-size:.875em;
line-height:1.4285714;
/* line-height:1.4285714; */
margin-top:.8571429em
}
.prose :where(code):not(:where([class~=not-prose] *)) {
@ -318,7 +549,7 @@ blockquote, dd, dl, fieldset, figure, h1, h2, h3, h4, h5, h6, hr, p, pre {
color:currentColor;
font-size:.875em;
font-weight:400;
line-height:1.7142857;
/* line-height:1.7142857; */
margin:0;
overflow-x:auto;
padding:0
@ -342,7 +573,7 @@ blockquote, dd, dl, fieldset, figure, h1, h2, h3, h4, h5, h6, hr, p, pre {
}
.prose :where(table):not(:where([class~=not-prose] *)) {
font-size:.875em;
line-height:1.7142857;
/* line-height:1.7142857; */
margin-bottom:2em;
margin-top:2em;
table-layout:auto;
@ -378,46 +609,10 @@ blockquote, dd, dl, fieldset, figure, h1, h2, h3, h4, h5, h6, hr, p, pre {
.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] *)) {
/* .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
@ -941,6 +1136,8 @@ html {
display: flex;
flex-direction: column;
padding-left: 1rem;
margin-top: 0;
margin-bottom: 0;
}
.markdown ol {
@ -1796,7 +1993,11 @@ html {
.markdown ul {
display:flex;
flex-direction:column;
padding-left:1rem
padding-left:1rem;
margin: 0;
}
.markdown ul::marker {
margin-top: 1.55rem;
}
.markdown ol li,
.markdown ol li>p,

View file

@ -315,4 +315,42 @@ const languages = new Set([
'zephir',
]);
module.exports = languages;
const langSubset = [
'python',
'javascript',
'java',
'go',
'bash',
'c',
'cpp',
'csharp',
'css',
'diff',
'graphql',
'json',
'kotlin',
'less',
'lua',
'makefile',
'markdown',
'objectivec',
'perl',
'php',
'php-template',
'plaintext',
'python-repl',
'r',
'ruby',
'rust',
'scss',
'shell',
'sql',
'swift',
'typescript',
'vbnet',
'wasm',
'xml',
'yaml',
];
module.exports = { languages, langSubset };