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.
@@ -1,6 +1,14 @@
1
1
  import React from 'react';
2
2
  import ReactMarkdown from 'react-markdown';
3
- export const renderPreviewText = (text) => (React.createElement(ReactMarkdown, { skipHtml: true }, text));
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.6.5";
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 (![Minion](https://octodex.github.com/images/minion.png))
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 (![Minion](https://octodex.github.com/images/minion.png))
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
+ }
@@ -1,2 +1,4 @@
1
1
  export * from './htmlToTextPlugin';
2
+ export * from './imageToLink';
2
3
  export * from './keepLineBreaksPlugin';
4
+ export * from './plusPlusToEmphasis';
@@ -1,2 +1,4 @@
1
1
  export * from './htmlToTextPlugin';
2
+ export * from './imageToLink';
2
3
  export * from './keepLineBreaksPlugin';
4
+ export * from './plusPlusToEmphasis';
@@ -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) {