Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
chenpangpang
open-webui
Commits
92e77d7b
Commit
92e77d7b
authored
Aug 09, 2024
by
Timothy J. Baek
Browse files
refac: katex
parent
3b370bbc
Changes
8
Show whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
147 additions
and
12 deletions
+147
-12
package-lock.json
package-lock.json
+18
-0
package.json
package.json
+1
-0
src/lib/components/chat/Messages/KatexRenderer.svelte
src/lib/components/chat/Messages/KatexRenderer.svelte
+9
-0
src/lib/components/chat/Messages/MarkdownInlineTokens.svelte
src/lib/components/chat/Messages/MarkdownInlineTokens.svelte
+13
-3
src/lib/components/chat/Messages/MarkdownTokens.svelte
src/lib/components/chat/Messages/MarkdownTokens.svelte
+13
-1
src/lib/components/chat/Messages/ResponseMessage.svelte
src/lib/components/chat/Messages/ResponseMessage.svelte
+10
-6
src/lib/utils/index.ts
src/lib/utils/index.ts
+3
-2
src/lib/utils/katex-extension.ts
src/lib/utils/katex-extension.ts
+80
-0
No files found.
package-lock.json
View file @
92e77d7b
...
...
@@ -29,6 +29,7 @@
"js-sha256"
:
"^0.10.1"
,
"katex"
:
"^0.16.9"
,
"marked"
:
"^9.1.0"
,
"marked-katex-extension"
:
"^5.1.1"
,
"mermaid"
:
"^10.9.1"
,
"pyodide"
:
"^0.26.1"
,
"socket.io-client"
:
"^4.2.0"
,
...
...
@@ -1544,6 +1545,11 @@
"integrity"
:
"sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="
,
"dev"
:
true
},
"node_modules/@types/katex"
:
{
"version"
:
"0.16.7"
,
"resolved"
:
"https://registry.npmjs.org/@types/katex/-/katex-0.16.7.tgz"
,
"integrity"
:
"sha512-HMwFiRujE5PjrgwHQ25+bsLJgowjGjm5Z8FVSf0N6PwgJrwxH0QxzHYDcKsTfV3wva0vzrpqMTJS2jXPr5BMEQ=="
},
"node_modules/@types/mdast"
:
{
"version"
:
"3.0.15"
,
"resolved"
:
"https://registry.npmjs.org/@types/mdast/-/mdast-3.0.15.tgz"
,
...
...
@@ -6036,6 +6042,18 @@
"node"
:
">= 16"
}
},
"node_modules/marked-katex-extension"
:
{
"version"
:
"5.1.1"
,
"resolved"
:
"https://registry.npmjs.org/marked-katex-extension/-/marked-katex-extension-5.1.1.tgz"
,
"integrity"
:
"sha512-piquiCyZpZ1aiocoJlJkRXr+hkk5UI4xw9GhRZiIAAgvX5rhzUDSJ0seup1JcsgueC8MLNDuqe5cRcAzkFE42Q=="
,
"dependencies"
:
{
"@types/katex"
:
"^0.16.7"
},
"peerDependencies"
:
{
"katex"
:
">=0.16 <0.17"
,
"marked"
:
">=4 <15"
}
},
"node_modules/matcher-collection"
:
{
"version"
:
"2.0.1"
,
"resolved"
:
"https://registry.npmjs.org/matcher-collection/-/matcher-collection-2.0.1.tgz"
,
...
...
package.json
View file @
92e77d7b
...
...
@@ -70,6 +70,7 @@
"
js-sha256
"
:
"
^0.10.1
"
,
"
katex
"
:
"
^0.16.9
"
,
"
marked
"
:
"
^9.1.0
"
,
"
marked-katex-extension
"
:
"
^5.1.1
"
,
"
mermaid
"
:
"
^10.9.1
"
,
"
pyodide
"
:
"
^0.26.1
"
,
"
socket.io-client
"
:
"
^4.2.0
"
,
...
...
src/lib/components/chat/Messages/KatexRenderer.svelte
0 → 100644
View file @
92e77d7b
<script lang="ts">
import katex from 'katex';
import 'katex/contrib/mhchem';
export let content: string;
export let displayMode: boolean = false;
</script>
{@html katex.renderToString(content, { displayMode, throwOnError: false })}
src/lib/components/chat/Messages/MarkdownInlineTokens.svelte
View file @
92e77d7b
<script lang="ts">
import type { Token } from 'marked';
import { unescapeHtml } from '$lib/utils';
import {
revertSanitizedResponseContent,
unescapeHtml } from '$lib/utils';
import { onMount } from 'svelte';
import Image from '$lib/components/common/Image.svelte';
import KatexRenderer from './KatexRenderer.svelte';
export let id: string;
export let tokens: Token[];
</script>
...
...
@@ -25,14 +28,21 @@
<svelte:self id={`${id}-em`} tokens={token.tokens} />
</em>
{:else if token.type === 'codespan'}
<code class="codespan">{
unescapeHtml(token.text.replaceAll('&', '&')
)}</code>
<code class="codespan">{
revertSanitizedResponseContent(token.raw
)}</code>
{:else if token.type === 'br'}
<br />
{:else if token.type === 'del'}
<del>
<svelte:self id={`${id}-del`} tokens={token.tokens} />
</del>
{:else if token.type === 'inlineKatex'}
{#if token.text}
<KatexRenderer
content={revertSanitizedResponseContent(token.text)}
displayMode={token?.displayMode ?? false}
/>
{/if}
{:else if token.type === 'text'}
{
unescapeHtml(
token.
text)
}
{token.
raw
}
{/if}
{/each}
src/lib/components/chat/Messages/MarkdownTokens.svelte
View file @
92e77d7b
<script lang="ts">
import { onMount } from 'svelte';
import type { Token } from 'marked';
import { revertSanitizedResponseContent, unescapeHtml } from '$lib/utils';
import CodeBlock from '$lib/components/chat/Messages/CodeBlock.svelte';
import { onMount } from 'svelte';
import MarkdownInlineTokens from '$lib/components/chat/Messages/MarkdownInlineTokens.svelte';
import KatexRenderer from './KatexRenderer.svelte';
export let id: string;
export let tokens: Token[];
export let top = true;
const headerComponent = (depth: number) => {
return 'h' + depth;
};
</script>
<!-- {JSON.stringify(tokens)} -->
{#each tokens as token, tokenIdx}
{#if token.type === 'hr'}
<hr />
...
...
@@ -104,6 +109,13 @@
{:else}
{unescapeHtml(token.text)}
{/if}
{:else if token.type === 'inlineKatex'}
{#if token.text}
<KatexRenderer
content={revertSanitizedResponseContent(token.text)}
displayMode={token?.displayMode ?? false}
/>
{/if}
{:else if token.type === 'space'}
{''}
{:else}
...
...
src/lib/components/chat/Messages/ResponseMessage.svelte
View file @
92e77d7b
...
...
@@ -4,7 +4,6 @@
import { marked } from 'marked';
import tippy from 'tippy.js';
import auto_render from 'katex/dist/contrib/auto-render.mjs';
import 'katex/dist/katex.min.css';
import mermaid from 'mermaid';
import { fade } from 'svelte/transition';
...
...
@@ -79,19 +78,24 @@
let tokens;
import 'katex/dist/katex.min.css';
import markedKatex from '$lib/utils/katex-extension';
const options = {
throwOnError: false
};
marked.use(markedKatex(options));
$: (async () => {
if (message?.content) {
tokens = marked.lexer(
replaceTokens(sanitizeResponseContent(message?.content), model?.name, $user?.name)
);
// console.log(message?.content, tokens);
}
})();
$: if (message) {
renderStyling();
}
const renderStyling = async () => {
await tick();
...
...
src/lib/utils/index.ts
View file @
92e77d7b
...
...
@@ -25,7 +25,8 @@ const convertLatexToSingleLine = (content) => {
export
const
sanitizeResponseContent
=
(
content
:
string
)
=>
{
// replace single backslash with double backslash
content
=
content
.
replace
(
/
\\
/g
,
'
\\\\
'
);
content
=
content
.
replace
(
/
\\\\
/g
,
'
\\\\\\\\
'
);
content
=
convertLatexToSingleLine
(
content
);
// First, temporarily replace valid <video> tags with a placeholder
...
...
@@ -87,7 +88,7 @@ export const replaceTokens = (content, char, user) => {
};
export
const
revertSanitizedResponseContent
=
(
content
:
string
)
=>
{
return
content
.
replaceAll
(
'
<
'
,
'
<
'
).
replaceAll
(
'
>
'
,
'
>
'
);
return
content
.
replaceAll
(
'
<
'
,
'
<
'
).
replaceAll
(
'
>
'
,
'
>
'
)
.
replaceAll
(
'
\\\\
'
,
'
\\
'
)
;
};
export
function
unescapeHtml
(
html
:
string
)
{
...
...
src/lib/utils/katex-extension.ts
0 → 100644
View file @
92e77d7b
import
katex
from
'
katex
'
;
const
inlineRule
=
/^
(\${1,2})(?!\$)((?:\\
.|
[^\\\n])
*
?(?:\\
.|
[^\\\n\$]))\1(?=[\s
?!
\.
,:?!。,:
]
|$
)
/
;
const
inlineRuleNonStandard
=
/^
(\${1,2})(?!\$)((?:\\
.|
[^\\\n])
*
?(?:\\
.|
[^\\\n\$]))\1
/
;
// Non-standard, even if there are no spaces before and after $ or $$, try to parse
const
blockRule
=
/^
(\${1,2})\n((?:\\[^]
|
[^\\])
+
?)\n\1(?:\n
|$
)
/
;
export
default
function
(
options
=
{})
{
return
{
extensions
:
[
inlineKatex
(
options
,
createRenderer
(
options
,
false
)),
blockKatex
(
options
,
createRenderer
(
options
,
true
)),
],
};
}
function
createRenderer
(
options
,
newlineAfter
)
{
return
(
token
)
=>
katex
.
renderToString
(
token
.
text
,
{
...
options
,
displayMode
:
token
.
displayMode
})
+
(
newlineAfter
?
'
\n
'
:
''
);
}
function
inlineKatex
(
options
,
renderer
)
{
const
nonStandard
=
options
&&
options
.
nonStandard
;
const
ruleReg
=
nonStandard
?
inlineRuleNonStandard
:
inlineRule
;
return
{
name
:
'
inlineKatex
'
,
level
:
'
inline
'
,
start
(
src
)
{
let
index
;
let
indexSrc
=
src
;
while
(
indexSrc
)
{
index
=
indexSrc
.
indexOf
(
'
$
'
);
if
(
index
===
-
1
)
{
return
;
}
const
f
=
nonStandard
?
index
>
-
1
:
index
===
0
||
indexSrc
.
charAt
(
index
-
1
)
===
'
'
;
if
(
f
)
{
const
possibleKatex
=
indexSrc
.
substring
(
index
);
if
(
possibleKatex
.
match
(
ruleReg
))
{
return
index
;
}
}
indexSrc
=
indexSrc
.
substring
(
index
+
1
).
replace
(
/^
\$
+/
,
''
);
}
},
tokenizer
(
src
,
tokens
)
{
const
match
=
src
.
match
(
ruleReg
);
if
(
match
)
{
return
{
type
:
'
inlineKatex
'
,
raw
:
match
[
0
],
text
:
match
[
2
].
trim
(),
displayMode
:
match
[
1
].
length
===
2
,
};
}
},
renderer
,
};
}
function
blockKatex
(
options
,
renderer
)
{
return
{
name
:
'
blockKatex
'
,
level
:
'
block
'
,
tokenizer
(
src
,
tokens
)
{
const
match
=
src
.
match
(
blockRule
);
if
(
match
)
{
return
{
type
:
'
blockKatex
'
,
raw
:
match
[
0
],
text
:
match
[
2
].
trim
(),
displayMode
:
match
[
1
].
length
===
2
,
};
}
},
renderer
,
};
}
\ No newline at end of file
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment