mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-12-16 16:30:15 +01:00
adds markdown support, fix code wrapping
This commit is contained in:
parent
c58a9bbe93
commit
fd01fd3334
9 changed files with 1545 additions and 115 deletions
|
|
@ -37,7 +37,8 @@ Currently, this project is only functional with the `text-davinci-003` model.
|
|||
- [x] AI Model Selection
|
||||
- [x] Bing AI integration
|
||||
- [x] Remember last selected model
|
||||
- [ ] Highlight.js for code blocks
|
||||
- [x] Highlight.js for code blocks
|
||||
- [ ] Markdown handling
|
||||
- [ ] 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
|
||||
|
|
|
|||
1448
package-lock.json
generated
1448
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -40,9 +40,11 @@
|
|||
"openai": "^3.1.0",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-markdown": "^8.0.5",
|
||||
"react-redux": "^8.0.5",
|
||||
"react-textarea-autosize": "^8.4.0",
|
||||
"react-transition-group": "^4.4.5",
|
||||
"remark-supersub": "^1.0.0",
|
||||
"swr": "^2.0.3",
|
||||
"tailwind-merge": "^1.9.1",
|
||||
"tailwindcss-animate": "^1.0.5",
|
||||
|
|
|
|||
|
|
@ -1,71 +0,0 @@
|
|||
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;
|
||||
}
|
||||
|
|
@ -1,10 +1,9 @@
|
|||
import React from 'react';
|
||||
// import '~/atom-one-dark.css';
|
||||
|
||||
export default function Embed({ children, language = ''}) {
|
||||
return (
|
||||
<pre>
|
||||
<div className="mb-2 rounded-md bg-black">
|
||||
<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 rounded-tl-md rounded-tr-md">
|
||||
<span className="">{ language }</span>
|
||||
<button className="ml-auto flex gap-2">
|
||||
|
|
|
|||
|
|
@ -1,50 +1,60 @@
|
|||
import React from 'react';
|
||||
import ReactMarkdown from 'react-markdown';
|
||||
import supersub from 'remark-supersub'
|
||||
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'
|
||||
];
|
||||
import { languages, wrapperRegex } from '~/utils';
|
||||
const { codeRegex, inLineRegex, matchRegex, languageMatch, newLineMatch } = wrapperRegex;
|
||||
|
||||
const inLineWrap = (parts) =>
|
||||
parts.map((part, i) => {
|
||||
// original function
|
||||
// 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;
|
||||
// return part.includes('`') ? part : <ReactMarkdown key={i}>{part}</ReactMarkdown>;
|
||||
// }
|
||||
// });
|
||||
|
||||
const inLineWrap = (parts) => {
|
||||
let previousElement = null;
|
||||
return parts.map((part, i) => {
|
||||
if (part.match(matchRegex)) {
|
||||
return <code key={i}>{part.slice(1, -1)}</code>;
|
||||
const codeElement = <code key={i}>{part.slice(1, -1)}</code>;
|
||||
if (previousElement && typeof previousElement !== 'string') {
|
||||
// Append code element as a child to previous non-code element
|
||||
previousElement = (
|
||||
<ReactMarkdown remarkPlugins={[supersub]} key={i}>
|
||||
{previousElement}
|
||||
{codeElement}
|
||||
</ReactMarkdown>
|
||||
);
|
||||
return previousElement;
|
||||
} else {
|
||||
return codeElement;
|
||||
}
|
||||
} else {
|
||||
// return <p key={i}>{part}</p>;
|
||||
return part;
|
||||
previousElement = part;
|
||||
return previousElement;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export default function CodeWrapper({ text }) {
|
||||
if (text.includes('```')) {
|
||||
export default function TextWrapper({ text }) {
|
||||
// append triple backticks to the end of the text if only singular found and language found
|
||||
if (text.match(/```/g)?.length === 1 && text.match(languageMatch)) {
|
||||
text += '```';
|
||||
}
|
||||
|
||||
if (text.match(codeRegex)) {
|
||||
// if (text.includes('```')) {
|
||||
// const parts = text.split(codeRegex);
|
||||
|
||||
const parts = regexSplit(text);
|
||||
console.log(parts);
|
||||
// console.log(parts);
|
||||
const codeParts = parts.map((part, i) => {
|
||||
if (part.match(codeRegex)) {
|
||||
let language = 'javascript';
|
||||
|
|
@ -73,7 +83,8 @@ export default function CodeWrapper({ text }) {
|
|||
const innerParts = part.split(inLineRegex);
|
||||
return inLineWrap(innerParts);
|
||||
} else {
|
||||
return part;
|
||||
// return part;
|
||||
return <ReactMarkdown remarkPlugins={[supersub]} key={i}>{part}</ReactMarkdown>;
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,10 @@
|
|||
outline: 1px solid limegreen !important;
|
||||
} */
|
||||
|
||||
blockquote, dd, dl, fieldset, figure, h1, h2, h3, h4, h5, h6, hr, p, pre {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.scroll-down-enter {
|
||||
opacity: 0;
|
||||
}
|
||||
|
|
@ -242,7 +246,7 @@
|
|||
.prose :where(code):not(:where([class~=not-prose] *)) {
|
||||
color:var(--tw-prose-code);
|
||||
font-size:.875em;
|
||||
font-weight:600
|
||||
font-weight:600;
|
||||
}
|
||||
.prose :where(code):not(:where([class~=not-prose] *)):before {
|
||||
content:"`"
|
||||
|
|
|
|||
|
|
@ -3,4 +3,42 @@ import { twMerge } from 'tailwind-merge';
|
|||
|
||||
export function cn(...inputs) {
|
||||
return twMerge(clsx(inputs));
|
||||
}
|
||||
}
|
||||
|
||||
export const languages = [
|
||||
'java',
|
||||
'c',
|
||||
'markdown',
|
||||
'css',
|
||||
'html',
|
||||
'xml',
|
||||
'bash',
|
||||
'json',
|
||||
'yaml',
|
||||
'jsx',
|
||||
'python',
|
||||
'c++',
|
||||
'javascript',
|
||||
'csharp',
|
||||
'php',
|
||||
'typescript',
|
||||
'swift',
|
||||
'objectivec',
|
||||
'sql',
|
||||
'r',
|
||||
'kotlin',
|
||||
'ruby',
|
||||
'go',
|
||||
'x86asm',
|
||||
'matlab',
|
||||
'perl',
|
||||
'pascal'
|
||||
];
|
||||
|
||||
export const wrapperRegex = {
|
||||
codeRegex: /(```[\s\S]*?```)/g,
|
||||
inLineRegex: /(`[^`]+?`)/g,
|
||||
matchRegex: /(`[^`]+?`)/g,
|
||||
languageMatch: /^```(\w+)/,
|
||||
newLineMatch: /^```(\n+)/
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
// 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) {
|
||||
|
|
@ -8,8 +7,7 @@ export default function regexSplit(string) {
|
|||
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\`\`\``);
|
||||
output.push(`\`\`\`${language}\n${code}\n\`\`\``);
|
||||
if (i < matches.length - 1) {
|
||||
const nextText = string.slice(matches[i].index + fullMatch.length, matches[i + 1].index);
|
||||
output.push(nextText.trim());
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue