stream-chat-react 13.6.5 → 13.7.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/components/ChannelPreview/utils.js +9 -1
- package/dist/components/Chat/hooks/useChat.js +1 -1
- package/dist/components/Message/renderText/remarkPlugins/imageToLink.d.ts +12 -0
- package/dist/components/Message/renderText/remarkPlugins/imageToLink.js +27 -0
- package/dist/components/Message/renderText/remarkPlugins/index.d.ts +2 -0
- package/dist/components/Message/renderText/remarkPlugins/index.js +2 -0
- package/dist/components/Message/renderText/remarkPlugins/plusPlusToEmphasis.d.ts +6 -0
- package/dist/components/Message/renderText/remarkPlugins/plusPlusToEmphasis.js +64 -0
- package/dist/components/Message/renderText/renderText.js +10 -1
- package/dist/experimental/index.browser.cjs +4917 -1793
- package/dist/experimental/index.browser.cjs.map +4 -4
- package/dist/experimental/index.node.cjs +4866 -1742
- package/dist/experimental/index.node.cjs.map +4 -4
- package/dist/index.browser.cjs +1476 -1400
- package/dist/index.browser.cjs.map +4 -4
- package/dist/index.node.cjs +1478 -1400
- package/dist/index.node.cjs.map +4 -4
- package/package.json +1 -1
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import ReactMarkdown from 'react-markdown';
|
|
3
|
-
|
|
3
|
+
import { htmlToTextPlugin, imageToLink, plusPlusToEmphasis } from '../Message';
|
|
4
|
+
import remarkGfm from 'remark-gfm';
|
|
5
|
+
const remarkPlugins = [
|
|
6
|
+
htmlToTextPlugin,
|
|
7
|
+
[remarkGfm, { singleTilde: false }],
|
|
8
|
+
plusPlusToEmphasis,
|
|
9
|
+
imageToLink,
|
|
10
|
+
];
|
|
11
|
+
export const renderPreviewText = (text) => (React.createElement(ReactMarkdown, { remarkPlugins: remarkPlugins, skipHtml: true }, text));
|
|
4
12
|
const getLatestPollVote = (latestVotesByOption) => {
|
|
5
13
|
let latestVote;
|
|
6
14
|
for (const optionVotes of Object.values(latestVotesByOption)) {
|
|
@@ -24,7 +24,7 @@ export const useChat = ({ client, defaultLanguage = 'en', i18nInstance, initialN
|
|
|
24
24
|
useEffect(() => {
|
|
25
25
|
if (!client)
|
|
26
26
|
return;
|
|
27
|
-
const version = "13.
|
|
27
|
+
const version = "13.7.0";
|
|
28
28
|
const userAgent = client.getUserAgent();
|
|
29
29
|
if (!userAgent.includes('stream-chat-react')) {
|
|
30
30
|
// result looks like: 'stream-chat-react-2.3.2-stream-chat-javascript-client-browser-2.2.2'
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Node } from 'unist';
|
|
2
|
+
export type ImageToLinkPluginOptions = {
|
|
3
|
+
getTextLabelFrom?: 'alt' | 'title' | 'url';
|
|
4
|
+
};
|
|
5
|
+
/**
|
|
6
|
+
* Converts image Markdown links ()
|
|
7
|
+
* to HTML <a href={url}>{url | title | alt}</a>
|
|
8
|
+
*
|
|
9
|
+
* By default, the anchor text content is the image url so that image preview can be generated / enriched on the server.
|
|
10
|
+
* @param getTextLabelFrom
|
|
11
|
+
*/
|
|
12
|
+
export declare function imageToLink({ getTextLabelFrom }?: ImageToLinkPluginOptions): (tree: Node) => void;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { SKIP, visit } from 'unist-util-visit';
|
|
2
|
+
const text = (value) => ({ type: 'text', value });
|
|
3
|
+
/**
|
|
4
|
+
* Converts image Markdown links ()
|
|
5
|
+
* to HTML <a href={url}>{url | title | alt}</a>
|
|
6
|
+
*
|
|
7
|
+
* By default, the anchor text content is the image url so that image preview can be generated / enriched on the server.
|
|
8
|
+
* @param getTextLabelFrom
|
|
9
|
+
*/
|
|
10
|
+
export function imageToLink({ getTextLabelFrom = 'url' } = {}) {
|
|
11
|
+
return (tree) => {
|
|
12
|
+
const visitor = (node, index, parent) => {
|
|
13
|
+
if (parent == null || index == null)
|
|
14
|
+
return;
|
|
15
|
+
const label = node[getTextLabelFrom] ?? node.url; // node.alt || node.title || node.url;
|
|
16
|
+
const link = {
|
|
17
|
+
children: [text(label)],
|
|
18
|
+
title: node.title ?? node.alt ?? node.url,
|
|
19
|
+
type: 'link',
|
|
20
|
+
url: node.url,
|
|
21
|
+
};
|
|
22
|
+
parent.children.splice(index, 1, link);
|
|
23
|
+
return [SKIP, index + 1];
|
|
24
|
+
};
|
|
25
|
+
visit(tree, 'image', visitor);
|
|
26
|
+
};
|
|
27
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { Plugin } from 'unified';
|
|
2
|
+
/**
|
|
3
|
+
* Converts MD "++Some text++" to inserted text element rendered in HTML as <ins>Some text</ins>
|
|
4
|
+
* https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/ins
|
|
5
|
+
*/
|
|
6
|
+
export declare const plusPlusToEmphasis: Plugin<[]>;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { SKIP, visit } from 'unist-util-visit';
|
|
2
|
+
/**
|
|
3
|
+
* \S → first char must be non-whitespace
|
|
4
|
+
* (?:...)?→ optional middle+closing when length > 1
|
|
5
|
+
* [\s\S]*?→ anything (including newlines), lazy
|
|
6
|
+
* final \S→ last char non-whitespace (only required when there’s more than 1)
|
|
7
|
+
*
|
|
8
|
+
* Matches:
|
|
9
|
+
* ++a++
|
|
10
|
+
* Does not match:
|
|
11
|
+
* ++++
|
|
12
|
+
* ++ ++
|
|
13
|
+
*/
|
|
14
|
+
const INS_REGEX = /\+\+(\S(?:[\s\S]*?\S)?)\+\+/g;
|
|
15
|
+
const IGNORE_NODE_TYPES = new Set([
|
|
16
|
+
'code',
|
|
17
|
+
'inlineCode',
|
|
18
|
+
'link',
|
|
19
|
+
'linkReference',
|
|
20
|
+
'definition',
|
|
21
|
+
'math',
|
|
22
|
+
'inlineMath',
|
|
23
|
+
]);
|
|
24
|
+
/**
|
|
25
|
+
* Converts MD "++Some text++" to inserted text element rendered in HTML as <ins>Some text</ins>
|
|
26
|
+
* https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/ins
|
|
27
|
+
*/
|
|
28
|
+
export const plusPlusToEmphasis = () => {
|
|
29
|
+
const visitor = (node, index, parent) => {
|
|
30
|
+
// 1) Don’t traverse inside ignored nodes
|
|
31
|
+
if (IGNORE_NODE_TYPES.has(node.type))
|
|
32
|
+
return SKIP;
|
|
33
|
+
// 2) Only transform text nodes with a valid parent + index
|
|
34
|
+
if (node.type !== 'text' || parent == null || typeof index !== 'number')
|
|
35
|
+
return;
|
|
36
|
+
const value = node.value;
|
|
37
|
+
// Reset lastIndex to 0 per node so each node is scanned from the beginning
|
|
38
|
+
INS_REGEX.lastIndex = 0;
|
|
39
|
+
let match;
|
|
40
|
+
let last = 0;
|
|
41
|
+
const out = [];
|
|
42
|
+
while ((match = INS_REGEX.exec(value))) {
|
|
43
|
+
const [full, inner] = match;
|
|
44
|
+
const start = match.index;
|
|
45
|
+
if (start > last)
|
|
46
|
+
out.push({ type: 'text', value: value.slice(last, start) });
|
|
47
|
+
// Render as <ins>…</ins> (remark-rehype respects data.hName)
|
|
48
|
+
out.push({
|
|
49
|
+
children: [{ type: 'text', value: inner }],
|
|
50
|
+
data: { hName: 'ins' },
|
|
51
|
+
type: 'emphasis',
|
|
52
|
+
});
|
|
53
|
+
last = start + full.length;
|
|
54
|
+
}
|
|
55
|
+
if (out.length === 0)
|
|
56
|
+
return; // nothing to change
|
|
57
|
+
if (last < value.length)
|
|
58
|
+
out.push({ type: 'text', value: value.slice(last) });
|
|
59
|
+
parent.children.splice(index, 1, ...out);
|
|
60
|
+
// Skip re-visiting the replaced range; continue after inserted nodes
|
|
61
|
+
return [SKIP, index + out.length];
|
|
62
|
+
};
|
|
63
|
+
return (tree) => visit(tree, visitor);
|
|
64
|
+
};
|
|
@@ -5,7 +5,7 @@ import remarkGfm from 'remark-gfm';
|
|
|
5
5
|
import { Anchor, Emoji, Mention } from './componentRenderers';
|
|
6
6
|
import { detectHttp, matchMarkdownLinks, messageCodeBlocks } from './regex';
|
|
7
7
|
import { emojiMarkdownPlugin, mentionsMarkdownPlugin } from './rehypePlugins';
|
|
8
|
-
import { htmlToTextPlugin, keepLineBreaksPlugin } from './remarkPlugins';
|
|
8
|
+
import { htmlToTextPlugin, imageToLink, keepLineBreaksPlugin, plusPlusToEmphasis, } from './remarkPlugins';
|
|
9
9
|
import { ErrorBoundary } from '../../UtilityComponents';
|
|
10
10
|
export const defaultAllowedTagNames = [
|
|
11
11
|
'html',
|
|
@@ -32,6 +32,13 @@ export const defaultAllowedTagNames = [
|
|
|
32
32
|
// custom types (tagNames)
|
|
33
33
|
'emoji',
|
|
34
34
|
'mention',
|
|
35
|
+
'h1',
|
|
36
|
+
'h2',
|
|
37
|
+
'h3',
|
|
38
|
+
'h4',
|
|
39
|
+
'h5',
|
|
40
|
+
'h6',
|
|
41
|
+
'ins',
|
|
35
42
|
];
|
|
36
43
|
function formatUrlForDisplay(url) {
|
|
37
44
|
try {
|
|
@@ -118,6 +125,8 @@ export const renderText = (text, mentionedUsers, { allowedTagNames = defaultAllo
|
|
|
118
125
|
htmlToTextPlugin,
|
|
119
126
|
keepLineBreaksPlugin,
|
|
120
127
|
[remarkGfm, { singleTilde: false }],
|
|
128
|
+
plusPlusToEmphasis,
|
|
129
|
+
imageToLink,
|
|
121
130
|
];
|
|
122
131
|
const rehypePlugins = [emojiMarkdownPlugin];
|
|
123
132
|
if (mentionedUsers?.length) {
|