@ng-native/components 0.5.0 → 0.6.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/markdown-blocks.d.ts +79 -0
- package/dist/markdown-blocks.js +218 -0
- package/dist/markdown-blocks.js.map +1 -0
- package/dist/markdown.d.ts +63 -0
- package/dist/markdown.js +338 -0
- package/dist/markdown.js.map +1 -0
- package/dist/virtual-list.d.ts +4 -0
- package/dist/virtual-list.js +60 -33
- package/dist/virtual-list.js.map +1 -1
- package/package.json +14 -5
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Marked's tokens, turned into the blocks `<markdown>` draws: the entities decoded, the links and
|
|
3
|
+
* images held to the schemes they may use, raw HTML made plain text, and an image lifted out of
|
|
4
|
+
* its paragraph, since a `<text>` cannot hold one.
|
|
5
|
+
*
|
|
6
|
+
* Plain functions with no Angular in them, so the policy is the same wherever the tokens came from:
|
|
7
|
+
* `marked.lexer` on the device, or a build step that lexed them ahead of time.
|
|
8
|
+
*/
|
|
9
|
+
import type { Token } from 'marked';
|
|
10
|
+
/** The elements a document is drawn with, by the HTML names the Markdown would have produced. */
|
|
11
|
+
export type MarkdownElement = 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' | 'p' | 'blockquote' | 'ul' | 'ol' | 'li' | 'marker' | 'pre' | 'code' | 'strong' | 'em' | 'del' | 'a' | 'hr' | 'img' | 'table' | 'tr' | 'th' | 'td';
|
|
12
|
+
export type MarkdownInline = {
|
|
13
|
+
readonly kind: 'text';
|
|
14
|
+
readonly text: string;
|
|
15
|
+
} | {
|
|
16
|
+
readonly kind: 'strong' | 'em' | 'del';
|
|
17
|
+
readonly children: readonly MarkdownInline[];
|
|
18
|
+
} | {
|
|
19
|
+
readonly kind: 'code';
|
|
20
|
+
readonly text: string;
|
|
21
|
+
} | {
|
|
22
|
+
readonly kind: 'link';
|
|
23
|
+
readonly href: string;
|
|
24
|
+
readonly title: string | null;
|
|
25
|
+
readonly children: readonly MarkdownInline[];
|
|
26
|
+
};
|
|
27
|
+
export interface MarkdownListItem {
|
|
28
|
+
readonly marker: string;
|
|
29
|
+
readonly children: readonly MarkdownBlock[];
|
|
30
|
+
}
|
|
31
|
+
export type MarkdownBlock = {
|
|
32
|
+
readonly kind: 'heading';
|
|
33
|
+
readonly element: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6';
|
|
34
|
+
readonly children: readonly MarkdownInline[];
|
|
35
|
+
} | {
|
|
36
|
+
readonly kind: 'paragraph';
|
|
37
|
+
readonly children: readonly MarkdownInline[];
|
|
38
|
+
} | {
|
|
39
|
+
readonly kind: 'image';
|
|
40
|
+
readonly src: string;
|
|
41
|
+
readonly alt: string;
|
|
42
|
+
} | {
|
|
43
|
+
readonly kind: 'blockquote';
|
|
44
|
+
readonly children: readonly MarkdownBlock[];
|
|
45
|
+
} | {
|
|
46
|
+
readonly kind: 'list';
|
|
47
|
+
readonly element: 'ul' | 'ol';
|
|
48
|
+
readonly items: readonly MarkdownListItem[];
|
|
49
|
+
} | {
|
|
50
|
+
readonly kind: 'code';
|
|
51
|
+
readonly text: string;
|
|
52
|
+
} | {
|
|
53
|
+
readonly kind: 'hr';
|
|
54
|
+
} | {
|
|
55
|
+
readonly kind: 'table';
|
|
56
|
+
readonly header: readonly (readonly MarkdownInline[])[];
|
|
57
|
+
readonly rows: readonly (readonly (readonly MarkdownInline[])[])[];
|
|
58
|
+
};
|
|
59
|
+
/**
|
|
60
|
+
* The text an HTML reader would show for `text`, in one pass so `&lt;` is `<` and not `<`.
|
|
61
|
+
* A reference to no character, or to one outside Unicode, is the replacement character, as HTML
|
|
62
|
+
* makes it; a name not in the table stays as written.
|
|
63
|
+
*/
|
|
64
|
+
export declare function decodeEntities(text: string): string;
|
|
65
|
+
/** The scheme `url` would be opened with, lowercased, or null for a relative one. */
|
|
66
|
+
export declare function schemeOf(url: string): string | null;
|
|
67
|
+
/**
|
|
68
|
+
* A link's target if it may be followed: `http`, `https`, `mailto`, `tel`, or relative, which is
|
|
69
|
+
* the app's to resolve. Anything else, `javascript:`, `data:`, `vbscript:` and `file:` among
|
|
70
|
+
* them, is null, and the link is drawn as its text. The scheme is read after the trim, so
|
|
71
|
+
* whitespace the trim strips cannot hide one.
|
|
72
|
+
*/
|
|
73
|
+
export declare function linkTarget(href: string): string | null;
|
|
74
|
+
/** Whether `href` is absolute in a scheme a link may be followed to, and so is opened on press. */
|
|
75
|
+
export declare function opensOnPress(href: string): boolean;
|
|
76
|
+
/** An image's address if it may be loaded: `http` or `https` only. */
|
|
77
|
+
export declare function imageSource(href: string): string | null;
|
|
78
|
+
/** The blocks a document of `tokens` is drawn as. */
|
|
79
|
+
export declare function markdownBlocks(tokens: readonly Token[]): MarkdownBlock[];
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
/** The named references marked leaves in text, beside the numeric ones every reference can use. */
|
|
2
|
+
const NAMED_ENTITIES = {
|
|
3
|
+
amp: '&',
|
|
4
|
+
lt: '<',
|
|
5
|
+
gt: '>',
|
|
6
|
+
quot: '"',
|
|
7
|
+
apos: "'",
|
|
8
|
+
nbsp: ' ',
|
|
9
|
+
copy: '©',
|
|
10
|
+
reg: '®',
|
|
11
|
+
trade: '™',
|
|
12
|
+
hellip: '…',
|
|
13
|
+
mdash: '—',
|
|
14
|
+
ndash: '–',
|
|
15
|
+
lsquo: '‘',
|
|
16
|
+
rsquo: '’',
|
|
17
|
+
ldquo: '“',
|
|
18
|
+
rdquo: '”',
|
|
19
|
+
laquo: '«',
|
|
20
|
+
raquo: '»',
|
|
21
|
+
middot: '·',
|
|
22
|
+
bull: '•',
|
|
23
|
+
deg: '°',
|
|
24
|
+
times: '×',
|
|
25
|
+
};
|
|
26
|
+
const ENTITY = /&(?:#(\d{1,7})|#[xX]([0-9a-fA-F]{1,6})|([a-zA-Z][a-zA-Z0-9]{1,31}));/g;
|
|
27
|
+
/**
|
|
28
|
+
* The text an HTML reader would show for `text`, in one pass so `&lt;` is `<` and not `<`.
|
|
29
|
+
* A reference to no character, or to one outside Unicode, is the replacement character, as HTML
|
|
30
|
+
* makes it; a name not in the table stays as written.
|
|
31
|
+
*/
|
|
32
|
+
export function decodeEntities(text) {
|
|
33
|
+
if (!text.includes('&'))
|
|
34
|
+
return text;
|
|
35
|
+
return text.replace(ENTITY, (whole, decimal, hex, name) => {
|
|
36
|
+
if (name !== undefined)
|
|
37
|
+
return NAMED_ENTITIES[name] ?? whole;
|
|
38
|
+
const code = decimal !== undefined ? Number(decimal) : parseInt(hex, 16);
|
|
39
|
+
const valid = code > 0 && code <= 0x10ffff && (code < 0xd800 || code > 0xdfff);
|
|
40
|
+
return valid ? String.fromCodePoint(code) : '�';
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* What a URL parser ignores while it reads a scheme: the C0 controls and the space, and the C1
|
|
45
|
+
* controls and DEL beside them. `java\tscript:` is `javascript:` to the thing that opens it.
|
|
46
|
+
*/
|
|
47
|
+
const IGNORED_IN_SCHEME = /[\u0000- \u007f-\u009f]/g;
|
|
48
|
+
const SCHEME = /^([a-z][a-z0-9+.-]*):/;
|
|
49
|
+
/** The scheme `url` would be opened with, lowercased, or null for a relative one. */
|
|
50
|
+
export function schemeOf(url) {
|
|
51
|
+
return SCHEME.exec(url.replace(IGNORED_IN_SCHEME, '').toLowerCase())?.[1] ?? null;
|
|
52
|
+
}
|
|
53
|
+
const LINK_SCHEMES = new Set(['http', 'https', 'mailto', 'tel']);
|
|
54
|
+
const IMAGE_SCHEMES = new Set(['http', 'https']);
|
|
55
|
+
/**
|
|
56
|
+
* A link's target if it may be followed: `http`, `https`, `mailto`, `tel`, or relative, which is
|
|
57
|
+
* the app's to resolve. Anything else, `javascript:`, `data:`, `vbscript:` and `file:` among
|
|
58
|
+
* them, is null, and the link is drawn as its text. The scheme is read after the trim, so
|
|
59
|
+
* whitespace the trim strips cannot hide one.
|
|
60
|
+
*/
|
|
61
|
+
export function linkTarget(href) {
|
|
62
|
+
const target = href.trim();
|
|
63
|
+
const scheme = schemeOf(target);
|
|
64
|
+
return scheme === null || LINK_SCHEMES.has(scheme) ? target : null;
|
|
65
|
+
}
|
|
66
|
+
/** Whether `href` is absolute in a scheme a link may be followed to, and so is opened on press. */
|
|
67
|
+
export function opensOnPress(href) {
|
|
68
|
+
const scheme = schemeOf(href.trim());
|
|
69
|
+
return scheme !== null && LINK_SCHEMES.has(scheme);
|
|
70
|
+
}
|
|
71
|
+
/** An image's address if it may be loaded: `http` or `https` only. */
|
|
72
|
+
export function imageSource(href) {
|
|
73
|
+
const source = href.trim();
|
|
74
|
+
const scheme = schemeOf(source);
|
|
75
|
+
return scheme !== null && IMAGE_SCHEMES.has(scheme) ? source : null;
|
|
76
|
+
}
|
|
77
|
+
/** A link's or an image's target with its entities decoded, or null when it is not a string. */
|
|
78
|
+
function hrefOf(token) {
|
|
79
|
+
const href = token.href;
|
|
80
|
+
return typeof href === 'string' ? decodeEntities(href) : null;
|
|
81
|
+
}
|
|
82
|
+
const HEADINGS = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'];
|
|
83
|
+
/** The blocks a document of `tokens` is drawn as. */
|
|
84
|
+
export function markdownBlocks(tokens) {
|
|
85
|
+
return tokens.flatMap(block);
|
|
86
|
+
}
|
|
87
|
+
/** What each kind of block token is drawn as. A kind not here is drawn as its source text. */
|
|
88
|
+
const BLOCKS = {
|
|
89
|
+
heading: (token) => {
|
|
90
|
+
const heading = token;
|
|
91
|
+
const depth = Math.min(Math.max(heading.depth, 1), 6);
|
|
92
|
+
return [{ kind: 'heading', element: HEADINGS[depth - 1], children: inlines(heading.tokens) }];
|
|
93
|
+
},
|
|
94
|
+
paragraph: (token) => paragraphs(token.tokens),
|
|
95
|
+
text: (token) => {
|
|
96
|
+
const text = token;
|
|
97
|
+
return text.tokens
|
|
98
|
+
? paragraphs(text.tokens)
|
|
99
|
+
: [{ kind: 'paragraph', children: [plain(text.text)] }];
|
|
100
|
+
},
|
|
101
|
+
blockquote: (token) => [
|
|
102
|
+
{ kind: 'blockquote', children: markdownBlocks(token.tokens) },
|
|
103
|
+
],
|
|
104
|
+
list: (token) => [list(token)],
|
|
105
|
+
code: (token) => [{ kind: 'code', text: indented(token.text) }],
|
|
106
|
+
hr: () => [{ kind: 'hr' }],
|
|
107
|
+
table: (token) => {
|
|
108
|
+
const table = token;
|
|
109
|
+
return [
|
|
110
|
+
{
|
|
111
|
+
kind: 'table',
|
|
112
|
+
header: table.header.map((cell) => inlines(cell.tokens)),
|
|
113
|
+
rows: table.rows.map((row) => row.map((cell) => inlines(cell.tokens))),
|
|
114
|
+
},
|
|
115
|
+
];
|
|
116
|
+
},
|
|
117
|
+
html: (token) => literal(token.text),
|
|
118
|
+
space: () => [],
|
|
119
|
+
def: () => [],
|
|
120
|
+
checkbox: () => [],
|
|
121
|
+
};
|
|
122
|
+
function block(token) {
|
|
123
|
+
const draw = Object.hasOwn(BLOCKS, token.type) ? BLOCKS[token.type] : null;
|
|
124
|
+
return draw ? draw(token) : literal(token.raw ?? '');
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* A code block's text with its first line's indent made of no-break spaces: a paragraph loses the
|
|
128
|
+
* spaces at its start, and a block's indent is part of the code.
|
|
129
|
+
*/
|
|
130
|
+
function indented(text) {
|
|
131
|
+
return text.replace(/^ +/, (spaces) => '\u00a0'.repeat(spaces.length));
|
|
132
|
+
}
|
|
133
|
+
/** Text drawn as written, which is what raw HTML and a token nothing here knows become. */
|
|
134
|
+
function literal(text) {
|
|
135
|
+
const trimmed = text.replace(/\n+$/, '');
|
|
136
|
+
return trimmed.trim() ? [{ kind: 'paragraph', children: [{ kind: 'text', text: trimmed }] }] : [];
|
|
137
|
+
}
|
|
138
|
+
function list(token) {
|
|
139
|
+
const start = typeof token.start === 'number' ? token.start : 1;
|
|
140
|
+
return {
|
|
141
|
+
kind: 'list',
|
|
142
|
+
element: token.ordered ? 'ol' : 'ul',
|
|
143
|
+
items: token.items.map((item, index) => ({
|
|
144
|
+
marker: item.task ? (item.checked ? '☑' : '☐') : token.ordered ? `${start + index}.` : '•',
|
|
145
|
+
children: markdownBlocks(item.tokens),
|
|
146
|
+
})),
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* A paragraph's runs, split around the images at its top level: an image is a block of its own,
|
|
151
|
+
* and the text either side of it a paragraph each. An image whose address is refused, and one
|
|
152
|
+
* inside a link or an emphasis, is its alt text instead.
|
|
153
|
+
*/
|
|
154
|
+
function paragraphs(tokens) {
|
|
155
|
+
const out = [];
|
|
156
|
+
let run = [];
|
|
157
|
+
const flush = () => {
|
|
158
|
+
if (run.some((one) => one.kind !== 'text' || one.text.trim())) {
|
|
159
|
+
out.push({ kind: 'paragraph', children: run });
|
|
160
|
+
}
|
|
161
|
+
run = [];
|
|
162
|
+
};
|
|
163
|
+
for (const token of tokens) {
|
|
164
|
+
const href = token.type === 'image' ? hrefOf(token) : null;
|
|
165
|
+
const src = href === null ? null : imageSource(href);
|
|
166
|
+
if (src === null) {
|
|
167
|
+
run.push(...inline(token));
|
|
168
|
+
continue;
|
|
169
|
+
}
|
|
170
|
+
flush();
|
|
171
|
+
out.push({ kind: 'image', src, alt: decodeEntities(token.text) });
|
|
172
|
+
}
|
|
173
|
+
flush();
|
|
174
|
+
return out;
|
|
175
|
+
}
|
|
176
|
+
function inlines(tokens) {
|
|
177
|
+
return (tokens ?? []).flatMap(inline);
|
|
178
|
+
}
|
|
179
|
+
function plain(text) {
|
|
180
|
+
return { kind: 'text', text: decodeEntities(text) };
|
|
181
|
+
}
|
|
182
|
+
function emphasis(token) {
|
|
183
|
+
const kind = token.type;
|
|
184
|
+
return [{ kind, children: inlines(token.tokens) }];
|
|
185
|
+
}
|
|
186
|
+
/** What each kind of inline token is drawn as. A kind not here is drawn as its source text. */
|
|
187
|
+
const INLINES = {
|
|
188
|
+
text: (token) => {
|
|
189
|
+
const text = token;
|
|
190
|
+
return text.tokens ? inlines(text.tokens) : [plain(text.text)];
|
|
191
|
+
},
|
|
192
|
+
escape: (token) => [plain(token.text)],
|
|
193
|
+
strong: emphasis,
|
|
194
|
+
em: emphasis,
|
|
195
|
+
del: emphasis,
|
|
196
|
+
codespan: (token) => [{ kind: 'code', text: token.text }],
|
|
197
|
+
br: () => [{ kind: 'text', text: '\n' }],
|
|
198
|
+
link: (token) => {
|
|
199
|
+
const link = token;
|
|
200
|
+
const children = inlines(link.tokens);
|
|
201
|
+
const decoded = hrefOf(link);
|
|
202
|
+
const href = decoded === null ? null : linkTarget(decoded);
|
|
203
|
+
if (href === null)
|
|
204
|
+
return children;
|
|
205
|
+
const title = link.title ? decodeEntities(link.title) : null;
|
|
206
|
+
return [{ kind: 'link', href, title, children }];
|
|
207
|
+
},
|
|
208
|
+
image: (token) => [plain(token.text)],
|
|
209
|
+
html: (token) => [{ kind: 'text', text: token.text }],
|
|
210
|
+
checkbox: () => [],
|
|
211
|
+
};
|
|
212
|
+
function inline(token) {
|
|
213
|
+
const draw = Object.hasOwn(INLINES, token.type) ? INLINES[token.type] : null;
|
|
214
|
+
if (draw)
|
|
215
|
+
return draw(token);
|
|
216
|
+
return token.raw ? [{ kind: 'text', text: token.raw }] : [];
|
|
217
|
+
}
|
|
218
|
+
//# sourceMappingURL=markdown-blocks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"markdown-blocks.js","sourceRoot":"","sources":["../src/markdown-blocks.ts"],"names":[],"mappings":"AA2EA,mGAAmG;AACnG,MAAM,cAAc,GAAqC;IACvD,GAAG,EAAE,GAAG;IACR,EAAE,EAAE,GAAG;IACP,EAAE,EAAE,GAAG;IACP,IAAI,EAAE,GAAG;IACT,IAAI,EAAE,GAAG;IACT,IAAI,EAAE,GAAG;IACT,IAAI,EAAE,GAAG;IACT,GAAG,EAAE,GAAG;IACR,KAAK,EAAE,GAAG;IACV,MAAM,EAAE,GAAG;IACX,KAAK,EAAE,GAAG;IACV,KAAK,EAAE,GAAG;IACV,KAAK,EAAE,GAAG;IACV,KAAK,EAAE,GAAG;IACV,KAAK,EAAE,GAAG;IACV,KAAK,EAAE,GAAG;IACV,KAAK,EAAE,GAAG;IACV,KAAK,EAAE,GAAG;IACV,MAAM,EAAE,GAAG;IACX,IAAI,EAAE,GAAG;IACT,GAAG,EAAE,GAAG;IACR,KAAK,EAAE,GAAG;CACX,CAAC;AAEF,MAAM,MAAM,GAAG,uEAAuE,CAAC;AAEvF;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAAC,IAAY;IACzC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IACrC,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,OAAgB,EAAE,GAAY,EAAE,IAAa,EAAE,EAAE;QACnF,IAAI,IAAI,KAAK,SAAS;YAAE,OAAO,cAAc,CAAC,IAAI,CAAC,IAAI,KAAK,CAAC;QAC7D,MAAM,IAAI,GAAG,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,GAAI,EAAE,EAAE,CAAC,CAAC;QAC1E,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC,IAAI,IAAI,IAAI,QAAQ,IAAI,CAAC,IAAI,GAAG,MAAM,IAAI,IAAI,GAAG,MAAM,CAAC,CAAC;QAC/E,OAAO,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;IAClD,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;;GAGG;AACH,MAAM,iBAAiB,GAAG,0BAA0B,CAAC;AACrD,MAAM,MAAM,GAAG,uBAAuB,CAAC;AAEvC,qFAAqF;AACrF,MAAM,UAAU,QAAQ,CAAC,GAAW;IAClC,OAAO,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,iBAAiB,EAAE,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC;AACpF,CAAC;AAED,MAAM,YAAY,GAAG,IAAI,GAAG,CAAC,CAAC,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,KAAK,CAAC,CAAC,CAAC;AACjE,MAAM,aAAa,GAAG,IAAI,GAAG,CAAC,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;AAEjD;;;;;GAKG;AACH,MAAM,UAAU,UAAU,CAAC,IAAY;IACrC,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;IAC3B,MAAM,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC;IAChC,OAAO,MAAM,KAAK,IAAI,IAAI,YAAY,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC;AACrE,CAAC;AAED,mGAAmG;AACnG,MAAM,UAAU,YAAY,CAAC,IAAY;IACvC,MAAM,MAAM,GAAG,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;IACrC,OAAO,MAAM,KAAK,IAAI,IAAI,YAAY,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;AACrD,CAAC;AAED,sEAAsE;AACtE,MAAM,UAAU,WAAW,CAAC,IAAY;IACtC,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;IAC3B,MAAM,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC;IAChC,OAAO,MAAM,KAAK,IAAI,IAAI,aAAa,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC;AACtE,CAAC;AAED,gGAAgG;AAChG,SAAS,MAAM,CAAC,KAAY;IAC1B,MAAM,IAAI,GAAa,KAAqB,CAAC,IAAI,CAAC;IAClD,OAAO,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AAChE,CAAC;AAED,MAAM,QAAQ,GAAG,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAU,CAAC;AAE/D,qDAAqD;AACrD,MAAM,UAAU,cAAc,CAAC,MAAwB;IACrD,OAAO,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;AAC/B,CAAC;AAED,8FAA8F;AAC9F,MAAM,MAAM,GAAgE;IAC1E,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE;QACjB,MAAM,OAAO,GAAG,KAAuB,CAAC;QACxC,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QACtD,OAAO,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,QAAQ,CAAC,KAAK,GAAG,CAAC,CAAE,EAAE,QAAQ,EAAE,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IACjG,CAAC;IACD,SAAS,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,UAAU,CAAE,KAA0B,CAAC,MAAM,CAAC;IACpE,IAAI,EAAE,CAAC,KAAK,EAAE,EAAE;QACd,MAAM,IAAI,GAAG,KAAoB,CAAC;QAClC,OAAO,IAAI,CAAC,MAAM;YAChB,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC;YACzB,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,QAAQ,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC;IAC5D,CAAC;IACD,UAAU,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC;QACrB,EAAE,IAAI,EAAE,YAAY,EAAE,QAAQ,EAAE,cAAc,CAAE,KAA2B,CAAC,MAAM,CAAC,EAAE;KACtF;IACD,IAAI,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,KAAoB,CAAC,CAAC;IAC7C,IAAI,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,CAAE,KAAqB,CAAC,IAAI,CAAC,EAAE,CAAC;IAChF,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;IAC1B,KAAK,EAAE,CAAC,KAAK,EAAE,EAAE;QACf,MAAM,KAAK,GAAG,KAAqB,CAAC;QACpC,OAAO;YACL;gBACE,IAAI,EAAE,OAAO;gBACb,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;gBACxD,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC;aACvE;SACF,CAAC;IACJ,CAAC;IACD,IAAI,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,OAAO,CAAE,KAAqB,CAAC,IAAI,CAAC;IACrD,KAAK,EAAE,GAAG,EAAE,CAAC,EAAE;IACf,GAAG,EAAE,GAAG,EAAE,CAAC,EAAE;IACb,QAAQ,EAAE,GAAG,EAAE,CAAC,EAAE;CACnB,CAAC;AAEF,SAAS,KAAK,CAAC,KAAY;IACzB,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAE,CAAC,CAAC,CAAC,IAAI,CAAC;IAC5E,OAAO,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,IAAI,EAAE,CAAC,CAAC;AACvD,CAAC;AAED;;;GAGG;AACH,SAAS,QAAQ,CAAC,IAAY;IAC5B,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC;AACzE,CAAC;AAED,2FAA2F;AAC3F,SAAS,OAAO,CAAC,IAAY;IAC3B,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IACzC,OAAO,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;AACpG,CAAC;AAED,SAAS,IAAI,CAAC,KAAkB;IAC9B,MAAM,KAAK,GAAG,OAAO,KAAK,CAAC,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IAChE,OAAO;QACL,IAAI,EAAE,MAAM;QACZ,OAAO,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI;QACpC,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC;YACvC,MAAM,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,KAAK,GAAG,KAAK,GAAG,CAAC,CAAC,CAAC,GAAG;YAC1F,QAAQ,EAAE,cAAc,CAAC,IAAI,CAAC,MAAM,CAAC;SACtC,CAAC,CAAC;KACJ,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,SAAS,UAAU,CAAC,MAAwB;IAC1C,MAAM,GAAG,GAAoB,EAAE,CAAC;IAChC,IAAI,GAAG,GAAqB,EAAE,CAAC;IAC/B,MAAM,KAAK,GAAG,GAAG,EAAE;QACjB,IAAI,GAAG,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,KAAK,MAAM,IAAI,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC;YAC9D,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,QAAQ,EAAE,GAAG,EAAE,CAAC,CAAC;QACjD,CAAC;QACD,GAAG,GAAG,EAAE,CAAC;IACX,CAAC,CAAC;IACF,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;QAC3D,MAAM,GAAG,GAAG,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;QACrD,IAAI,GAAG,KAAK,IAAI,EAAE,CAAC;YACjB,GAAG,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;YAC3B,SAAS;QACX,CAAC;QACD,KAAK,EAAE,CAAC;QACR,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,EAAE,cAAc,CAAE,KAAsB,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACtF,CAAC;IACD,KAAK,EAAE,CAAC;IACR,OAAO,GAAG,CAAC;AACb,CAAC;AAED,SAAS,OAAO,CAAC,MAAoC;IACnD,OAAO,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;AACxC,CAAC;AAED,SAAS,KAAK,CAAC,IAAY;IACzB,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,cAAc,CAAC,IAAI,CAAC,EAAE,CAAC;AACtD,CAAC;AAED,SAAS,QAAQ,CAAC,KAAY;IAC5B,MAAM,IAAI,GAAG,KAAK,CAAC,IAA+B,CAAC;IACnD,OAAO,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,CAAE,KAAuB,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;AACxE,CAAC;AAED,+FAA+F;AAC/F,MAAM,OAAO,GAAiE;IAC5E,IAAI,EAAE,CAAC,KAAK,EAAE,EAAE;QACd,MAAM,IAAI,GAAG,KAAoB,CAAC;QAClC,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IACjE,CAAC;IACD,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,KAAK,CAAE,KAAuB,CAAC,IAAI,CAAC,CAAC;IACzD,MAAM,EAAE,QAAQ;IAChB,EAAE,EAAE,QAAQ;IACZ,GAAG,EAAE,QAAQ;IACb,QAAQ,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAG,KAAyB,CAAC,IAAI,EAAE,CAAC;IAC9E,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;IACxC,IAAI,EAAE,CAAC,KAAK,EAAE,EAAE;QACd,MAAM,IAAI,GAAG,KAAoB,CAAC;QAClC,MAAM,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACtC,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC;QAC7B,MAAM,IAAI,GAAG,OAAO,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;QAC3D,IAAI,IAAI,KAAK,IAAI;YAAE,OAAO,QAAQ,CAAC;QACnC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,cAAc,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;QAC7D,OAAO,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC;IACnD,CAAC;IACD,KAAK,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,KAAK,CAAE,KAAsB,CAAC,IAAI,CAAC,CAAC;IACvD,IAAI,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAG,KAAqB,CAAC,IAAI,EAAE,CAAC;IACtE,QAAQ,EAAE,GAAG,EAAE,CAAC,EAAE;CACnB,CAAC;AAEF,SAAS,MAAM,CAAC,KAAY;IAC1B,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAE,CAAC,CAAC,CAAC,IAAI,CAAC;IAC9E,IAAI,IAAI;QAAE,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC;IAC7B,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;AAC9D,CAAC","sourcesContent":["/**\n * Marked's tokens, turned into the blocks `<markdown>` draws: the entities decoded, the links and\n * images held to the schemes they may use, raw HTML made plain text, and an image lifted out of\n * its paragraph, since a `<text>` cannot hold one.\n *\n * Plain functions with no Angular in them, so the policy is the same wherever the tokens came from:\n * `marked.lexer` on the device, or a build step that lexed them ahead of time.\n */\nimport type { Token, Tokens } from 'marked';\n\n/** The elements a document is drawn with, by the HTML names the Markdown would have produced. */\nexport type MarkdownElement =\n | 'h1'\n | 'h2'\n | 'h3'\n | 'h4'\n | 'h5'\n | 'h6'\n | 'p'\n | 'blockquote'\n | 'ul'\n | 'ol'\n | 'li'\n | 'marker'\n | 'pre'\n | 'code'\n | 'strong'\n | 'em'\n | 'del'\n | 'a'\n | 'hr'\n | 'img'\n | 'table'\n | 'tr'\n | 'th'\n | 'td';\n\nexport type MarkdownInline =\n | { readonly kind: 'text'; readonly text: string }\n | { readonly kind: 'strong' | 'em' | 'del'; readonly children: readonly MarkdownInline[] }\n | { readonly kind: 'code'; readonly text: string }\n | {\n readonly kind: 'link';\n readonly href: string;\n readonly title: string | null;\n readonly children: readonly MarkdownInline[];\n };\n\nexport interface MarkdownListItem {\n readonly marker: string;\n readonly children: readonly MarkdownBlock[];\n}\n\nexport type MarkdownBlock =\n | {\n readonly kind: 'heading';\n readonly element: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6';\n readonly children: readonly MarkdownInline[];\n }\n | { readonly kind: 'paragraph'; readonly children: readonly MarkdownInline[] }\n | { readonly kind: 'image'; readonly src: string; readonly alt: string }\n | { readonly kind: 'blockquote'; readonly children: readonly MarkdownBlock[] }\n | {\n readonly kind: 'list';\n readonly element: 'ul' | 'ol';\n readonly items: readonly MarkdownListItem[];\n }\n | { readonly kind: 'code'; readonly text: string }\n | { readonly kind: 'hr' }\n | {\n readonly kind: 'table';\n readonly header: readonly (readonly MarkdownInline[])[];\n readonly rows: readonly (readonly (readonly MarkdownInline[])[])[];\n };\n\n/** The named references marked leaves in text, beside the numeric ones every reference can use. */\nconst NAMED_ENTITIES: Readonly<Record<string, string>> = {\n amp: '&',\n lt: '<',\n gt: '>',\n quot: '\"',\n apos: \"'\",\n nbsp: ' ',\n copy: '©',\n reg: '®',\n trade: '™',\n hellip: '…',\n mdash: '—',\n ndash: '–',\n lsquo: '‘',\n rsquo: '’',\n ldquo: '“',\n rdquo: '”',\n laquo: '«',\n raquo: '»',\n middot: '·',\n bull: '•',\n deg: '°',\n times: '×',\n};\n\nconst ENTITY = /&(?:#(\\d{1,7})|#[xX]([0-9a-fA-F]{1,6})|([a-zA-Z][a-zA-Z0-9]{1,31}));/g;\n\n/**\n * The text an HTML reader would show for `text`, in one pass so `&lt;` is `<` and not `<`.\n * A reference to no character, or to one outside Unicode, is the replacement character, as HTML\n * makes it; a name not in the table stays as written.\n */\nexport function decodeEntities(text: string): string {\n if (!text.includes('&')) return text;\n return text.replace(ENTITY, (whole, decimal?: string, hex?: string, name?: string) => {\n if (name !== undefined) return NAMED_ENTITIES[name] ?? whole;\n const code = decimal !== undefined ? Number(decimal) : parseInt(hex!, 16);\n const valid = code > 0 && code <= 0x10ffff && (code < 0xd800 || code > 0xdfff);\n return valid ? String.fromCodePoint(code) : '�';\n });\n}\n\n/**\n * What a URL parser ignores while it reads a scheme: the C0 controls and the space, and the C1\n * controls and DEL beside them. `java\\tscript:` is `javascript:` to the thing that opens it.\n */\nconst IGNORED_IN_SCHEME = /[\\u0000- \\u007f-\\u009f]/g;\nconst SCHEME = /^([a-z][a-z0-9+.-]*):/;\n\n/** The scheme `url` would be opened with, lowercased, or null for a relative one. */\nexport function schemeOf(url: string): string | null {\n return SCHEME.exec(url.replace(IGNORED_IN_SCHEME, '').toLowerCase())?.[1] ?? null;\n}\n\nconst LINK_SCHEMES = new Set(['http', 'https', 'mailto', 'tel']);\nconst IMAGE_SCHEMES = new Set(['http', 'https']);\n\n/**\n * A link's target if it may be followed: `http`, `https`, `mailto`, `tel`, or relative, which is\n * the app's to resolve. Anything else, `javascript:`, `data:`, `vbscript:` and `file:` among\n * them, is null, and the link is drawn as its text. The scheme is read after the trim, so\n * whitespace the trim strips cannot hide one.\n */\nexport function linkTarget(href: string): string | null {\n const target = href.trim();\n const scheme = schemeOf(target);\n return scheme === null || LINK_SCHEMES.has(scheme) ? target : null;\n}\n\n/** Whether `href` is absolute in a scheme a link may be followed to, and so is opened on press. */\nexport function opensOnPress(href: string): boolean {\n const scheme = schemeOf(href.trim());\n return scheme !== null && LINK_SCHEMES.has(scheme);\n}\n\n/** An image's address if it may be loaded: `http` or `https` only. */\nexport function imageSource(href: string): string | null {\n const source = href.trim();\n const scheme = schemeOf(source);\n return scheme !== null && IMAGE_SCHEMES.has(scheme) ? source : null;\n}\n\n/** A link's or an image's target with its entities decoded, or null when it is not a string. */\nfunction hrefOf(token: Token): string | null {\n const href: unknown = (token as Tokens.Link).href;\n return typeof href === 'string' ? decodeEntities(href) : null;\n}\n\nconst HEADINGS = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'] as const;\n\n/** The blocks a document of `tokens` is drawn as. */\nexport function markdownBlocks(tokens: readonly Token[]): MarkdownBlock[] {\n return tokens.flatMap(block);\n}\n\n/** What each kind of block token is drawn as. A kind not here is drawn as its source text. */\nconst BLOCKS: Readonly<Record<string, (token: Token) => MarkdownBlock[]>> = {\n heading: (token) => {\n const heading = token as Tokens.Heading;\n const depth = Math.min(Math.max(heading.depth, 1), 6);\n return [{ kind: 'heading', element: HEADINGS[depth - 1]!, children: inlines(heading.tokens) }];\n },\n paragraph: (token) => paragraphs((token as Tokens.Paragraph).tokens),\n text: (token) => {\n const text = token as Tokens.Text;\n return text.tokens\n ? paragraphs(text.tokens)\n : [{ kind: 'paragraph', children: [plain(text.text)] }];\n },\n blockquote: (token) => [\n { kind: 'blockquote', children: markdownBlocks((token as Tokens.Blockquote).tokens) },\n ],\n list: (token) => [list(token as Tokens.List)],\n code: (token) => [{ kind: 'code', text: indented((token as Tokens.Code).text) }],\n hr: () => [{ kind: 'hr' }],\n table: (token) => {\n const table = token as Tokens.Table;\n return [\n {\n kind: 'table',\n header: table.header.map((cell) => inlines(cell.tokens)),\n rows: table.rows.map((row) => row.map((cell) => inlines(cell.tokens))),\n },\n ];\n },\n html: (token) => literal((token as Tokens.HTML).text),\n space: () => [],\n def: () => [],\n checkbox: () => [],\n};\n\nfunction block(token: Token): MarkdownBlock[] {\n const draw = Object.hasOwn(BLOCKS, token.type) ? BLOCKS[token.type]! : null;\n return draw ? draw(token) : literal(token.raw ?? '');\n}\n\n/**\n * A code block's text with its first line's indent made of no-break spaces: a paragraph loses the\n * spaces at its start, and a block's indent is part of the code.\n */\nfunction indented(text: string): string {\n return text.replace(/^ +/, (spaces) => '\\u00a0'.repeat(spaces.length));\n}\n\n/** Text drawn as written, which is what raw HTML and a token nothing here knows become. */\nfunction literal(text: string): MarkdownBlock[] {\n const trimmed = text.replace(/\\n+$/, '');\n return trimmed.trim() ? [{ kind: 'paragraph', children: [{ kind: 'text', text: trimmed }] }] : [];\n}\n\nfunction list(token: Tokens.List): MarkdownBlock {\n const start = typeof token.start === 'number' ? token.start : 1;\n return {\n kind: 'list',\n element: token.ordered ? 'ol' : 'ul',\n items: token.items.map((item, index) => ({\n marker: item.task ? (item.checked ? '☑' : '☐') : token.ordered ? `${start + index}.` : '•',\n children: markdownBlocks(item.tokens),\n })),\n };\n}\n\n/**\n * A paragraph's runs, split around the images at its top level: an image is a block of its own,\n * and the text either side of it a paragraph each. An image whose address is refused, and one\n * inside a link or an emphasis, is its alt text instead.\n */\nfunction paragraphs(tokens: readonly Token[]): MarkdownBlock[] {\n const out: MarkdownBlock[] = [];\n let run: MarkdownInline[] = [];\n const flush = () => {\n if (run.some((one) => one.kind !== 'text' || one.text.trim())) {\n out.push({ kind: 'paragraph', children: run });\n }\n run = [];\n };\n for (const token of tokens) {\n const href = token.type === 'image' ? hrefOf(token) : null;\n const src = href === null ? null : imageSource(href);\n if (src === null) {\n run.push(...inline(token));\n continue;\n }\n flush();\n out.push({ kind: 'image', src, alt: decodeEntities((token as Tokens.Image).text) });\n }\n flush();\n return out;\n}\n\nfunction inlines(tokens: readonly Token[] | undefined): MarkdownInline[] {\n return (tokens ?? []).flatMap(inline);\n}\n\nfunction plain(text: string): MarkdownInline {\n return { kind: 'text', text: decodeEntities(text) };\n}\n\nfunction emphasis(token: Token): MarkdownInline[] {\n const kind = token.type as 'strong' | 'em' | 'del';\n return [{ kind, children: inlines((token as Tokens.Strong).tokens) }];\n}\n\n/** What each kind of inline token is drawn as. A kind not here is drawn as its source text. */\nconst INLINES: Readonly<Record<string, (token: Token) => MarkdownInline[]>> = {\n text: (token) => {\n const text = token as Tokens.Text;\n return text.tokens ? inlines(text.tokens) : [plain(text.text)];\n },\n escape: (token) => [plain((token as Tokens.Escape).text)],\n strong: emphasis,\n em: emphasis,\n del: emphasis,\n codespan: (token) => [{ kind: 'code', text: (token as Tokens.Codespan).text }],\n br: () => [{ kind: 'text', text: '\\n' }],\n link: (token) => {\n const link = token as Tokens.Link;\n const children = inlines(link.tokens);\n const decoded = hrefOf(link);\n const href = decoded === null ? null : linkTarget(decoded);\n if (href === null) return children;\n const title = link.title ? decodeEntities(link.title) : null;\n return [{ kind: 'link', href, title, children }];\n },\n image: (token) => [plain((token as Tokens.Image).text)],\n html: (token) => [{ kind: 'text', text: (token as Tokens.HTML).text }],\n checkbox: () => [],\n};\n\nfunction inline(token: Token): MarkdownInline[] {\n const draw = Object.hasOwn(INLINES, token.type) ? INLINES[token.type]! : null;\n if (draw) return draw(token);\n return token.raw ? [{ kind: 'text', text: token.raw }] : [];\n}\n"]}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { type Token } from 'marked';
|
|
2
|
+
import type { ImageLoadEvent } from './events.ts';
|
|
3
|
+
import { type MarkdownBlock, type MarkdownElement, type MarkdownInline } from './markdown-blocks.ts';
|
|
4
|
+
import * as i0 from "@angular/core";
|
|
5
|
+
export type { MarkdownElement } from './markdown-blocks.ts';
|
|
6
|
+
/** Classes for the elements a document is drawn with, each one in place of the default. */
|
|
7
|
+
export type MarkdownClasses = Partial<Readonly<Record<MarkdownElement, string>>>;
|
|
8
|
+
/** `(linkPress)`: a link was pressed. */
|
|
9
|
+
export interface MarkdownLinkPress {
|
|
10
|
+
/** The target as written, after its entities are decoded and its scheme was allowed. */
|
|
11
|
+
readonly href: string;
|
|
12
|
+
readonly title: string | null;
|
|
13
|
+
/** Keep the link from being opened, to handle it in the app instead. */
|
|
14
|
+
preventDefault(): void;
|
|
15
|
+
}
|
|
16
|
+
type MarkdownLink = Extract<MarkdownInline, {
|
|
17
|
+
kind: 'link';
|
|
18
|
+
}>;
|
|
19
|
+
/**
|
|
20
|
+
* A Markdown document as `<text>`, `<view>`, `<image>` and `<scroll-view>`: headings, paragraphs,
|
|
21
|
+
* emphasis, inline code, links, quotes, ordered, unordered and task lists, code blocks, rules,
|
|
22
|
+
* tables and images.
|
|
23
|
+
*
|
|
24
|
+
* `source` is parsed with `marked.lexer`; `tokens` takes tokens lexed already, and wins when both
|
|
25
|
+
* are set. A paragraph is one `<text>` with a nested `<text>` for each emphasis, code span and
|
|
26
|
+
* link, so it wraps as one run; an image is lifted out to a block of its own.
|
|
27
|
+
*
|
|
28
|
+
* Nothing in the document is interpreted. Raw HTML is drawn as the text it is written as, a link
|
|
29
|
+
* is followed only to `http`, `https`, `mailto`, `tel` or a relative target, and an image is
|
|
30
|
+
* loaded only over `http` or `https`; anything else is drawn as its text.
|
|
31
|
+
*
|
|
32
|
+
* Each element carries a default class, `md-h1`, `md-p`, `md-a` and so on, styled in light and
|
|
33
|
+
* dark. `classes` replaces one by its element's name, so a global stylesheet or Tailwind styles it
|
|
34
|
+
* instead: `{ h1: 'text-3xl font-bold' }`. Put the default back beside your own to keep both.
|
|
35
|
+
*
|
|
36
|
+
* A pressed link is `(linkPress)`. An absolute target is then opened with `DeepLinks.open` unless
|
|
37
|
+
* a handler calls `preventDefault()`; a relative one is only reported, for the app to route.
|
|
38
|
+
*/
|
|
39
|
+
export declare class Markdown {
|
|
40
|
+
private readonly injector;
|
|
41
|
+
/** Which monospace font code is drawn in, as the platform names it. */
|
|
42
|
+
protected readonly platform: string;
|
|
43
|
+
/** The Markdown to draw. */
|
|
44
|
+
readonly source: import("@angular/core").InputSignal<string | undefined>;
|
|
45
|
+
/** Tokens `marked.lexer` made already, drawn in place of `source`. */
|
|
46
|
+
readonly tokens: import("@angular/core").InputSignal<readonly Token[] | undefined>;
|
|
47
|
+
/** A class for an element, in place of its default. See the class note. */
|
|
48
|
+
readonly classes: import("@angular/core").InputSignal<Partial<Readonly<Record<MarkdownElement, string>>>>;
|
|
49
|
+
/** A link was pressed. See the class note. */
|
|
50
|
+
readonly linkPress: import("@angular/core").OutputEmitterRef<MarkdownLinkPress>;
|
|
51
|
+
protected readonly blocks: import("@angular/core").Signal<MarkdownBlock[]>;
|
|
52
|
+
private readonly classOf;
|
|
53
|
+
/**
|
|
54
|
+
* Each image's width over its height, once it has loaded, so it is drawn at the page's width and
|
|
55
|
+
* its own shape. An image the document no longer has is forgotten when the document changes.
|
|
56
|
+
*/
|
|
57
|
+
protected readonly ratios: import("@angular/core").WritableSignal<ReadonlyMap<string, number>>;
|
|
58
|
+
protected classFor(element: MarkdownElement): string;
|
|
59
|
+
protected sizeImage(src: string, event: Partial<ImageLoadEvent>): void;
|
|
60
|
+
protected followLink(link: MarkdownLink): void;
|
|
61
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<Markdown, never>;
|
|
62
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<Markdown, "markdown", never, { "source": { "alias": "source"; "required": false; "isSignal": true; }; "tokens": { "alias": "tokens"; "required": false; "isSignal": true; }; "classes": { "alias": "classes"; "required": false; "isSignal": true; }; }, { "linkPress": "linkPress"; }, never, never, true, never>;
|
|
63
|
+
}
|