@tensor_1/react 2.39.0 → 2.40.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/README.md +18 -0
- package/dist/chart-analytic-model.d.ts +5 -1
- package/dist/chart-analytic-model.js +23 -16
- package/dist/chart-analytic.cjs +23 -16
- package/dist/chart-analytic.d.cts +5 -1
- package/dist/components/Comments.d.ts +4 -1
- package/dist/components/Comments.js +3 -2
- package/dist/components/Markdown.d.ts +9 -2
- package/dist/components/Markdown.js +16 -2
- package/dist/components/RichTextEditor.d.ts +17 -0
- package/dist/components/RichTextEditor.js +179 -11
- package/dist/components/ToggleRow.d.ts +8 -1
- package/dist/components/ToggleRow.js +17 -3
- package/dist/icons/Icon.d.ts +12 -12
- package/dist/index.cjs +490 -475
- package/dist/index.d.cts +12 -2
- package/dist/markdown-model-Cuwi6pIk.d.cts +110 -0
- package/dist/markdown-model.d.ts +38 -1
- package/dist/markdown-model.js +67 -0
- package/dist/markdown.cjs +90 -4
- package/dist/markdown.d.cts +11 -75
- package/dist/markdown.d.ts +1 -1
- package/dist/markdown.js +16 -2
- package/dist/rich-text/mention.d.ts +14 -0
- package/dist/rich-text/mention.js +57 -0
- package/dist/rich-text/pipeline.js +4 -1
- package/dist/rich-text.cjs +1795 -1483
- package/dist/rich-text.d.cts +27 -1
- package/dist/rich-text.d.ts +3 -0
- package/dist/rich-text.js +6 -0
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -3220,12 +3220,19 @@ interface RelatedLinksProps extends Omit<HTMLAttributes<HTMLElement>, 'title' |
|
|
|
3220
3220
|
declare function RelatedLinks({ title, items, className, ...props }: RelatedLinksProps): react.JSX.Element;
|
|
3221
3221
|
|
|
3222
3222
|
interface ToggleRowProps extends Omit<HTMLAttributes<HTMLDivElement>, 'onChange'> {
|
|
3223
|
+
/** The row's visible label. It is the switch's `<label>`: clicking it flips the switch, and it names the switch. */
|
|
3223
3224
|
label: ReactNode;
|
|
3225
|
+
/** Shown under the label and announced as the switch's description (`aria-describedby`). */
|
|
3224
3226
|
description?: ReactNode;
|
|
3225
3227
|
checked: boolean;
|
|
3226
3228
|
onCheckedChange: (checked: boolean) => void;
|
|
3227
3229
|
disabled?: boolean;
|
|
3228
|
-
|
|
3230
|
+
/**
|
|
3231
|
+
* The switch's name when the visible label is not text (an icon, an image).
|
|
3232
|
+
* Leave it out for a text label: the label already names the switch, and a
|
|
3233
|
+
* repeated `aria-label` would replace it.
|
|
3234
|
+
*/
|
|
3235
|
+
accessibleLabel?: string;
|
|
3229
3236
|
}
|
|
3230
3237
|
declare function ToggleRow({ label, description, checked, onCheckedChange, disabled, accessibleLabel, className, ...props }: ToggleRowProps): react.JSX.Element;
|
|
3231
3238
|
|
|
@@ -4454,7 +4461,10 @@ interface CommentsProps extends HTMLAttributes<HTMLDivElement> {
|
|
|
4454
4461
|
*/
|
|
4455
4462
|
declare function Comments({ children, className, ...props }: CommentsProps): react.JSX.Element;
|
|
4456
4463
|
interface CommentProps extends HTMLAttributes<HTMLDivElement> {
|
|
4457
|
-
/**
|
|
4464
|
+
/**
|
|
4465
|
+
* Usually an `<Avatar />` (the grid reserves the leading column). `false` drops the
|
|
4466
|
+
* column, for a comment that sits on a rail which already marks each row (a timeline).
|
|
4467
|
+
*/
|
|
4458
4468
|
avatar?: ReactNode;
|
|
4459
4469
|
author?: ReactNode;
|
|
4460
4470
|
meta?: ReactNode;
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
type MarkdownInline = {
|
|
2
|
+
type: 'text';
|
|
3
|
+
value: string;
|
|
4
|
+
} | {
|
|
5
|
+
type: 'code';
|
|
6
|
+
value: string;
|
|
7
|
+
} | {
|
|
8
|
+
type: 'strong';
|
|
9
|
+
children: MarkdownInline[];
|
|
10
|
+
} | {
|
|
11
|
+
type: 'em';
|
|
12
|
+
children: MarkdownInline[];
|
|
13
|
+
} | {
|
|
14
|
+
type: 'del';
|
|
15
|
+
children: MarkdownInline[];
|
|
16
|
+
} | {
|
|
17
|
+
type: 'link';
|
|
18
|
+
href: string;
|
|
19
|
+
title?: string;
|
|
20
|
+
children: MarkdownInline[];
|
|
21
|
+
bare?: boolean;
|
|
22
|
+
} | {
|
|
23
|
+
type: 'image';
|
|
24
|
+
src: string;
|
|
25
|
+
alt: string;
|
|
26
|
+
title?: string;
|
|
27
|
+
}
|
|
28
|
+
/** `@[Display name](mention:<id>)` — a person mentioned by id; `label` is the stored name. */
|
|
29
|
+
| {
|
|
30
|
+
type: 'mention';
|
|
31
|
+
id: string;
|
|
32
|
+
label: string;
|
|
33
|
+
} | {
|
|
34
|
+
type: 'br';
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* The one stored form of a mention: `@[Display name](mention:<id>)`. The id is the
|
|
38
|
+
* product's (a user id); the name is what the author saw, kept so the text still reads
|
|
39
|
+
* without the product. `[`, `]` and `\` in the name are backslash-escaped. Ids are
|
|
40
|
+
* limited to letters, digits and `_ . -`, so a mention can never carry a URL.
|
|
41
|
+
*/
|
|
42
|
+
declare const MENTION_SCHEME = "mention:";
|
|
43
|
+
/** True when `id` can be stored in a mention. */
|
|
44
|
+
declare function isMentionId(id: string): boolean;
|
|
45
|
+
/** The markdown for one mention, or `null` when the id cannot be stored or the name is empty. */
|
|
46
|
+
declare function formatMention(mention: {
|
|
47
|
+
id: string;
|
|
48
|
+
label: string;
|
|
49
|
+
}): string | null;
|
|
50
|
+
/**
|
|
51
|
+
* The mention at the start of `text`, with the number of characters it spans, or `null`.
|
|
52
|
+
* Used by the viewer, the editor and products that read stored text on the server.
|
|
53
|
+
*/
|
|
54
|
+
declare function readMention(text: string): {
|
|
55
|
+
id: string;
|
|
56
|
+
label: string;
|
|
57
|
+
length: number;
|
|
58
|
+
} | null;
|
|
59
|
+
type TableAlign = 'left' | 'center' | 'right' | null;
|
|
60
|
+
interface MarkdownListItem {
|
|
61
|
+
/** `null` for a plain item, otherwise the task state. */
|
|
62
|
+
checked: boolean | null;
|
|
63
|
+
children: MarkdownInline[];
|
|
64
|
+
/** Nested lists. */
|
|
65
|
+
lists: MarkdownList[];
|
|
66
|
+
}
|
|
67
|
+
interface MarkdownList {
|
|
68
|
+
type: 'list';
|
|
69
|
+
ordered: boolean;
|
|
70
|
+
start: number;
|
|
71
|
+
items: MarkdownListItem[];
|
|
72
|
+
}
|
|
73
|
+
type MarkdownBlock = {
|
|
74
|
+
type: 'paragraph';
|
|
75
|
+
children: MarkdownInline[];
|
|
76
|
+
} | {
|
|
77
|
+
type: 'heading';
|
|
78
|
+
level: number;
|
|
79
|
+
children: MarkdownInline[];
|
|
80
|
+
} | {
|
|
81
|
+
type: 'code';
|
|
82
|
+
lang: string;
|
|
83
|
+
value: string;
|
|
84
|
+
} | {
|
|
85
|
+
type: 'quote';
|
|
86
|
+
children: MarkdownInline[];
|
|
87
|
+
} | {
|
|
88
|
+
type: 'rule';
|
|
89
|
+
} | MarkdownList | {
|
|
90
|
+
type: 'table';
|
|
91
|
+
align: TableAlign[];
|
|
92
|
+
head: MarkdownInline[][];
|
|
93
|
+
rows: MarkdownInline[][][];
|
|
94
|
+
};
|
|
95
|
+
/** Inline markdown to nodes. Newlines become line breaks. */
|
|
96
|
+
declare function parseInline(text: string, depth?: number): MarkdownInline[];
|
|
97
|
+
/** The text a reader sees, for accessible names and image alt text. */
|
|
98
|
+
declare function plainText(nodes: ReadonlyArray<MarkdownInline>): string;
|
|
99
|
+
/**
|
|
100
|
+
* Every mention in a markdown text, in reading order (an id may repeat). Code spans and
|
|
101
|
+
* code blocks hold no mentions. For products that act on mentions when text is saved.
|
|
102
|
+
*/
|
|
103
|
+
declare function findMentions(markdown: string): Array<{
|
|
104
|
+
id: string;
|
|
105
|
+
label: string;
|
|
106
|
+
}>;
|
|
107
|
+
/** Markdown source to blocks. Empty or whitespace-only input yields no blocks. */
|
|
108
|
+
declare function parseMarkdown(source: string): MarkdownBlock[];
|
|
109
|
+
|
|
110
|
+
export { MENTION_SCHEME as M, type TableAlign as T, type MarkdownBlock as a, type MarkdownInline as b, type MarkdownList as c, type MarkdownListItem as d, formatMention as e, findMentions as f, parseMarkdown as g, plainText as h, isMentionId as i, parseInline as p, readMention as r };
|
package/dist/markdown-model.d.ts
CHANGED
|
@@ -24,9 +24,38 @@ type MarkdownInline = {
|
|
|
24
24
|
src: string;
|
|
25
25
|
alt: string;
|
|
26
26
|
title?: string;
|
|
27
|
+
}
|
|
28
|
+
/** `@[Display name](mention:<id>)` — a person mentioned by id; `label` is the stored name. */
|
|
29
|
+
| {
|
|
30
|
+
type: 'mention';
|
|
31
|
+
id: string;
|
|
32
|
+
label: string;
|
|
27
33
|
} | {
|
|
28
34
|
type: 'br';
|
|
29
35
|
};
|
|
36
|
+
/**
|
|
37
|
+
* The one stored form of a mention: `@[Display name](mention:<id>)`. The id is the
|
|
38
|
+
* product's (a user id); the name is what the author saw, kept so the text still reads
|
|
39
|
+
* without the product. `[`, `]` and `\` in the name are backslash-escaped. Ids are
|
|
40
|
+
* limited to letters, digits and `_ . -`, so a mention can never carry a URL.
|
|
41
|
+
*/
|
|
42
|
+
declare const MENTION_SCHEME = "mention:";
|
|
43
|
+
/** True when `id` can be stored in a mention. */
|
|
44
|
+
declare function isMentionId(id: string): boolean;
|
|
45
|
+
/** The markdown for one mention, or `null` when the id cannot be stored or the name is empty. */
|
|
46
|
+
declare function formatMention(mention: {
|
|
47
|
+
id: string;
|
|
48
|
+
label: string;
|
|
49
|
+
}): string | null;
|
|
50
|
+
/**
|
|
51
|
+
* The mention at the start of `text`, with the number of characters it spans, or `null`.
|
|
52
|
+
* Used by the viewer, the editor and products that read stored text on the server.
|
|
53
|
+
*/
|
|
54
|
+
declare function readMention(text: string): {
|
|
55
|
+
id: string;
|
|
56
|
+
label: string;
|
|
57
|
+
length: number;
|
|
58
|
+
} | null;
|
|
30
59
|
type TableAlign = 'left' | 'center' | 'right' | null;
|
|
31
60
|
interface MarkdownListItem {
|
|
32
61
|
/** `null` for a plain item, otherwise the task state. */
|
|
@@ -67,9 +96,17 @@ type MarkdownBlock = {
|
|
|
67
96
|
declare function parseInline(text: string, depth?: number): MarkdownInline[];
|
|
68
97
|
/** The text a reader sees, for accessible names and image alt text. */
|
|
69
98
|
declare function plainText(nodes: ReadonlyArray<MarkdownInline>): string;
|
|
99
|
+
/**
|
|
100
|
+
* Every mention in a markdown text, in reading order (an id may repeat). Code spans and
|
|
101
|
+
* code blocks hold no mentions. For products that act on mentions when text is saved.
|
|
102
|
+
*/
|
|
103
|
+
declare function findMentions(markdown: string): Array<{
|
|
104
|
+
id: string;
|
|
105
|
+
label: string;
|
|
106
|
+
}>;
|
|
70
107
|
/** Split a table row on unescaped pipes outside code spans. */
|
|
71
108
|
declare function splitTableRow(line: string): string[];
|
|
72
109
|
/** Markdown source to blocks. Empty or whitespace-only input yields no blocks. */
|
|
73
110
|
declare function parseMarkdown(source: string): MarkdownBlock[];
|
|
74
111
|
|
|
75
|
-
export { type MarkdownBlock, type MarkdownInline, type MarkdownList, type MarkdownListItem, type TableAlign, parseInline, parseMarkdown, plainText, splitTableRow };
|
|
112
|
+
export { MENTION_SCHEME, type MarkdownBlock, type MarkdownInline, type MarkdownList, type MarkdownListItem, type TableAlign, findMentions, formatMention, isMentionId, parseInline, parseMarkdown, plainText, readMention, splitTableRow };
|
package/dist/markdown-model.js
CHANGED
|
@@ -1,3 +1,21 @@
|
|
|
1
|
+
const MENTION_SCHEME = "mention:";
|
|
2
|
+
const MENTION_ID = /^[A-Za-z0-9][A-Za-z0-9_.-]{0,127}$/;
|
|
3
|
+
const MENTION_RE = /^@\[((?:\\[\\[\]]|[^\\[\]\n]){1,200})\]\(mention:([A-Za-z0-9][A-Za-z0-9_.-]{0,127})\)/;
|
|
4
|
+
const cleanMentionLabel = (label) => label.replace(/\s+/gu, " ").trim().slice(0, 200);
|
|
5
|
+
function isMentionId(id) {
|
|
6
|
+
return MENTION_ID.test(id);
|
|
7
|
+
}
|
|
8
|
+
function formatMention(mention) {
|
|
9
|
+
const label = cleanMentionLabel(mention.label);
|
|
10
|
+
if (!isMentionId(mention.id) || !label) return null;
|
|
11
|
+
return `@[${label.replace(/[\\[\]]/g, (ch) => `\\${ch}`)}](${MENTION_SCHEME}${mention.id})`;
|
|
12
|
+
}
|
|
13
|
+
function readMention(text) {
|
|
14
|
+
const match = MENTION_RE.exec(text);
|
|
15
|
+
if (!match) return null;
|
|
16
|
+
const label = cleanMentionLabel(match[1].replace(/\\([\\[\]])/g, "$1"));
|
|
17
|
+
return label ? { id: match[2], label, length: match[0].length } : null;
|
|
18
|
+
}
|
|
1
19
|
const ASCII_PUNCT = /[!-/:-@[-`{-~]/;
|
|
2
20
|
const WORD = /[\p{L}\p{N}]/u;
|
|
3
21
|
const NAMED_ENTITIES = {
|
|
@@ -152,6 +170,14 @@ function parseInline(text, depth = 0) {
|
|
|
152
170
|
i += run.length;
|
|
153
171
|
continue;
|
|
154
172
|
}
|
|
173
|
+
if (ch === "@" && text[i + 1] === "[") {
|
|
174
|
+
const mention = readMention(rest);
|
|
175
|
+
if (mention) {
|
|
176
|
+
out.push({ type: "mention", id: mention.id, label: mention.label });
|
|
177
|
+
i += mention.length;
|
|
178
|
+
continue;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
155
181
|
if (ch === "!" && text[i + 1] === "[") {
|
|
156
182
|
const end = findBracketEnd(text, i + 1);
|
|
157
183
|
const dest = end > 0 ? readDestination(text, end + 1) : null;
|
|
@@ -246,11 +272,47 @@ function plainText(nodes) {
|
|
|
246
272
|
return " ";
|
|
247
273
|
case "image":
|
|
248
274
|
return n.alt;
|
|
275
|
+
case "mention":
|
|
276
|
+
return `@${n.label}`;
|
|
249
277
|
default:
|
|
250
278
|
return plainText(n.children);
|
|
251
279
|
}
|
|
252
280
|
}).join("");
|
|
253
281
|
}
|
|
282
|
+
const collectMentions = (nodes, out) => {
|
|
283
|
+
for (const n of nodes) {
|
|
284
|
+
if (n.type === "mention") out.push({ id: n.id, label: n.label });
|
|
285
|
+
else if ("children" in n) collectMentions(n.children, out);
|
|
286
|
+
}
|
|
287
|
+
};
|
|
288
|
+
function findMentions(markdown) {
|
|
289
|
+
const out = [];
|
|
290
|
+
const walkList = (list) => {
|
|
291
|
+
for (const item of list.items) {
|
|
292
|
+
collectMentions(item.children, out);
|
|
293
|
+
item.lists.forEach(walkList);
|
|
294
|
+
}
|
|
295
|
+
};
|
|
296
|
+
for (const block of parseMarkdown(markdown)) {
|
|
297
|
+
switch (block.type) {
|
|
298
|
+
case "paragraph":
|
|
299
|
+
case "heading":
|
|
300
|
+
case "quote":
|
|
301
|
+
collectMentions(block.children, out);
|
|
302
|
+
break;
|
|
303
|
+
case "list":
|
|
304
|
+
walkList(block);
|
|
305
|
+
break;
|
|
306
|
+
case "table":
|
|
307
|
+
block.head.forEach((cell) => collectMentions(cell, out));
|
|
308
|
+
block.rows.forEach((row) => row.forEach((cell) => collectMentions(cell, out)));
|
|
309
|
+
break;
|
|
310
|
+
default:
|
|
311
|
+
break;
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
return out;
|
|
315
|
+
}
|
|
254
316
|
const FENCE_RE = /^(`{3,}|~{3,})\s*([^`\s]*)/;
|
|
255
317
|
const HEADING_RE = /^(#{1,6})\s+(.*?)(?:\s+#+)?$/;
|
|
256
318
|
const RULE_RE = /^(?:(?:\*\s*){3,}|(?:-\s*){3,}|(?:_\s*){3,})$/;
|
|
@@ -431,8 +493,13 @@ ${current.trim()}`;
|
|
|
431
493
|
return blocks;
|
|
432
494
|
}
|
|
433
495
|
export {
|
|
496
|
+
MENTION_SCHEME,
|
|
497
|
+
findMentions,
|
|
498
|
+
formatMention,
|
|
499
|
+
isMentionId,
|
|
434
500
|
parseInline,
|
|
435
501
|
parseMarkdown,
|
|
436
502
|
plainText,
|
|
503
|
+
readMention,
|
|
437
504
|
splitTableRow
|
|
438
505
|
};
|
package/dist/markdown.cjs
CHANGED
|
@@ -20,11 +20,16 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
// src/markdown.ts
|
|
21
21
|
var markdown_exports = {};
|
|
22
22
|
__export(markdown_exports, {
|
|
23
|
+
MENTION_SCHEME: () => MENTION_SCHEME,
|
|
23
24
|
Markdown: () => Markdown,
|
|
24
25
|
defaultMarkdownIsSafeUrl: () => defaultMarkdownIsSafeUrl,
|
|
26
|
+
findMentions: () => findMentions,
|
|
27
|
+
formatMention: () => formatMention,
|
|
28
|
+
isMentionId: () => isMentionId,
|
|
25
29
|
parseInline: () => parseInline,
|
|
26
30
|
parseMarkdown: () => parseMarkdown,
|
|
27
|
-
plainText: () => plainText
|
|
31
|
+
plainText: () => plainText,
|
|
32
|
+
readMention: () => readMention
|
|
28
33
|
});
|
|
29
34
|
module.exports = __toCommonJS(markdown_exports);
|
|
30
35
|
|
|
@@ -34,6 +39,24 @@ function cx(...args) {
|
|
|
34
39
|
}
|
|
35
40
|
|
|
36
41
|
// src/markdown-model.ts
|
|
42
|
+
var MENTION_SCHEME = "mention:";
|
|
43
|
+
var MENTION_ID = /^[A-Za-z0-9][A-Za-z0-9_.-]{0,127}$/;
|
|
44
|
+
var MENTION_RE = /^@\[((?:\\[\\[\]]|[^\\[\]\n]){1,200})\]\(mention:([A-Za-z0-9][A-Za-z0-9_.-]{0,127})\)/;
|
|
45
|
+
var cleanMentionLabel = (label) => label.replace(/\s+/gu, " ").trim().slice(0, 200);
|
|
46
|
+
function isMentionId(id) {
|
|
47
|
+
return MENTION_ID.test(id);
|
|
48
|
+
}
|
|
49
|
+
function formatMention(mention) {
|
|
50
|
+
const label = cleanMentionLabel(mention.label);
|
|
51
|
+
if (!isMentionId(mention.id) || !label) return null;
|
|
52
|
+
return `@[${label.replace(/[\\[\]]/g, (ch) => `\\${ch}`)}](${MENTION_SCHEME}${mention.id})`;
|
|
53
|
+
}
|
|
54
|
+
function readMention(text) {
|
|
55
|
+
const match = MENTION_RE.exec(text);
|
|
56
|
+
if (!match) return null;
|
|
57
|
+
const label = cleanMentionLabel(match[1].replace(/\\([\\[\]])/g, "$1"));
|
|
58
|
+
return label ? { id: match[2], label, length: match[0].length } : null;
|
|
59
|
+
}
|
|
37
60
|
var ASCII_PUNCT = /[!-/:-@[-`{-~]/;
|
|
38
61
|
var WORD = /[\p{L}\p{N}]/u;
|
|
39
62
|
var NAMED_ENTITIES = {
|
|
@@ -188,6 +211,14 @@ function parseInline(text, depth = 0) {
|
|
|
188
211
|
i += run.length;
|
|
189
212
|
continue;
|
|
190
213
|
}
|
|
214
|
+
if (ch === "@" && text[i + 1] === "[") {
|
|
215
|
+
const mention = readMention(rest);
|
|
216
|
+
if (mention) {
|
|
217
|
+
out.push({ type: "mention", id: mention.id, label: mention.label });
|
|
218
|
+
i += mention.length;
|
|
219
|
+
continue;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
191
222
|
if (ch === "!" && text[i + 1] === "[") {
|
|
192
223
|
const end = findBracketEnd(text, i + 1);
|
|
193
224
|
const dest = end > 0 ? readDestination(text, end + 1) : null;
|
|
@@ -282,11 +313,47 @@ function plainText(nodes) {
|
|
|
282
313
|
return " ";
|
|
283
314
|
case "image":
|
|
284
315
|
return n.alt;
|
|
316
|
+
case "mention":
|
|
317
|
+
return `@${n.label}`;
|
|
285
318
|
default:
|
|
286
319
|
return plainText(n.children);
|
|
287
320
|
}
|
|
288
321
|
}).join("");
|
|
289
322
|
}
|
|
323
|
+
var collectMentions = (nodes, out) => {
|
|
324
|
+
for (const n of nodes) {
|
|
325
|
+
if (n.type === "mention") out.push({ id: n.id, label: n.label });
|
|
326
|
+
else if ("children" in n) collectMentions(n.children, out);
|
|
327
|
+
}
|
|
328
|
+
};
|
|
329
|
+
function findMentions(markdown) {
|
|
330
|
+
const out = [];
|
|
331
|
+
const walkList = (list) => {
|
|
332
|
+
for (const item of list.items) {
|
|
333
|
+
collectMentions(item.children, out);
|
|
334
|
+
item.lists.forEach(walkList);
|
|
335
|
+
}
|
|
336
|
+
};
|
|
337
|
+
for (const block of parseMarkdown(markdown)) {
|
|
338
|
+
switch (block.type) {
|
|
339
|
+
case "paragraph":
|
|
340
|
+
case "heading":
|
|
341
|
+
case "quote":
|
|
342
|
+
collectMentions(block.children, out);
|
|
343
|
+
break;
|
|
344
|
+
case "list":
|
|
345
|
+
walkList(block);
|
|
346
|
+
break;
|
|
347
|
+
case "table":
|
|
348
|
+
block.head.forEach((cell) => collectMentions(cell, out));
|
|
349
|
+
block.rows.forEach((row) => row.forEach((cell) => collectMentions(cell, out)));
|
|
350
|
+
break;
|
|
351
|
+
default:
|
|
352
|
+
break;
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
return out;
|
|
356
|
+
}
|
|
290
357
|
var FENCE_RE = /^(`{3,}|~{3,})\s*([^`\s]*)/;
|
|
291
358
|
var HEADING_RE = /^(#{1,6})\s+(.*?)(?:\s+#+)?$/;
|
|
292
359
|
var RULE_RE = /^(?:(?:\*\s*){3,}|(?:-\s*){3,}|(?:_\s*){3,})$/;
|
|
@@ -510,6 +577,13 @@ function renderInlines(nodes, policy) {
|
|
|
510
577
|
i
|
|
511
578
|
);
|
|
512
579
|
}
|
|
580
|
+
case "mention": {
|
|
581
|
+
const name = policy.resolveMention?.(node.id, node.label) ?? node.label;
|
|
582
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "uix-mention", "data-mention-id": node.id, children: [
|
|
583
|
+
"@",
|
|
584
|
+
name
|
|
585
|
+
] }, i);
|
|
586
|
+
}
|
|
513
587
|
case "image": {
|
|
514
588
|
const resolved = policy.resolveImageSrc?.(node.src) ?? null;
|
|
515
589
|
if (resolved) {
|
|
@@ -568,18 +642,30 @@ function renderBlock(block, policy, headingOffset, key) {
|
|
|
568
642
|
);
|
|
569
643
|
}
|
|
570
644
|
}
|
|
571
|
-
function Markdown({
|
|
645
|
+
function Markdown({
|
|
646
|
+
children,
|
|
647
|
+
className,
|
|
648
|
+
isSafeUrl = defaultMarkdownIsSafeUrl,
|
|
649
|
+
resolveImageSrc,
|
|
650
|
+
headingOffset = 2,
|
|
651
|
+
resolveMention
|
|
652
|
+
}) {
|
|
572
653
|
const source = typeof children === "string" ? children : "";
|
|
573
654
|
if (source.trim() === "") return null;
|
|
574
|
-
const policy = { isSafeUrl, resolveImageSrc };
|
|
655
|
+
const policy = { isSafeUrl, resolveImageSrc, resolveMention };
|
|
575
656
|
const blocks = parseMarkdown(source);
|
|
576
657
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: cx("uix-prose", "uix-markdown", className), "data-component": "markdown", children: blocks.map((block, i) => renderBlock(block, policy, headingOffset, i)) });
|
|
577
658
|
}
|
|
578
659
|
// Annotate the CommonJS export names for ESM import in node:
|
|
579
660
|
0 && (module.exports = {
|
|
661
|
+
MENTION_SCHEME,
|
|
580
662
|
Markdown,
|
|
581
663
|
defaultMarkdownIsSafeUrl,
|
|
664
|
+
findMentions,
|
|
665
|
+
formatMention,
|
|
666
|
+
isMentionId,
|
|
582
667
|
parseInline,
|
|
583
668
|
parseMarkdown,
|
|
584
|
-
plainText
|
|
669
|
+
plainText,
|
|
670
|
+
readMention
|
|
585
671
|
});
|
package/dist/markdown.d.cts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
|
+
export { M as MENTION_SCHEME, a as MarkdownBlock, b as MarkdownInline, c as MarkdownList, d as MarkdownListItem, T as TableAlign, f as findMentions, e as formatMention, i as isMentionId, p as parseInline, g as parseMarkdown, h as plainText, r as readMention } from './markdown-model-Cuwi6pIk.cjs';
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* Allows http(s), mailto and same-app references (`/path`, `#anchor`, `./x`, `../x`).
|
|
@@ -26,6 +27,12 @@ interface MarkdownProps {
|
|
|
26
27
|
* (`#` → `<h3>` by default, as in TENSOR's original renderer). Capped at `<h6>`.
|
|
27
28
|
*/
|
|
28
29
|
headingOffset?: number;
|
|
30
|
+
/**
|
|
31
|
+
* The name to show for a mention (`@[Name](mention:<id>)`), e.g. the person's current
|
|
32
|
+
* or masked name. Return `undefined` to show the name stored in the text. Without it
|
|
33
|
+
* every mention shows its stored name.
|
|
34
|
+
*/
|
|
35
|
+
resolveMention?: (id: string, storedLabel: string) => string | undefined;
|
|
29
36
|
}
|
|
30
37
|
/**
|
|
31
38
|
* Safe markdown viewer over `.uix-prose`. It never produces an HTML string and never
|
|
@@ -35,81 +42,10 @@ interface MarkdownProps {
|
|
|
35
42
|
* Supports headings, paragraphs (single newline = line break), bold, italic,
|
|
36
43
|
* strikethrough, inline and fenced code, links and bare http(s) URLs, blockquotes,
|
|
37
44
|
* horizontal rules, nested bulleted/numbered lists, read-only task lists, GFM tables
|
|
38
|
-
* (in a keyboard-scrollable wrapper)
|
|
45
|
+
* (in a keyboard-scrollable wrapper), images gated by `resolveImageSrc` and mentions
|
|
46
|
+
* (`@[Name](mention:<id>)`, drawn as `.uix-mention`, named by `resolveMention`).
|
|
39
47
|
* Server-component safe.
|
|
40
48
|
*/
|
|
41
|
-
declare function Markdown({ children, className, isSafeUrl, resolveImageSrc, headingOffset }: MarkdownProps): react.JSX.Element | null;
|
|
42
|
-
|
|
43
|
-
type MarkdownInline = {
|
|
44
|
-
type: 'text';
|
|
45
|
-
value: string;
|
|
46
|
-
} | {
|
|
47
|
-
type: 'code';
|
|
48
|
-
value: string;
|
|
49
|
-
} | {
|
|
50
|
-
type: 'strong';
|
|
51
|
-
children: MarkdownInline[];
|
|
52
|
-
} | {
|
|
53
|
-
type: 'em';
|
|
54
|
-
children: MarkdownInline[];
|
|
55
|
-
} | {
|
|
56
|
-
type: 'del';
|
|
57
|
-
children: MarkdownInline[];
|
|
58
|
-
} | {
|
|
59
|
-
type: 'link';
|
|
60
|
-
href: string;
|
|
61
|
-
title?: string;
|
|
62
|
-
children: MarkdownInline[];
|
|
63
|
-
bare?: boolean;
|
|
64
|
-
} | {
|
|
65
|
-
type: 'image';
|
|
66
|
-
src: string;
|
|
67
|
-
alt: string;
|
|
68
|
-
title?: string;
|
|
69
|
-
} | {
|
|
70
|
-
type: 'br';
|
|
71
|
-
};
|
|
72
|
-
type TableAlign = 'left' | 'center' | 'right' | null;
|
|
73
|
-
interface MarkdownListItem {
|
|
74
|
-
/** `null` for a plain item, otherwise the task state. */
|
|
75
|
-
checked: boolean | null;
|
|
76
|
-
children: MarkdownInline[];
|
|
77
|
-
/** Nested lists. */
|
|
78
|
-
lists: MarkdownList[];
|
|
79
|
-
}
|
|
80
|
-
interface MarkdownList {
|
|
81
|
-
type: 'list';
|
|
82
|
-
ordered: boolean;
|
|
83
|
-
start: number;
|
|
84
|
-
items: MarkdownListItem[];
|
|
85
|
-
}
|
|
86
|
-
type MarkdownBlock = {
|
|
87
|
-
type: 'paragraph';
|
|
88
|
-
children: MarkdownInline[];
|
|
89
|
-
} | {
|
|
90
|
-
type: 'heading';
|
|
91
|
-
level: number;
|
|
92
|
-
children: MarkdownInline[];
|
|
93
|
-
} | {
|
|
94
|
-
type: 'code';
|
|
95
|
-
lang: string;
|
|
96
|
-
value: string;
|
|
97
|
-
} | {
|
|
98
|
-
type: 'quote';
|
|
99
|
-
children: MarkdownInline[];
|
|
100
|
-
} | {
|
|
101
|
-
type: 'rule';
|
|
102
|
-
} | MarkdownList | {
|
|
103
|
-
type: 'table';
|
|
104
|
-
align: TableAlign[];
|
|
105
|
-
head: MarkdownInline[][];
|
|
106
|
-
rows: MarkdownInline[][][];
|
|
107
|
-
};
|
|
108
|
-
/** Inline markdown to nodes. Newlines become line breaks. */
|
|
109
|
-
declare function parseInline(text: string, depth?: number): MarkdownInline[];
|
|
110
|
-
/** The text a reader sees, for accessible names and image alt text. */
|
|
111
|
-
declare function plainText(nodes: ReadonlyArray<MarkdownInline>): string;
|
|
112
|
-
/** Markdown source to blocks. Empty or whitespace-only input yields no blocks. */
|
|
113
|
-
declare function parseMarkdown(source: string): MarkdownBlock[];
|
|
49
|
+
declare function Markdown({ children, className, isSafeUrl, resolveImageSrc, headingOffset, resolveMention, }: MarkdownProps): react.JSX.Element | null;
|
|
114
50
|
|
|
115
|
-
export { Markdown, type
|
|
51
|
+
export { Markdown, type MarkdownProps, defaultMarkdownIsSafeUrl };
|
package/dist/markdown.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export { Markdown, MarkdownProps, defaultMarkdownIsSafeUrl } from './components/Markdown.js';
|
|
2
|
-
export { MarkdownBlock, MarkdownInline, MarkdownList, MarkdownListItem, TableAlign, parseInline, parseMarkdown, plainText } from './markdown-model.js';
|
|
2
|
+
export { MENTION_SCHEME, MarkdownBlock, MarkdownInline, MarkdownList, MarkdownListItem, TableAlign, findMentions, formatMention, isMentionId, parseInline, parseMarkdown, plainText, readMention } from './markdown-model.js';
|
|
3
3
|
import 'react';
|
|
4
4
|
import './url-policy.js';
|
package/dist/markdown.js
CHANGED
|
@@ -1,9 +1,23 @@
|
|
|
1
1
|
import { Markdown, defaultMarkdownIsSafeUrl } from "./components/Markdown.js";
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
parseMarkdown,
|
|
4
|
+
parseInline,
|
|
5
|
+
plainText,
|
|
6
|
+
findMentions,
|
|
7
|
+
formatMention,
|
|
8
|
+
readMention,
|
|
9
|
+
isMentionId,
|
|
10
|
+
MENTION_SCHEME
|
|
11
|
+
} from "./markdown-model.js";
|
|
3
12
|
export {
|
|
13
|
+
MENTION_SCHEME,
|
|
4
14
|
Markdown,
|
|
5
15
|
defaultMarkdownIsSafeUrl,
|
|
16
|
+
findMentions,
|
|
17
|
+
formatMention,
|
|
18
|
+
isMentionId,
|
|
6
19
|
parseInline,
|
|
7
20
|
parseMarkdown,
|
|
8
|
-
plainText
|
|
21
|
+
plainText,
|
|
22
|
+
readMention
|
|
9
23
|
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Node } from '@tiptap/core';
|
|
2
|
+
|
|
3
|
+
/** A person the consumer offers in the `@` listbox. */
|
|
4
|
+
interface MentionCandidate {
|
|
5
|
+
/** Stored in the text; letters, digits and `_ . -` only (see `isMentionId`). */
|
|
6
|
+
id: string;
|
|
7
|
+
/** The name inserted and shown. */
|
|
8
|
+
label: string;
|
|
9
|
+
/** A second line in the listbox (e.g. team or e-mail), never stored. */
|
|
10
|
+
description?: string;
|
|
11
|
+
}
|
|
12
|
+
declare const Mention: Node<any, any>;
|
|
13
|
+
|
|
14
|
+
export { Mention, type MentionCandidate };
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { mergeAttributes, Node } from "@tiptap/core";
|
|
2
|
+
import { formatMention, readMention } from "../markdown-model.js";
|
|
3
|
+
const Mention = Node.create({
|
|
4
|
+
name: "mention",
|
|
5
|
+
group: "inline",
|
|
6
|
+
inline: true,
|
|
7
|
+
atom: true,
|
|
8
|
+
selectable: false,
|
|
9
|
+
addAttributes() {
|
|
10
|
+
return {
|
|
11
|
+
id: { default: null },
|
|
12
|
+
label: { default: null }
|
|
13
|
+
};
|
|
14
|
+
},
|
|
15
|
+
parseHTML() {
|
|
16
|
+
return [{
|
|
17
|
+
tag: "span[data-mention-id]",
|
|
18
|
+
getAttrs: (el) => {
|
|
19
|
+
const node = el;
|
|
20
|
+
const id = node.getAttribute("data-mention-id") ?? "";
|
|
21
|
+
const label = node.getAttribute("data-mention-label") ?? node.textContent?.replace(/^@/, "") ?? "";
|
|
22
|
+
return formatMention({ id, label }) ? { id, label } : false;
|
|
23
|
+
}
|
|
24
|
+
}];
|
|
25
|
+
},
|
|
26
|
+
renderHTML({ node, HTMLAttributes }) {
|
|
27
|
+
const label = String(node.attrs.label ?? "");
|
|
28
|
+
return [
|
|
29
|
+
"span",
|
|
30
|
+
mergeAttributes(HTMLAttributes, {
|
|
31
|
+
class: "uix-mention",
|
|
32
|
+
"data-mention-id": String(node.attrs.id ?? ""),
|
|
33
|
+
"data-mention-label": label,
|
|
34
|
+
contenteditable: "false"
|
|
35
|
+
}),
|
|
36
|
+
`@${label}`
|
|
37
|
+
];
|
|
38
|
+
},
|
|
39
|
+
renderText({ node }) {
|
|
40
|
+
return `@${String(node.attrs.label ?? "")}`;
|
|
41
|
+
},
|
|
42
|
+
markdownTokenizer: {
|
|
43
|
+
name: "mention",
|
|
44
|
+
level: "inline",
|
|
45
|
+
start: (src) => src.indexOf("@["),
|
|
46
|
+
tokenize: (src) => {
|
|
47
|
+
const found = readMention(src);
|
|
48
|
+
if (!found) return void 0;
|
|
49
|
+
return { type: "mention", raw: src.slice(0, found.length), id: found.id, label: found.label };
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
parseMarkdown: (token, helpers) => helpers.createNode("mention", { id: String(token.id ?? ""), label: String(token.label ?? "") }),
|
|
53
|
+
renderMarkdown: (node) => formatMention({ id: String(node.attrs?.id ?? ""), label: String(node.attrs?.label ?? "") }) ?? `@${String(node.attrs?.label ?? "")}`
|
|
54
|
+
});
|
|
55
|
+
export {
|
|
56
|
+
Mention
|
|
57
|
+
};
|
|
@@ -6,6 +6,7 @@ import { Image } from "@tiptap/extension-image";
|
|
|
6
6
|
import { StarterKit } from "@tiptap/starter-kit";
|
|
7
7
|
import { Marked } from "marked";
|
|
8
8
|
import { defaultIsSafeUrl } from "../url-policy.js";
|
|
9
|
+
import { Mention } from "./mention.js";
|
|
9
10
|
const indentContinuation = (h) => {
|
|
10
11
|
let first = true;
|
|
11
12
|
return {
|
|
@@ -60,7 +61,9 @@ function createSchemaExtensions(options = {}) {
|
|
|
60
61
|
TaskList,
|
|
61
62
|
UixTaskItem.configure({ nested: true }),
|
|
62
63
|
TableKit.configure({ table: { resizable: false } }),
|
|
63
|
-
options.image ?? Image.configure({ inline: true, allowBase64: false })
|
|
64
|
+
options.image ?? Image.configure({ inline: true, allowBase64: false }),
|
|
65
|
+
// HAR-1599: `@[Name](mention:<id>)` is one token in every editor and headless pipeline.
|
|
66
|
+
Mention
|
|
64
67
|
];
|
|
65
68
|
}
|
|
66
69
|
const ENTITY_AFTER_AMP = /^(?:#\d+;|#x[\da-f]+;|[a-z][a-z\d]*;)/i;
|