@aleph-alpha/chat-kit 6.1.1 → 6.2.0
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/{CkHistory.vue_vue_type_script_setup_true_lang-Cr8vF94e.js → CkHistory.vue_vue_type_script_setup_true_lang-DPCpIxWc.js} +2 -2
- package/dist/adapters/responses-api.js +1 -1
- package/dist/components/index.js +1 -1
- package/dist/composables/index.js +1 -1
- package/dist/{createCrepeEditor-CFbvSH3s.js → createCrepeEditor-C9seqnzm.js} +28 -27
- package/dist/handlers/index.js +2 -2
- package/dist/helpers/index.d.ts +1 -0
- package/dist/helpers/index.d.ts.map +1 -1
- package/dist/helpers/markdownToPlainText.d.ts +18 -0
- package/dist/helpers/markdownToPlainText.d.ts.map +1 -0
- package/dist/{index-C6zYjwIQ.js → index-6PiPEjJd.js} +2 -2
- package/dist/{index-DdlzabmR.js → index-8mYiDHFC.js} +2 -2
- package/dist/{index-gzleljO6.js → index-B0nlcqqF.js} +2 -2
- package/dist/{index-DSU7cBGz.js → index-Bxg0Q_mk.js} +2 -2
- package/dist/{index-CRfGWVdQ.js → index-By7WdPbk.js} +4 -4
- package/dist/{index-BVOjyn3y.js → index-C5UC1wP7.js} +2 -2
- package/dist/{index-C_gTb50u.js → index-C90_LSx6.js} +4 -4
- package/dist/{index-A5rWjRtZ.js → index-CFdcEeXx.js} +2 -2
- package/dist/{index-B0ldhzaj.js → index-CHtY7ztn.js} +2 -2
- package/dist/index-CLdXUpL8.js +9560 -0
- package/dist/{index-C72e57-5.js → index-CZFx44C7.js} +2 -2
- package/dist/{index-BoqzHVNB.js → index-D85elIIr.js} +2 -2
- package/dist/{index-Da3777l2.js → index-DINJn4Mn.js} +3 -3
- package/dist/{index-DhY_9bdS.js → index-DLZcs4H8.js} +2 -2
- package/dist/{index-ZeRS5rER.js → index-DNxEOsDD.js} +2 -2
- package/dist/{index-Tj3pzx-E.js → index-DO8xk00f.js} +3 -3
- package/dist/{index-CMJGuXap.js → index-DXTRMb09.js} +3 -3
- package/dist/{index-BaS8OW8V.js → index-DbVRgXxM.js} +3 -3
- package/dist/{index-naWlQpaL.js → index-DfvAv_Y2.js} +2 -2
- package/dist/{index-BWvkps_G.js → index-DzpUblMx.js} +3 -3
- package/dist/{index-sKO1lHHC.js → index-U4LYutvz.js} +2 -2
- package/dist/{index-Dm6C1blN.js → index-VbqLf82s.js} +4 -4
- package/dist/{index-BlHHEbV-.js → index-eZunhxNH.js} +2 -2
- package/dist/{index-DSq3gvqU.js → index-reQfKGuJ.js} +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -4
- package/dist/markdownToPlainText-BOtlQ_BY.js +34 -0
- package/dist/{useChatKitLabels-Ww1Q7xUr.js → useChatKitLabels-Bz58tas9.js} +879 -10428
- package/dist/{useMessageHandler-Tellov3w.js → useMessageHandler-nnsort0-.js} +1 -1
- package/package.json +3 -3
- package/src/helpers/index.ts +1 -0
- package/src/helpers/markdownToPlainText.spec.ts +107 -0
- package/src/helpers/markdownToPlainText.ts +69 -0
- package/src/index.ts +1 -1
- package/dist/generateId-BHf0W4Rq.js +0 -6
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aleph-alpha/chat-kit",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.2.0",
|
|
4
4
|
"description": "Chat kit package for building chat interfaces with Vue",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -113,9 +113,9 @@
|
|
|
113
113
|
"vue": "^3.5.27",
|
|
114
114
|
"vue-tsc": "^3.2.4",
|
|
115
115
|
"wait-on": "9.0.3",
|
|
116
|
+
"@aleph-alpha/prettier-config-frontend": "2.1.1",
|
|
116
117
|
"@aleph-alpha/eslint-config-frontend": "0.7.1",
|
|
117
|
-
"@aleph-alpha/tsconfig-frontend": "0.7.3"
|
|
118
|
-
"@aleph-alpha/prettier-config-frontend": "2.1.1"
|
|
118
|
+
"@aleph-alpha/tsconfig-frontend": "0.7.3"
|
|
119
119
|
},
|
|
120
120
|
"scripts": {
|
|
121
121
|
"build": "vite build && vue-tsc --project tsconfig.build.json --declaration --emitDeclarationOnly --outDir dist",
|
package/src/helpers/index.ts
CHANGED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
|
|
3
|
+
import { markdownToPlainText } from './markdownToPlainText';
|
|
4
|
+
|
|
5
|
+
describe('markdownToPlainText', () => {
|
|
6
|
+
it('returns text without markdown unchanged', () => {
|
|
7
|
+
expect(markdownToPlainText('A plain excerpt from a page.')).toBe(
|
|
8
|
+
'A plain excerpt from a page.',
|
|
9
|
+
);
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
it('keeps a link label and drops its url', () => {
|
|
13
|
+
expect(
|
|
14
|
+
markdownToPlainText(
|
|
15
|
+
'See the [annual report](https://example.com/r.pdf).',
|
|
16
|
+
),
|
|
17
|
+
).toBe('See the annual report.');
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it('strips emphasis markers', () => {
|
|
21
|
+
expect(markdownToPlainText('Revenue **rose** by _12%_ and ~~fell~~.')).toBe(
|
|
22
|
+
'Revenue rose by 12% and fell.',
|
|
23
|
+
);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it('separates a heading from the paragraph that follows it', () => {
|
|
27
|
+
expect(markdownToPlainText('## Cost\n\n12 EUR per seat.')).toBe(
|
|
28
|
+
'Cost 12 EUR per seat.',
|
|
29
|
+
);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it('flattens a GFM table without its pipes', () => {
|
|
33
|
+
const table = ['| Plan | Cost |', '| --- | --- |', '| Pro | 12 |'].join(
|
|
34
|
+
'\n',
|
|
35
|
+
);
|
|
36
|
+
expect(markdownToPlainText(table)).toBe('Plan Cost Pro 12');
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('separates list items', () => {
|
|
40
|
+
expect(markdownToPlainText('- first\n- second\n- third')).toBe(
|
|
41
|
+
'first second third',
|
|
42
|
+
);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('keeps code content but not its fence', () => {
|
|
46
|
+
expect(markdownToPlainText('Run `pnpm build` first.')).toBe(
|
|
47
|
+
'Run pnpm build first.',
|
|
48
|
+
);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it('drops an image rather than reading its alt text as prose', () => {
|
|
52
|
+
expect(markdownToPlainText(' Revenue rose.')).toBe(
|
|
53
|
+
'Revenue rose.',
|
|
54
|
+
);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it('removes inline html tags without joining the words around them', () => {
|
|
58
|
+
expect(markdownToPlainText('Revenue rose.<br>Costs fell.')).toBe(
|
|
59
|
+
'Revenue rose. Costs fell.',
|
|
60
|
+
);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it('keeps the prose inside a raw html block', () => {
|
|
64
|
+
expect(
|
|
65
|
+
markdownToPlainText('<div class="x">Revenue rose.</div>\nCosts fell.'),
|
|
66
|
+
).toBe('Revenue rose. Costs fell.');
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it('keeps a hard line break as a word separator', () => {
|
|
70
|
+
expect(markdownToPlainText('Revenue rose. \nCosts fell.')).toBe(
|
|
71
|
+
'Revenue rose. Costs fell.',
|
|
72
|
+
);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it('leaves a literal comparison inside a raw html block alone', () => {
|
|
76
|
+
expect(markdownToPlainText('<div>2 < 3 and 4 > 1</div>')).toBe(
|
|
77
|
+
'2 < 3 and 4 > 1',
|
|
78
|
+
);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it('removes an html comment', () => {
|
|
82
|
+
expect(markdownToPlainText('<div>Hi</div>\n<!-- hidden -->\nText.')).toBe(
|
|
83
|
+
'Hi Text.',
|
|
84
|
+
);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it('collapses the newlines a multi-paragraph excerpt carries', () => {
|
|
88
|
+
expect(markdownToPlainText('First line.\n\n\nSecond line.')).toBe(
|
|
89
|
+
'First line. Second line.',
|
|
90
|
+
);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it('leaves an unbalanced emphasis marker as literal text', () => {
|
|
94
|
+
expect(markdownToPlainText('Revenue **rose sharply in')).toBe(
|
|
95
|
+
'Revenue **rose sharply in',
|
|
96
|
+
);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it('is idempotent', () => {
|
|
100
|
+
const once = markdownToPlainText('A [linked](https://example.com) claim.');
|
|
101
|
+
expect(markdownToPlainText(once)).toBe(once);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it('returns an empty string for empty input', () => {
|
|
105
|
+
expect(markdownToPlainText('')).toBe('');
|
|
106
|
+
});
|
|
107
|
+
});
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { unified } from 'unified';
|
|
2
|
+
import remarkParse from 'remark-parse';
|
|
3
|
+
import remarkGfm from 'remark-gfm';
|
|
4
|
+
import type { Nodes } from 'mdast';
|
|
5
|
+
|
|
6
|
+
const parser = unified().use(remarkParse).use(remarkGfm);
|
|
7
|
+
|
|
8
|
+
const DROPPED_NODES = new Set(['image', 'imageReference']);
|
|
9
|
+
|
|
10
|
+
// A hard line break is a childless, valueless leaf, so it has to yield its
|
|
11
|
+
// separator explicitly or `first<br>second` flattens to `firstsecond`.
|
|
12
|
+
const SEPARATOR_NODES = new Set(['break']);
|
|
13
|
+
|
|
14
|
+
// An `html` node cannot be dropped whole: a line starting with a block tag
|
|
15
|
+
// makes markdown's HTML-block rule run to the next blank line, so the node
|
|
16
|
+
// holds the following prose too. Its tags are stripped instead, replaced by a
|
|
17
|
+
// space so `rose.<br>Costs` doesn't come out as one word. A tag has to start
|
|
18
|
+
// like one (`<p`, `</p`, `<!--`) so a literal comparison inside the block —
|
|
19
|
+
// `2 < 3 and 4 > 1` — isn't mistaken for one and deleted. The repetition is
|
|
20
|
+
// bounded because an unbounded `[^>]*` scans quadratically over a run of `<`
|
|
21
|
+
// with no closing `>` (`sonarjs/slow-regex`).
|
|
22
|
+
const HTML_TAG_RE = /<[!/]?[a-zA-Z-][^>]{0,512}>/g;
|
|
23
|
+
|
|
24
|
+
// Children of these need a separator, or a heading runs straight into the
|
|
25
|
+
// paragraph below it (`## Cost` + `12 EUR` → `Cost12 EUR`).
|
|
26
|
+
const BLOCK_CONTAINERS = new Set([
|
|
27
|
+
'root',
|
|
28
|
+
'blockquote',
|
|
29
|
+
'list',
|
|
30
|
+
'listItem',
|
|
31
|
+
'table',
|
|
32
|
+
'tableRow',
|
|
33
|
+
'footnoteDefinition',
|
|
34
|
+
]);
|
|
35
|
+
|
|
36
|
+
function flatten(node: Nodes): string {
|
|
37
|
+
if (DROPPED_NODES.has(node.type)) return '';
|
|
38
|
+
if (SEPARATOR_NODES.has(node.type)) return ' ';
|
|
39
|
+
if ('value' in node) {
|
|
40
|
+
return node.type === 'html'
|
|
41
|
+
? node.value.replace(HTML_TAG_RE, ' ')
|
|
42
|
+
: node.value;
|
|
43
|
+
}
|
|
44
|
+
if (!('children' in node)) return '';
|
|
45
|
+
return node.children
|
|
46
|
+
.map(flatten)
|
|
47
|
+
.join(BLOCK_CONTAINERS.has(node.type) ? ' ' : '');
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Flatten markdown to the prose inside it: `[label](url)` becomes `label`,
|
|
52
|
+
* headings lose their `#`, emphasis markers and table pipes go.
|
|
53
|
+
*
|
|
54
|
+
* For excerpt text that is *displayed as plain text* — a citation snippet
|
|
55
|
+
* preview, say — where the markdown is an artefact of how the source was
|
|
56
|
+
* extracted rather than something the UI intends to render. Reads the parsed
|
|
57
|
+
* tree instead of pattern-matching the string, so it is structurally exact for
|
|
58
|
+
* well-formed markdown where a regex would guess. Idempotent on text carrying
|
|
59
|
+
* no markdown, so it is a no-op once a producer starts sending plain text.
|
|
60
|
+
*
|
|
61
|
+
* Two artefacts survive. A truncated excerpt can end mid-syntax (`**bold` with
|
|
62
|
+
* no closing pair), which the parser reads as literal text — the correct
|
|
63
|
+
* reading, at the cost of a visible `**`. And markdown inside a raw HTML block
|
|
64
|
+
* is never parsed as markdown, so it is flattened as the literal text it is.
|
|
65
|
+
*/
|
|
66
|
+
export function markdownToPlainText(markdown: string): string {
|
|
67
|
+
if (!markdown) return '';
|
|
68
|
+
return flatten(parser.parse(markdown)).replace(/\s+/g, ' ').trim();
|
|
69
|
+
}
|
package/src/index.ts
CHANGED