@aleph-alpha/chat-kit 3.1.2 → 3.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{CkInput.vue_vue_type_script_setup_true_lang-DKzc21Yp.js → CkInput.vue_vue_type_script_setup_true_lang-LPrW7CSQ.js} +1 -1
- package/dist/chat-kit.css +1 -0
- package/dist/components/base/CkMarkdownRenderer/CkMarkdownRenderer.stories.d.ts +6 -0
- package/dist/components/base/CkMarkdownRenderer/CkMarkdownRenderer.stories.d.ts.map +1 -1
- package/dist/components/index.js +1 -1
- package/dist/composables/index.js +1 -1
- package/dist/handlers/index.js +1 -1
- package/dist/handlers/useMarkdownParser.d.ts +1 -0
- package/dist/handlers/useMarkdownParser.d.ts.map +1 -1
- package/dist/{index-BgQf4bMm.js → index-DVxCNAs3.js} +1 -1
- package/dist/index.js +3 -3
- package/dist/useChatKitLabels-Ctz1dHpx.js +28608 -0
- package/package.json +7 -3
- package/src/components/base/CkMarkdownRenderer/CkMarkdownRenderer.stories.ts +50 -0
- package/src/handlers/useMarkdownParser.spec.ts +31 -0
- package/src/handlers/useMarkdownParser.ts +70 -6
- package/dist/useChatKitLabels-CLsWWwgG.js +0 -12751
- /package/dist/{purify.es-md74IFlY.js → katex.min-md74IFlY.js} +0 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aleph-alpha/chat-kit",
|
|
3
|
-
"version": "3.1.
|
|
3
|
+
"version": "3.1.3",
|
|
4
4
|
"description": "Chat kit package for building chat interfaces with Vue",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -67,8 +67,11 @@
|
|
|
67
67
|
"dependencies": {
|
|
68
68
|
"@aleph-alpha/ui-library": "^2.8.3",
|
|
69
69
|
"dompurify": "^3.3.2",
|
|
70
|
+
"katex": "^0.17.0",
|
|
71
|
+
"rehype-katex": "^7.0.1",
|
|
70
72
|
"rehype-stringify": "^10.0.1",
|
|
71
73
|
"remark-gfm": "^4.0.1",
|
|
74
|
+
"remark-math": "^6.0.0",
|
|
72
75
|
"remark-parse": "^11.0.0",
|
|
73
76
|
"remark-rehype": "^11.1.2",
|
|
74
77
|
"unified": "^11.0.5"
|
|
@@ -95,9 +98,10 @@
|
|
|
95
98
|
"unocss-preset-animations": "^1.3.0",
|
|
96
99
|
"unocss-preset-shadcn": "^1.0.1",
|
|
97
100
|
"vite": "^7.3.2",
|
|
98
|
-
"vitest": "
|
|
101
|
+
"vitest": "3.2.6",
|
|
99
102
|
"vue": "^3.5.27",
|
|
100
|
-
"vue-tsc": "^3.2.4"
|
|
103
|
+
"vue-tsc": "^3.2.4",
|
|
104
|
+
"@aleph-alpha/tsconfig-frontend": "0.7.0"
|
|
101
105
|
},
|
|
102
106
|
"scripts": {
|
|
103
107
|
"build": "vite build && vue-tsc --project tsconfig.build.json --declaration --emitDeclarationOnly --outDir dist",
|
|
@@ -344,6 +344,56 @@ export const GfmFeatures: Story = {
|
|
|
344
344
|
parameters: { docs: { source: { code: gfmFeaturesTemplateSource } } },
|
|
345
345
|
};
|
|
346
346
|
|
|
347
|
+
const mathTemplateSource = `<script setup lang="ts">
|
|
348
|
+
import { CkMarkdownRenderer } from '@aleph-alpha/chat-kit';
|
|
349
|
+
const content = \`# Math Rendering
|
|
350
|
+
|
|
351
|
+
Inline math: $E = mc^2$
|
|
352
|
+
|
|
353
|
+
Display math:
|
|
354
|
+
|
|
355
|
+
$$
|
|
356
|
+
\\\\int_{-\\\\infty}^{\\\\infty} e^{-x^2} dx = \\\\sqrt{\\\\pi}
|
|
357
|
+
$$
|
|
358
|
+
|
|
359
|
+
The quadratic formula is $x = \\\\frac{-b \\\\pm \\\\sqrt{b^2 - 4ac}}{2a}$.
|
|
360
|
+
\`;
|
|
361
|
+
</script>
|
|
362
|
+
|
|
363
|
+
<template>
|
|
364
|
+
<CkMarkdownRenderer :content="content" />
|
|
365
|
+
</template>`;
|
|
366
|
+
|
|
367
|
+
/**
|
|
368
|
+
* Renders inline and display math expressions using KaTeX.
|
|
369
|
+
* Inline math uses single dollar signs ($...$) and display math uses
|
|
370
|
+
* double dollar signs ($$...$$).
|
|
371
|
+
*/
|
|
372
|
+
export const MathExpressions: Story = {
|
|
373
|
+
args: {
|
|
374
|
+
content: [
|
|
375
|
+
'# Math Rendering',
|
|
376
|
+
'',
|
|
377
|
+
'Inline math: $E = mc^2$',
|
|
378
|
+
'',
|
|
379
|
+
'Display math:',
|
|
380
|
+
'',
|
|
381
|
+
'$$',
|
|
382
|
+
'\\int_{-\\infty}^{\\infty} e^{-x^2} dx = \\sqrt{\\pi}',
|
|
383
|
+
'$$',
|
|
384
|
+
'',
|
|
385
|
+
'The quadratic formula is $x = \\frac{-b \\pm \\sqrt{b^2 - 4ac}}{2a}$.',
|
|
386
|
+
'',
|
|
387
|
+
'A matrix:',
|
|
388
|
+
'',
|
|
389
|
+
'$$',
|
|
390
|
+
'\\begin{pmatrix} a & b \\\\ c & d \\end{pmatrix}',
|
|
391
|
+
'$$',
|
|
392
|
+
].join('\n'),
|
|
393
|
+
},
|
|
394
|
+
parameters: { docs: { source: { code: mathTemplateSource } } },
|
|
395
|
+
};
|
|
396
|
+
|
|
347
397
|
function visitTextNodes(node: any, transform: (value: string) => string): void {
|
|
348
398
|
if (node.type === 'text') {
|
|
349
399
|
node.value = transform(node.value);
|
|
@@ -109,4 +109,35 @@ describe('useMarkdownParser', () => {
|
|
|
109
109
|
|
|
110
110
|
expect(html.value).toBe('<p>Hello world</p>');
|
|
111
111
|
});
|
|
112
|
+
|
|
113
|
+
it('renders inline math with single dollar signs', () => {
|
|
114
|
+
const source = ref('The formula $E = mc^2$ is famous.');
|
|
115
|
+
const { html } = useMarkdownParser(source, ref(undefined));
|
|
116
|
+
|
|
117
|
+
expect(html.value).toContain('class="katex"');
|
|
118
|
+
expect(html.value).toContain('E = mc^2');
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
it('renders display math with double dollar signs', () => {
|
|
122
|
+
const source = ref('$$\n\\int_0^1 x^2 dx\n$$');
|
|
123
|
+
const { html } = useMarkdownParser(source, ref(undefined));
|
|
124
|
+
|
|
125
|
+
expect(html.value).toContain('class="katex-display"');
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
it('preserves MathML elements through sanitization', () => {
|
|
129
|
+
const source = ref('$x^2$');
|
|
130
|
+
const { html } = useMarkdownParser(source, ref(undefined));
|
|
131
|
+
|
|
132
|
+
expect(html.value).toContain('<math');
|
|
133
|
+
expect(html.value).toContain('<semantics>');
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
it('does not treat a lone dollar sign as math', () => {
|
|
137
|
+
const source = ref('The price is $5.');
|
|
138
|
+
const { html } = useMarkdownParser(source, ref(undefined));
|
|
139
|
+
|
|
140
|
+
expect(html.value).not.toContain('class="katex"');
|
|
141
|
+
expect(html.value).toContain('$5');
|
|
142
|
+
});
|
|
112
143
|
});
|
|
@@ -2,18 +2,82 @@ import { computed, type MaybeRefOrGetter, toValue } from 'vue';
|
|
|
2
2
|
import { unified, type PluggableList } from 'unified';
|
|
3
3
|
import remarkParse from 'remark-parse';
|
|
4
4
|
import remarkGfm from 'remark-gfm';
|
|
5
|
+
import remarkMath from 'remark-math';
|
|
5
6
|
import remarkRehype from 'remark-rehype';
|
|
7
|
+
import rehypeKatex from 'rehype-katex';
|
|
6
8
|
import rehypeStringify from 'rehype-stringify';
|
|
7
|
-
import DOMPurify from 'dompurify';
|
|
9
|
+
import DOMPurify, { type Config } from 'dompurify';
|
|
10
|
+
import 'katex/dist/katex.min.css';
|
|
8
11
|
|
|
9
12
|
type SyncProcessor = { processSync: (value: string) => { toString(): string } };
|
|
10
13
|
|
|
14
|
+
const KATEX_MATHML_TAGS = [
|
|
15
|
+
'math',
|
|
16
|
+
'semantics',
|
|
17
|
+
'annotation',
|
|
18
|
+
'mrow',
|
|
19
|
+
'mi',
|
|
20
|
+
'mo',
|
|
21
|
+
'mn',
|
|
22
|
+
'ms',
|
|
23
|
+
'mtext',
|
|
24
|
+
'mspace',
|
|
25
|
+
'msup',
|
|
26
|
+
'msub',
|
|
27
|
+
'msubsup',
|
|
28
|
+
'mfrac',
|
|
29
|
+
'mover',
|
|
30
|
+
'munder',
|
|
31
|
+
'munderover',
|
|
32
|
+
'msqrt',
|
|
33
|
+
'mroot',
|
|
34
|
+
'mtable',
|
|
35
|
+
'mtr',
|
|
36
|
+
'mtd',
|
|
37
|
+
'menclose',
|
|
38
|
+
'mpadded',
|
|
39
|
+
'mphantom',
|
|
40
|
+
] as const;
|
|
41
|
+
|
|
42
|
+
const KATEX_MATHML_ATTRS = [
|
|
43
|
+
'xmlns',
|
|
44
|
+
'encoding',
|
|
45
|
+
'mathvariant',
|
|
46
|
+
'displaystyle',
|
|
47
|
+
'scriptlevel',
|
|
48
|
+
'fence',
|
|
49
|
+
'separator',
|
|
50
|
+
'stretchy',
|
|
51
|
+
'symmetric',
|
|
52
|
+
'lspace',
|
|
53
|
+
'rspace',
|
|
54
|
+
'movablelimits',
|
|
55
|
+
'accent',
|
|
56
|
+
'accentunder',
|
|
57
|
+
'linethickness',
|
|
58
|
+
'columnalign',
|
|
59
|
+
'rowalign',
|
|
60
|
+
'columnspan',
|
|
61
|
+
'rowspan',
|
|
62
|
+
'width',
|
|
63
|
+
'height',
|
|
64
|
+
'depth',
|
|
65
|
+
'minsize',
|
|
66
|
+
'maxsize',
|
|
67
|
+
'aria-hidden',
|
|
68
|
+
] as const;
|
|
69
|
+
|
|
70
|
+
const SANITIZE_CONFIG: Config = {
|
|
71
|
+
ADD_TAGS: [...KATEX_MATHML_TAGS],
|
|
72
|
+
ADD_ATTR: [...KATEX_MATHML_ATTRS],
|
|
73
|
+
};
|
|
74
|
+
|
|
11
75
|
function buildProcessor(plugins: PluggableList): SyncProcessor {
|
|
12
|
-
let proc = unified().use(remarkParse).use(remarkGfm);
|
|
76
|
+
let proc = unified().use(remarkParse).use(remarkGfm).use(remarkMath);
|
|
13
77
|
for (const plugin of plugins) {
|
|
14
78
|
proc = proc.use(plugin as any);
|
|
15
79
|
}
|
|
16
|
-
return proc.use(remarkRehype).use(rehypeStringify);
|
|
80
|
+
return proc.use(remarkRehype).use(rehypeKatex).use(rehypeStringify);
|
|
17
81
|
}
|
|
18
82
|
|
|
19
83
|
export function useMarkdownParser(
|
|
@@ -30,16 +94,16 @@ export function useMarkdownParser(
|
|
|
30
94
|
return cachedProcessor;
|
|
31
95
|
}
|
|
32
96
|
|
|
33
|
-
const html = computed(() => {
|
|
97
|
+
const html = computed<string>(() => {
|
|
34
98
|
const markdown = toValue(source);
|
|
35
99
|
if (!markdown) return '';
|
|
36
100
|
try {
|
|
37
101
|
const raw = String(
|
|
38
102
|
getProcessor(toValue(plugins) ?? []).processSync(markdown),
|
|
39
103
|
);
|
|
40
|
-
return DOMPurify.sanitize(raw);
|
|
104
|
+
return DOMPurify.sanitize(raw, SANITIZE_CONFIG) as string;
|
|
41
105
|
} catch {
|
|
42
|
-
return DOMPurify.sanitize(markdown);
|
|
106
|
+
return DOMPurify.sanitize(markdown, SANITIZE_CONFIG) as string;
|
|
43
107
|
}
|
|
44
108
|
});
|
|
45
109
|
|