@localix-ai/flowdown 0.0.0-stage → 0.5.1
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/LICENSE +21 -0
- package/README.md +105 -2
- package/dist/browser/file-actions.js +80 -0
- package/dist/browser/file-actions.js.map +1 -0
- package/dist/core/block-facts.js +72 -0
- package/dist/core/block-facts.js.map +1 -0
- package/dist/core/blocks.d.ts +25 -0
- package/dist/core/blocks.js +464 -0
- package/dist/core/blocks.js.map +1 -0
- package/dist/core/bounded-cache.js +42 -0
- package/dist/core/bounded-cache.js.map +1 -0
- package/dist/core/continues.js +15 -0
- package/dist/core/continues.js.map +1 -0
- package/dist/core/dollars.d.ts +12 -0
- package/dist/core/dollars.js +88 -0
- package/dist/core/dollars.js.map +1 -0
- package/dist/core/fence-scan.js +62 -0
- package/dist/core/fence-scan.js.map +1 -0
- package/dist/core/file-tree-parse.d.ts +26 -0
- package/dist/core/file-tree-parse.js +51 -0
- package/dist/core/file-tree-parse.js.map +1 -0
- package/dist/core/forms-html.js +63 -0
- package/dist/core/forms-html.js.map +1 -0
- package/dist/core/forms.d.ts +28 -0
- package/dist/core/forms.js +23 -0
- package/dist/core/forms.js.map +1 -0
- package/dist/core/headings.d.ts +11 -0
- package/dist/core/headings.js +74 -0
- package/dist/core/headings.js.map +1 -0
- package/dist/core/heal.d.ts +11 -0
- package/dist/core/heal.js +220 -0
- package/dist/core/heal.js.map +1 -0
- package/dist/core/load-once.js +23 -0
- package/dist/core/load-once.js.map +1 -0
- package/dist/core/math-delimiters.js +33 -0
- package/dist/core/math-delimiters.js.map +1 -0
- package/dist/core/math-syntax.js +56 -0
- package/dist/core/math-syntax.js.map +1 -0
- package/dist/core/prose.d.ts +10 -0
- package/dist/core/prose.js +77 -0
- package/dist/core/prose.js.map +1 -0
- package/dist/core/remark-answer-blocks.d.ts +5 -0
- package/dist/core/remark-answer-blocks.js +172 -0
- package/dist/core/remark-answer-blocks.js.map +1 -0
- package/dist/core/remark-answer-containers.js +299 -0
- package/dist/core/remark-answer-containers.js.map +1 -0
- package/dist/core/sanitize-schema.d.ts +18 -0
- package/dist/core/sanitize-schema.js +62 -0
- package/dist/core/sanitize-schema.js.map +1 -0
- package/dist/core/syntax.d.ts +8 -0
- package/dist/core/syntax.js +32 -0
- package/dist/core/syntax.js.map +1 -0
- package/dist/dialect.d.ts +92 -0
- package/dist/dialect.js +176 -0
- package/dist/dialect.js.map +1 -0
- package/dist/document/fence.d.ts +25 -0
- package/dist/document/fence.js +102 -0
- package/dist/document/fence.js.map +1 -0
- package/dist/document/index.d.ts +46 -0
- package/dist/document/index.js +66 -0
- package/dist/document/index.js.map +1 -0
- package/dist/document/table.d.ts +11 -0
- package/dist/document/table.js +35 -0
- package/dist/document/table.js.map +1 -0
- package/dist/dom/index.d.ts +21 -0
- package/dist/dom/index.js +141 -0
- package/dist/dom/index.js.map +1 -0
- package/dist/dom/patch.js +104 -0
- package/dist/dom/patch.js.map +1 -0
- package/dist/element/index.d.ts +35 -0
- package/dist/element/index.js +64 -0
- package/dist/element/index.js.map +1 -0
- package/dist/engine/blocks.js +785 -0
- package/dist/engine/blocks.js.map +1 -0
- package/dist/engine/entities.js +83 -0
- package/dist/engine/entities.js.map +1 -0
- package/dist/engine/entity-core.js +9 -0
- package/dist/engine/entity-core.js.map +1 -0
- package/dist/engine/entity-table.js +9 -0
- package/dist/engine/entity-table.js.map +1 -0
- package/dist/engine/html.d.ts +7 -0
- package/dist/engine/html.js +207 -0
- package/dist/engine/html.js.map +1 -0
- package/dist/engine/index.d.ts +7 -0
- package/dist/engine/index.js +74 -0
- package/dist/engine/index.js.map +1 -0
- package/dist/engine/inlines.js +887 -0
- package/dist/engine/inlines.js.map +1 -0
- package/dist/engine/types.d.ts +109 -0
- package/dist/engine/url.d.ts +11 -0
- package/dist/engine/url.js +16 -0
- package/dist/engine/url.js.map +1 -0
- package/dist/engine/visit.d.ts +10 -0
- package/dist/engine/visit.js +29 -0
- package/dist/engine/visit.js.map +1 -0
- package/dist/highlight/highlight.d.ts +4 -0
- package/dist/highlight/highlight.js +144 -0
- package/dist/highlight/highlight.js.map +1 -0
- package/dist/highlight/index.d.ts +4 -0
- package/dist/highlight/index.js +18 -0
- package/dist/highlight/index.js.map +1 -0
- package/dist/html/index.d.ts +4 -0
- package/dist/html/index.js +544 -0
- package/dist/html/index.js.map +1 -0
- package/dist/index.d.ts +27 -0
- package/dist/index.js +13 -0
- package/dist/math/index.d.ts +17 -0
- package/dist/math/index.js +20 -0
- package/dist/math/index.js.map +1 -0
- package/dist/math/typeset.d.ts +12 -0
- package/dist/math/typeset.js +74 -0
- package/dist/math/typeset.js.map +1 -0
- package/dist/mermaid/diagram.d.ts +8 -0
- package/dist/mermaid/diagram.js +114 -0
- package/dist/mermaid/diagram.js.map +1 -0
- package/dist/mermaid/drawing.js +139 -0
- package/dist/mermaid/drawing.js.map +1 -0
- package/dist/mermaid/icons.js +16 -0
- package/dist/mermaid/icons.js.map +1 -0
- package/dist/mermaid/index.d.ts +13 -0
- package/dist/mermaid/index.js +3 -0
- package/dist/mermaid/loader.d.ts +5 -0
- package/dist/mermaid/loader.js +18 -0
- package/dist/mermaid/loader.js.map +1 -0
- package/dist/mermaid/look.d.ts +16 -0
- package/dist/mermaid/look.js +167 -0
- package/dist/mermaid/look.js.map +1 -0
- package/dist/mermaid/normalize-svg.js +44 -0
- package/dist/mermaid/normalize-svg.js.map +1 -0
- package/dist/mermaid/renderer.d.ts +22 -0
- package/dist/mermaid/renderer.js +132 -0
- package/dist/mermaid/renderer.js.map +1 -0
- package/dist/mermaid/viewer.js +309 -0
- package/dist/mermaid/viewer.js.map +1 -0
- package/dist/prompt/index.d.ts +19 -0
- package/dist/prompt/index.js +54 -0
- package/dist/prompt/index.js.map +1 -0
- package/dist/react/callout.d.ts +9 -0
- package/dist/react/callout.js +12 -0
- package/dist/react/callout.js.map +1 -0
- package/dist/react/code-block.d.ts +18 -0
- package/dist/react/code-block.js +94 -0
- package/dist/react/code-block.js.map +1 -0
- package/dist/react/context.d.ts +18 -0
- package/dist/react/context.js +17 -0
- package/dist/react/context.js.map +1 -0
- package/dist/react/draw.d.ts +2 -0
- package/dist/react/draw.js +17 -0
- package/dist/react/draw.js.map +1 -0
- package/dist/react/file-tree.d.ts +16 -0
- package/dist/react/file-tree.js +69 -0
- package/dist/react/file-tree.js.map +1 -0
- package/dist/react/growing-code.js +36 -0
- package/dist/react/growing-code.js.map +1 -0
- package/dist/react/helpers.d.ts +6 -0
- package/dist/react/helpers.js +17 -0
- package/dist/react/helpers.js.map +1 -0
- package/dist/react/icons.js +23 -0
- package/dist/react/icons.js.map +1 -0
- package/dist/react/labels.js +9 -0
- package/dist/react/labels.js.map +1 -0
- package/dist/react/markdown.d.ts +78 -0
- package/dist/react/markdown.js +206 -0
- package/dist/react/markdown.js.map +1 -0
- package/dist/react/render.d.ts +8 -0
- package/dist/react/render.js +41 -0
- package/dist/react/render.js.map +1 -0
- package/dist/react/scrolls.js +62 -0
- package/dist/react/scrolls.js.map +1 -0
- package/dist/react/table-rows.js +66 -0
- package/dist/react/table-rows.js.map +1 -0
- package/dist/react/table.js +22 -0
- package/dist/react/table.js.map +1 -0
- package/dist/react/tabs.d.ts +25 -0
- package/dist/react/tabs.js +58 -0
- package/dist/react/tabs.js.map +1 -0
- package/dist/react/use-smooth-text.d.ts +6 -0
- package/dist/react/use-smooth-text.js +157 -0
- package/dist/react/use-smooth-text.js.map +1 -0
- package/dist/react/use-stage.d.ts +9 -0
- package/dist/react/use-stage.js +57 -0
- package/dist/react/use-stage.js.map +1 -0
- package/dist/remark.d.ts +20 -0
- package/dist/remark.js +10 -0
- package/dist/render/factory.d.ts +15 -0
- package/dist/render/labels.d.ts +46 -0
- package/dist/render/labels.js +46 -0
- package/dist/render/labels.js.map +1 -0
- package/dist/render/tree.js +314 -0
- package/dist/render/tree.js.map +1 -0
- package/dist/render/view.d.ts +58 -0
- package/dist/render/view.js +306 -0
- package/dist/render/view.js.map +1 -0
- package/dist/render/views.d.ts +16 -0
- package/dist/render/views.js +261 -0
- package/dist/render/views.js.map +1 -0
- package/dist/render/vnode.d.ts +21 -0
- package/dist/render/vnode.js +194 -0
- package/dist/render/vnode.js.map +1 -0
- package/dist/static/index.d.ts +28 -0
- package/dist/static/index.js +27 -0
- package/dist/static/index.js.map +1 -0
- package/dist/stream.d.ts +12 -0
- package/dist/stream.js +3 -0
- package/dist/styles.css +1362 -0
- package/dist/tui/code.d.ts +5 -0
- package/dist/tui/code.js +240 -0
- package/dist/tui/code.js.map +1 -0
- package/dist/tui/index.d.ts +54 -0
- package/dist/tui/index.js +99 -0
- package/dist/tui/index.js.map +1 -0
- package/dist/tui/math.js +242 -0
- package/dist/tui/math.js.map +1 -0
- package/dist/tui/printable.d.ts +12 -0
- package/dist/tui/printable.js +31 -0
- package/dist/tui/printable.js.map +1 -0
- package/dist/tui/render.d.ts +12 -0
- package/dist/tui/render.js +524 -0
- package/dist/tui/render.js.map +1 -0
- package/dist/tui/style.d.ts +35 -0
- package/dist/tui/style.js +77 -0
- package/dist/tui/style.js.map +1 -0
- package/dist/tui/table.js +73 -0
- package/dist/tui/table.js.map +1 -0
- package/dist/tui/width.d.ts +2 -0
- package/dist/tui/width.js +133 -0
- package/dist/tui/width.js.map +1 -0
- package/dist/tui/wrap.d.ts +8 -0
- package/dist/tui/wrap.js +191 -0
- package/dist/tui/wrap.js.map +1 -0
- package/package.json +128 -4
|
@@ -0,0 +1,544 @@
|
|
|
1
|
+
import { addEntities, decodeEntity } from "../engine/entities.js";
|
|
2
|
+
import { ENTITY_TABLE } from "../engine/entity-table.js";
|
|
3
|
+
// src/html/index.ts
|
|
4
|
+
/**
|
|
5
|
+
* `@localix-ai/flowdown/html`: raw HTML in Markdown (`<details>`, `<kbd>`, a README's `<p align>`), made
|
|
6
|
+
* into elements.
|
|
7
|
+
*
|
|
8
|
+
* Nothing in the text is ever handed to the browser as HTML. The tags are read here, and an
|
|
9
|
+
* element is made only for a tag on the list below, with only the attributes on the list below,
|
|
10
|
+
* so there is no second parser to disagree with and nothing to sanitize afterwards: `<script>`,
|
|
11
|
+
* `style`, `onclick` and `javascript:` have no way through. The lists are GitHub's.
|
|
12
|
+
*
|
|
13
|
+
* `<Markdown>` fetches this entry by itself the first time a document holds HTML; pass it
|
|
14
|
+
* directly where the HTML must be in the first render, as on a server:
|
|
15
|
+
*
|
|
16
|
+
* import { html } from '@localix-ai/flowdown/html'
|
|
17
|
+
* <Markdown html={html}>{readme}</Markdown>
|
|
18
|
+
*/
|
|
19
|
+
/** The elements raw HTML may make. Any other tag is dropped and what it holds is kept. */
|
|
20
|
+
const TAGS = new Set("a b blockquote br code dd del details div dl dt em h1 h2 h3 h4 h5 h6 hr i img input ins kbd li ol p picture pre q rp rt ruby s samp section source span strike strong sub summary sup table tbody td tfoot th thead tr tt ul var aside figure figcaption footer answer-tabs answer-tab".split(" "));
|
|
21
|
+
/** Elements that never have children. */
|
|
22
|
+
const VOID = new Set("br hr img input source area base col embed link meta param track wbr".split(" "));
|
|
23
|
+
/** Dropped with everything inside them. */
|
|
24
|
+
const DROPPED = /* @__PURE__ */ new Set(["script", "style"]);
|
|
25
|
+
/** Elements whose inside is text, not tags: the tag goes, the text stays. */
|
|
26
|
+
const RAW_TEXT = new Set("textarea title xmp iframe noembed noframes plaintext".split(" "));
|
|
27
|
+
/** Table parts mean nothing outside a table. */
|
|
28
|
+
const NEEDS_TABLE = new Set("tbody td tfoot th thead tr".split(" "));
|
|
29
|
+
/** Elements a paragraph cannot hold: a paragraph that gets one is drawn as a `<div>`. */
|
|
30
|
+
const BLOCK = new Set("blockquote details div dl h1 h2 h3 h4 h5 h6 hr ol p pre section table ul aside figure footer".split(" "));
|
|
31
|
+
/** Opening one of these ends the open element it cannot sit inside. */
|
|
32
|
+
const CLOSES = {
|
|
33
|
+
li: {
|
|
34
|
+
close: ["li"],
|
|
35
|
+
within: ["ul", "ol"]
|
|
36
|
+
},
|
|
37
|
+
dt: {
|
|
38
|
+
close: ["dt", "dd"],
|
|
39
|
+
within: ["dl"]
|
|
40
|
+
},
|
|
41
|
+
dd: {
|
|
42
|
+
close: ["dt", "dd"],
|
|
43
|
+
within: ["dl"]
|
|
44
|
+
},
|
|
45
|
+
tr: {
|
|
46
|
+
close: ["tr"],
|
|
47
|
+
within: [
|
|
48
|
+
"table",
|
|
49
|
+
"thead",
|
|
50
|
+
"tbody",
|
|
51
|
+
"tfoot"
|
|
52
|
+
]
|
|
53
|
+
},
|
|
54
|
+
td: {
|
|
55
|
+
close: ["td", "th"],
|
|
56
|
+
within: ["tr"]
|
|
57
|
+
},
|
|
58
|
+
th: {
|
|
59
|
+
close: ["td", "th"],
|
|
60
|
+
within: ["tr"]
|
|
61
|
+
},
|
|
62
|
+
thead: {
|
|
63
|
+
close: [
|
|
64
|
+
"thead",
|
|
65
|
+
"tbody",
|
|
66
|
+
"tfoot"
|
|
67
|
+
],
|
|
68
|
+
within: ["table"]
|
|
69
|
+
},
|
|
70
|
+
tbody: {
|
|
71
|
+
close: [
|
|
72
|
+
"thead",
|
|
73
|
+
"tbody",
|
|
74
|
+
"tfoot"
|
|
75
|
+
],
|
|
76
|
+
within: ["table"]
|
|
77
|
+
},
|
|
78
|
+
tfoot: {
|
|
79
|
+
close: [
|
|
80
|
+
"thead",
|
|
81
|
+
"tbody",
|
|
82
|
+
"tfoot"
|
|
83
|
+
],
|
|
84
|
+
within: ["table"]
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
/** Attributes any element may carry, by the name HTML writes: the property React takes. */
|
|
88
|
+
const GLOBAL = {
|
|
89
|
+
abbr: "abbr",
|
|
90
|
+
accept: "accept",
|
|
91
|
+
"accept-charset": "acceptCharset",
|
|
92
|
+
accesskey: "accessKey",
|
|
93
|
+
action: "action",
|
|
94
|
+
align: "align",
|
|
95
|
+
alt: "alt",
|
|
96
|
+
axis: "axis",
|
|
97
|
+
border: "border",
|
|
98
|
+
cellpadding: "cellPadding",
|
|
99
|
+
cellspacing: "cellSpacing",
|
|
100
|
+
char: "char",
|
|
101
|
+
charoff: "charoff",
|
|
102
|
+
charset: "charSet",
|
|
103
|
+
checked: "checked",
|
|
104
|
+
clear: "clear",
|
|
105
|
+
colspan: "colSpan",
|
|
106
|
+
color: "color",
|
|
107
|
+
cols: "cols",
|
|
108
|
+
compact: "compact",
|
|
109
|
+
coords: "coords",
|
|
110
|
+
datetime: "dateTime",
|
|
111
|
+
dir: "dir",
|
|
112
|
+
enctype: "encType",
|
|
113
|
+
frame: "frame",
|
|
114
|
+
hspace: "hspace",
|
|
115
|
+
headers: "headers",
|
|
116
|
+
height: "height",
|
|
117
|
+
hreflang: "hrefLang",
|
|
118
|
+
for: "htmlFor",
|
|
119
|
+
id: "id",
|
|
120
|
+
ismap: "ismap",
|
|
121
|
+
itemprop: "itemProp",
|
|
122
|
+
label: "label",
|
|
123
|
+
lang: "lang",
|
|
124
|
+
maxlength: "maxLength",
|
|
125
|
+
media: "media",
|
|
126
|
+
method: "method",
|
|
127
|
+
multiple: "multiple",
|
|
128
|
+
name: "name",
|
|
129
|
+
nohref: "nohref",
|
|
130
|
+
noshade: "noshade",
|
|
131
|
+
nowrap: "nowrap",
|
|
132
|
+
open: "open",
|
|
133
|
+
prompt: "prompt",
|
|
134
|
+
readonly: "readOnly",
|
|
135
|
+
rev: "rev",
|
|
136
|
+
rowspan: "rowSpan",
|
|
137
|
+
rows: "rows",
|
|
138
|
+
rules: "rules",
|
|
139
|
+
scope: "scope",
|
|
140
|
+
selected: "selected",
|
|
141
|
+
shape: "shape",
|
|
142
|
+
size: "size",
|
|
143
|
+
span: "span",
|
|
144
|
+
start: "start",
|
|
145
|
+
summary: "summary",
|
|
146
|
+
tabindex: "tabIndex",
|
|
147
|
+
title: "title",
|
|
148
|
+
usemap: "useMap",
|
|
149
|
+
valign: "valign",
|
|
150
|
+
value: "value",
|
|
151
|
+
width: "width",
|
|
152
|
+
"data-callout": "data-callout",
|
|
153
|
+
"data-callout-title": "data-callout-title",
|
|
154
|
+
"data-verdict": "data-verdict",
|
|
155
|
+
"data-label": "data-label",
|
|
156
|
+
"data-title": "data-title"
|
|
157
|
+
};
|
|
158
|
+
const ARIA = {
|
|
159
|
+
"aria-describedby": "aria-describedby",
|
|
160
|
+
"aria-label": "aria-label",
|
|
161
|
+
"aria-labelledby": "aria-labelledby"
|
|
162
|
+
};
|
|
163
|
+
/** Attributes only some elements may carry. */
|
|
164
|
+
const OWN = {
|
|
165
|
+
a: {
|
|
166
|
+
...ARIA,
|
|
167
|
+
"data-footnote-backref": "data-footnote-backref",
|
|
168
|
+
"data-footnote-ref": "data-footnote-ref",
|
|
169
|
+
href: "href",
|
|
170
|
+
target: "target",
|
|
171
|
+
rel: "rel"
|
|
172
|
+
},
|
|
173
|
+
blockquote: { cite: "cite" },
|
|
174
|
+
del: { cite: "cite" },
|
|
175
|
+
ins: { cite: "cite" },
|
|
176
|
+
q: { cite: "cite" },
|
|
177
|
+
div: {
|
|
178
|
+
itemscope: "itemScope",
|
|
179
|
+
itemtype: "itemType"
|
|
180
|
+
},
|
|
181
|
+
dl: ARIA,
|
|
182
|
+
ol: ARIA,
|
|
183
|
+
ul: ARIA,
|
|
184
|
+
summary: ARIA,
|
|
185
|
+
table: ARIA,
|
|
186
|
+
img: {
|
|
187
|
+
...ARIA,
|
|
188
|
+
longdesc: "longdesc",
|
|
189
|
+
src: "src"
|
|
190
|
+
},
|
|
191
|
+
section: { "data-footnotes": "data-footnotes" },
|
|
192
|
+
source: {
|
|
193
|
+
srcset: "srcSet",
|
|
194
|
+
media: "media",
|
|
195
|
+
type: "type"
|
|
196
|
+
}
|
|
197
|
+
};
|
|
198
|
+
/** Attributes that are on or off, as React wants them: `true`, not the empty string. */
|
|
199
|
+
const BOOLEAN = /* @__PURE__ */ new Set([
|
|
200
|
+
"checked",
|
|
201
|
+
"multiple",
|
|
202
|
+
"open",
|
|
203
|
+
"readOnly",
|
|
204
|
+
"selected",
|
|
205
|
+
"itemScope"
|
|
206
|
+
]);
|
|
207
|
+
/** The classes content may set: the dialect's own on any element, and these on theirs. */
|
|
208
|
+
const CLASSES = {
|
|
209
|
+
code: /^(?:language-.+|math-inline|math-display)$/,
|
|
210
|
+
ol: /^contains-task-list$/,
|
|
211
|
+
ul: /^contains-task-list$/,
|
|
212
|
+
li: /^task-list-item$/,
|
|
213
|
+
a: /^data-footnote-backref$/,
|
|
214
|
+
h2: /^sr-only$/,
|
|
215
|
+
section: /^footnotes$/
|
|
216
|
+
};
|
|
217
|
+
const DIALECT_CLASS = /^fd-/;
|
|
218
|
+
/** Where a URL may lead, by attribute. A URL with no scheme is the page's own and passes. */
|
|
219
|
+
const PROTOCOLS = {
|
|
220
|
+
href: /^(?:https?|ircs?|mailto|xmpp)$/i,
|
|
221
|
+
src: /^https?$/i,
|
|
222
|
+
cite: /^https?$/i,
|
|
223
|
+
longdesc: /^https?$/i
|
|
224
|
+
};
|
|
225
|
+
/** Ids and the attributes that point at them are prefixed, so content cannot stand in for one of the page's own. */
|
|
226
|
+
const PREFIXED = /* @__PURE__ */ new Set([
|
|
227
|
+
"id",
|
|
228
|
+
"name",
|
|
229
|
+
"aria-describedby",
|
|
230
|
+
"aria-labelledby"
|
|
231
|
+
]);
|
|
232
|
+
const PREFIX = "user-content-";
|
|
233
|
+
const safeUrl = (url, allowed) => {
|
|
234
|
+
const colon = url.indexOf(":");
|
|
235
|
+
if (colon === -1) return true;
|
|
236
|
+
const slash = url.indexOf("/");
|
|
237
|
+
const question = url.indexOf("?");
|
|
238
|
+
const hash = url.indexOf("#");
|
|
239
|
+
if (slash !== -1 && colon > slash || question !== -1 && colon > question || hash !== -1 && colon > hash) return true;
|
|
240
|
+
return allowed.test(url.slice(0, colon));
|
|
241
|
+
};
|
|
242
|
+
const reEntity = /&(?:#x[a-f0-9]{1,6}|#[0-9]{1,7}|[a-z][a-z0-9]{1,31});/gi;
|
|
243
|
+
const decode = (text) => text.includes("&") ? text.replace(reEntity, (entity) => decodeEntity(entity) ?? entity) : text;
|
|
244
|
+
/** The properties an element of raw HTML is drawn with: only what the lists allow. */
|
|
245
|
+
function properties(tag, attributes) {
|
|
246
|
+
if (tag === "input") {
|
|
247
|
+
const checked = attributes.some(([name]) => name === "checked");
|
|
248
|
+
return {
|
|
249
|
+
type: "checkbox",
|
|
250
|
+
disabled: true,
|
|
251
|
+
...checked ? { checked } : null
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
let out;
|
|
255
|
+
const own = OWN[tag];
|
|
256
|
+
for (const [name, raw] of attributes) {
|
|
257
|
+
let value = raw;
|
|
258
|
+
if (name === "class") {
|
|
259
|
+
const allowed = CLASSES[tag];
|
|
260
|
+
const kept = value.split(/\s+/).filter((token) => token !== "" && (DIALECT_CLASS.test(token) || allowed?.test(token)));
|
|
261
|
+
if (kept.length > 0) (out ??= {}).className = kept.join(" ");
|
|
262
|
+
continue;
|
|
263
|
+
}
|
|
264
|
+
const property = own?.[name] ?? GLOBAL[name];
|
|
265
|
+
if (property === void 0 || out !== void 0 && property in out) continue;
|
|
266
|
+
const protocols = PROTOCOLS[name];
|
|
267
|
+
if (protocols !== void 0 && !safeUrl(value, protocols)) continue;
|
|
268
|
+
if (name === "srcset" && !value.split(",").every((candidate) => safeUrl(candidate.trim(), PROTOCOLS.src))) continue;
|
|
269
|
+
if (PREFIXED.has(name)) value = value.split(/\s+/).filter(Boolean).map((id) => PREFIX + id).join(" ");
|
|
270
|
+
(out ??= {})[property] = BOOLEAN.has(property) ? true : value;
|
|
271
|
+
}
|
|
272
|
+
if (tag === "a" && out?.target !== void 0) {
|
|
273
|
+
const rel = typeof out.rel === "string" ? out.rel : "";
|
|
274
|
+
if (!/(?:^|\s)noopener(?:\s|$)/i.test(rel)) out.rel = rel ? `${rel} noopener` : "noopener";
|
|
275
|
+
}
|
|
276
|
+
return out;
|
|
277
|
+
}
|
|
278
|
+
const isLetter = (code) => code >= 97 && code <= 122 || code >= 65 && code <= 90;
|
|
279
|
+
const isSpace = (code) => code === 32 || code === 10 || code === 9 || code === 13 || code === 12;
|
|
280
|
+
/**
|
|
281
|
+
* Reads a run of HTML into tags and text the way a browser does, in one pass with no
|
|
282
|
+
* backtracking: `<` and a letter start a tag, which runs to the next `>` outside a quoted value.
|
|
283
|
+
* A tag the text ends inside is left out, so a reply that is still arriving does not flash
|
|
284
|
+
* `<div align="cen` as text.
|
|
285
|
+
*/
|
|
286
|
+
function tokenize(html, handlers) {
|
|
287
|
+
const length = html.length;
|
|
288
|
+
let from = 0;
|
|
289
|
+
let at = 0;
|
|
290
|
+
const flush = (to) => {
|
|
291
|
+
if (to > from) handlers.text(decode(html.slice(from, to)));
|
|
292
|
+
};
|
|
293
|
+
while ((at = html.indexOf("<", at)) !== -1) {
|
|
294
|
+
const next = html.charCodeAt(at + 1);
|
|
295
|
+
if (next === 33 || next === 63) {
|
|
296
|
+
let end;
|
|
297
|
+
if (html.startsWith("<!--", at)) {
|
|
298
|
+
end = html.indexOf("-->", at + 4);
|
|
299
|
+
end = end === -1 ? length : end + 3;
|
|
300
|
+
} else {
|
|
301
|
+
end = html.indexOf(">", at);
|
|
302
|
+
end = end === -1 ? length : end + 1;
|
|
303
|
+
}
|
|
304
|
+
flush(at);
|
|
305
|
+
at = from = end;
|
|
306
|
+
continue;
|
|
307
|
+
}
|
|
308
|
+
const closing = next === 47;
|
|
309
|
+
const start = at + (closing ? 2 : 1);
|
|
310
|
+
if (!isLetter(html.charCodeAt(start))) {
|
|
311
|
+
at++;
|
|
312
|
+
continue;
|
|
313
|
+
}
|
|
314
|
+
let cursor = start;
|
|
315
|
+
while (cursor < length) {
|
|
316
|
+
const code = html.charCodeAt(cursor);
|
|
317
|
+
if (isSpace(code) || code === 47 || code === 62) break;
|
|
318
|
+
cursor++;
|
|
319
|
+
}
|
|
320
|
+
const tag = html.slice(start, cursor).toLowerCase();
|
|
321
|
+
const attributes = [];
|
|
322
|
+
let complete = false;
|
|
323
|
+
while (cursor < length) {
|
|
324
|
+
const code = html.charCodeAt(cursor);
|
|
325
|
+
if (code === 62) {
|
|
326
|
+
cursor++;
|
|
327
|
+
complete = true;
|
|
328
|
+
break;
|
|
329
|
+
}
|
|
330
|
+
if (isSpace(code) || code === 47) {
|
|
331
|
+
cursor++;
|
|
332
|
+
continue;
|
|
333
|
+
}
|
|
334
|
+
const nameStart = cursor;
|
|
335
|
+
cursor++;
|
|
336
|
+
while (cursor < length) {
|
|
337
|
+
const end = html.charCodeAt(cursor);
|
|
338
|
+
if (isSpace(end) || end === 47 || end === 62 || end === 61) break;
|
|
339
|
+
cursor++;
|
|
340
|
+
}
|
|
341
|
+
const name = html.slice(nameStart, cursor).toLowerCase();
|
|
342
|
+
let value = "";
|
|
343
|
+
let ahead = cursor;
|
|
344
|
+
while (ahead < length && isSpace(html.charCodeAt(ahead))) ahead++;
|
|
345
|
+
if (html.charCodeAt(ahead) === 61) {
|
|
346
|
+
ahead++;
|
|
347
|
+
while (ahead < length && isSpace(html.charCodeAt(ahead))) ahead++;
|
|
348
|
+
const quote = html.charCodeAt(ahead);
|
|
349
|
+
if (quote === 34 || quote === 39) {
|
|
350
|
+
const close = html.indexOf(quote === 34 ? "\"" : "'", ahead + 1);
|
|
351
|
+
if (close === -1) {
|
|
352
|
+
cursor = length;
|
|
353
|
+
break;
|
|
354
|
+
}
|
|
355
|
+
value = html.slice(ahead + 1, close);
|
|
356
|
+
cursor = close + 1;
|
|
357
|
+
} else {
|
|
358
|
+
const valueStart = ahead;
|
|
359
|
+
while (ahead < length && !isSpace(html.charCodeAt(ahead)) && html.charCodeAt(ahead) !== 62) ahead++;
|
|
360
|
+
value = html.slice(valueStart, ahead);
|
|
361
|
+
cursor = ahead;
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
attributes.push([name, decode(value)]);
|
|
365
|
+
}
|
|
366
|
+
flush(at);
|
|
367
|
+
at = from = cursor;
|
|
368
|
+
if (!complete) break;
|
|
369
|
+
if (closing) {
|
|
370
|
+
handlers.close(tag);
|
|
371
|
+
continue;
|
|
372
|
+
}
|
|
373
|
+
handlers.open(tag, attributes);
|
|
374
|
+
if (RAW_TEXT.has(tag)) {
|
|
375
|
+
const end = html.toLowerCase().indexOf(`</${tag}`, cursor);
|
|
376
|
+
const stop = end === -1 ? length : end;
|
|
377
|
+
if (stop > cursor) handlers.text(html.slice(cursor, stop));
|
|
378
|
+
at = from = stop;
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
flush(length);
|
|
382
|
+
}
|
|
383
|
+
const WHITESPACE = /^[ \t\n\r\f]*$/;
|
|
384
|
+
const TABLE_PARTS = /* @__PURE__ */ new Set([
|
|
385
|
+
"table",
|
|
386
|
+
"thead",
|
|
387
|
+
"tbody",
|
|
388
|
+
"tfoot",
|
|
389
|
+
"tr"
|
|
390
|
+
]);
|
|
391
|
+
/** The nodes whose children are blocks. */
|
|
392
|
+
const BLOCK_PARENTS = /* @__PURE__ */ new Set([
|
|
393
|
+
"blockquote",
|
|
394
|
+
"listItem",
|
|
395
|
+
"footnoteDefinition",
|
|
396
|
+
"root"
|
|
397
|
+
]);
|
|
398
|
+
/** Whether a table part may hold the node directly. */
|
|
399
|
+
const fits = (parent, node) => {
|
|
400
|
+
if (node.type !== "element") return false;
|
|
401
|
+
const tag = node.data?.hName;
|
|
402
|
+
if (parent === "tr") return tag === "td" || tag === "th";
|
|
403
|
+
if (parent === "table") return tag === "thead" || tag === "tbody" || tag === "tfoot";
|
|
404
|
+
return tag === "tr";
|
|
405
|
+
};
|
|
406
|
+
/**
|
|
407
|
+
* One run of sibling nodes with its raw HTML made into elements. A tag opened in one `html` node
|
|
408
|
+
* and closed in a later one takes the Markdown between them as its children, which is how
|
|
409
|
+
* `<details>` around paragraphs works; a tag left open is closed where its parent ends.
|
|
410
|
+
*/
|
|
411
|
+
function rebuild(nodes, outer, block, root = false) {
|
|
412
|
+
const out = [];
|
|
413
|
+
const stack = [];
|
|
414
|
+
/** Inside a `<script>` or `<style>`: nothing is kept until it closes. */
|
|
415
|
+
let dropping = null;
|
|
416
|
+
/** A `<pre>` was just opened: the line break right after its tag is not content. */
|
|
417
|
+
let fresh = false;
|
|
418
|
+
const top = () => stack[stack.length - 1]?.tag;
|
|
419
|
+
const add = (node) => {
|
|
420
|
+
fresh = false;
|
|
421
|
+
const depth = stack.length - 1;
|
|
422
|
+
const parent = stack[depth];
|
|
423
|
+
if (!parent) {
|
|
424
|
+
out.push(node);
|
|
425
|
+
return;
|
|
426
|
+
}
|
|
427
|
+
if (TABLE_PARTS.has(parent.tag) && !fits(parent.tag, node)) {
|
|
428
|
+
let table = depth;
|
|
429
|
+
while (table > 0 && stack[table].tag !== "table") table--;
|
|
430
|
+
const around = table > 0 ? stack[table - 1].node.children : out;
|
|
431
|
+
const before = around.lastIndexOf(stack[table].node);
|
|
432
|
+
if (before === -1) around.push(node);
|
|
433
|
+
else around.splice(before, 0, node);
|
|
434
|
+
return;
|
|
435
|
+
}
|
|
436
|
+
parent.node.children.push(node);
|
|
437
|
+
};
|
|
438
|
+
const inside = (tag) => outer.includes(tag) || stack.some((open) => open.tag === tag);
|
|
439
|
+
const handlers = {
|
|
440
|
+
text(value) {
|
|
441
|
+
if (dropping !== null) return;
|
|
442
|
+
if (fresh && value.charCodeAt(0) === 10) value = value.slice(1);
|
|
443
|
+
if (value === "") return;
|
|
444
|
+
const parent = top();
|
|
445
|
+
if (parent !== void 0 && TABLE_PARTS.has(parent) && WHITESPACE.test(value)) return;
|
|
446
|
+
add({
|
|
447
|
+
type: "text",
|
|
448
|
+
value
|
|
449
|
+
});
|
|
450
|
+
},
|
|
451
|
+
open(tag, attributes) {
|
|
452
|
+
if (dropping !== null) return;
|
|
453
|
+
if (DROPPED.has(tag)) {
|
|
454
|
+
dropping = tag;
|
|
455
|
+
return;
|
|
456
|
+
}
|
|
457
|
+
if (!TAGS.has(tag)) return;
|
|
458
|
+
if (NEEDS_TABLE.has(tag) && !inside("table")) return;
|
|
459
|
+
const rule = CLOSES[tag];
|
|
460
|
+
if (rule) for (let depth = stack.length - 1; depth >= 0; depth--) {
|
|
461
|
+
const open = stack[depth].tag;
|
|
462
|
+
if (rule.within.includes(open)) break;
|
|
463
|
+
if (rule.close.includes(open)) {
|
|
464
|
+
stack.length = depth;
|
|
465
|
+
break;
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
else if (BLOCK.has(tag) && top() === "p") stack.pop();
|
|
469
|
+
if (tag === "tr" && top() === "table") {
|
|
470
|
+
const body = {
|
|
471
|
+
type: "element",
|
|
472
|
+
data: { hName: "tbody" },
|
|
473
|
+
children: []
|
|
474
|
+
};
|
|
475
|
+
add(body);
|
|
476
|
+
stack.push({
|
|
477
|
+
tag: "tbody",
|
|
478
|
+
node: body
|
|
479
|
+
});
|
|
480
|
+
}
|
|
481
|
+
const made = properties(tag, attributes);
|
|
482
|
+
const node = {
|
|
483
|
+
type: "element",
|
|
484
|
+
data: made === void 0 ? { hName: tag } : {
|
|
485
|
+
hName: tag,
|
|
486
|
+
hProperties: made
|
|
487
|
+
},
|
|
488
|
+
children: []
|
|
489
|
+
};
|
|
490
|
+
add(node);
|
|
491
|
+
if (!VOID.has(tag)) stack.push({
|
|
492
|
+
tag,
|
|
493
|
+
node
|
|
494
|
+
});
|
|
495
|
+
fresh = tag === "pre";
|
|
496
|
+
},
|
|
497
|
+
close(tag) {
|
|
498
|
+
if (dropping !== null) {
|
|
499
|
+
if (dropping === tag) dropping = null;
|
|
500
|
+
return;
|
|
501
|
+
}
|
|
502
|
+
for (let depth = stack.length - 1; depth >= 0; depth--) if (stack[depth].tag === tag) {
|
|
503
|
+
stack.length = depth;
|
|
504
|
+
return;
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
};
|
|
508
|
+
for (let index = 0; index < nodes.length; index++) {
|
|
509
|
+
const node = nodes[index];
|
|
510
|
+
if (block && index > 0) {
|
|
511
|
+
if (stack.length > 0) handlers.text("\n");
|
|
512
|
+
else if (root) out.push({
|
|
513
|
+
type: "text",
|
|
514
|
+
value: "\n"
|
|
515
|
+
});
|
|
516
|
+
}
|
|
517
|
+
if (node.type === "html") {
|
|
518
|
+
tokenize(node.value ?? "", handlers);
|
|
519
|
+
continue;
|
|
520
|
+
}
|
|
521
|
+
if (dropping !== null) continue;
|
|
522
|
+
if (node.children !== void 0 && node.children.length > 0) {
|
|
523
|
+
const within = stack.length === 0 ? outer : [...outer, ...stack.map((open) => open.tag)];
|
|
524
|
+
node.children = rebuild(node.children, within, BLOCK_PARENTS.has(node.type));
|
|
525
|
+
if (node.type === "paragraph" && node.data?.hName === void 0 && node.children.some((child) => child.type === "element" && BLOCK.has(child.data?.hName))) node.data = {
|
|
526
|
+
...node.data,
|
|
527
|
+
hName: "div"
|
|
528
|
+
};
|
|
529
|
+
}
|
|
530
|
+
add(node);
|
|
531
|
+
}
|
|
532
|
+
return out;
|
|
533
|
+
}
|
|
534
|
+
addEntities(ENTITY_TABLE);
|
|
535
|
+
/** The raw HTML stage: what `<Markdown html>` takes. */
|
|
536
|
+
const html = {
|
|
537
|
+
name: "html",
|
|
538
|
+
transform(root) {
|
|
539
|
+
if (root.children !== void 0) root.children = rebuild(root.children, [], true, true);
|
|
540
|
+
}
|
|
541
|
+
};
|
|
542
|
+
export { html as default, html };
|
|
543
|
+
|
|
544
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../src/html/index.ts"],"sourcesContent":["/**\n * `@localix-ai/flowdown/html`: raw HTML in Markdown (`<details>`, `<kbd>`, a README's `<p align>`), made\n * into elements.\n *\n * Nothing in the text is ever handed to the browser as HTML. The tags are read here, and an\n * element is made only for a tag on the list below, with only the attributes on the list below,\n * so there is no second parser to disagree with and nothing to sanitize afterwards: `<script>`,\n * `style`, `onclick` and `javascript:` have no way through. The lists are GitHub's.\n *\n * `<Markdown>` fetches this entry by itself the first time a document holds HTML; pass it\n * directly where the HTML must be in the first render, as on a server:\n *\n * import { html } from '@localix-ai/flowdown/html'\n * <Markdown html={html}>{readme}</Markdown>\n */\nimport { addEntities, decodeEntity } from '../engine/entities'\nimport { ENTITY_TABLE } from '../engine/entity-table'\nimport type { Extension, Node } from '../engine/types'\n\n/** The elements raw HTML may make. Any other tag is dropped and what it holds is kept. */\nconst TAGS = new Set(\n (\n 'a b blockquote br code dd del details div dl dt em h1 h2 h3 h4 h5 h6 hr i img input ins kbd ' +\n 'li ol p picture pre q rp rt ruby s samp section source span strike strong sub summary sup ' +\n 'table tbody td tfoot th thead tr tt ul var aside figure figcaption footer answer-tabs answer-tab'\n ).split(' '),\n)\n\n/** Elements that never have children. */\nconst VOID = new Set(\n 'br hr img input source area base col embed link meta param track wbr'.split(' '),\n)\n\n/** Dropped with everything inside them. */\nconst DROPPED = new Set(['script', 'style'])\n\n/** Elements whose inside is text, not tags: the tag goes, the text stays. */\nconst RAW_TEXT = new Set('textarea title xmp iframe noembed noframes plaintext'.split(' '))\n\n/** Table parts mean nothing outside a table. */\nconst NEEDS_TABLE = new Set('tbody td tfoot th thead tr'.split(' '))\n\n/** Elements a paragraph cannot hold: a paragraph that gets one is drawn as a `<div>`. */\nconst BLOCK = new Set(\n 'blockquote details div dl h1 h2 h3 h4 h5 h6 hr ol p pre section table ul aside figure footer'.split(\n ' ',\n ),\n)\n\n/** Opening one of these ends the open element it cannot sit inside. */\nconst CLOSES: Record<string, { close: string[]; within: string[] }> = {\n li: { close: ['li'], within: ['ul', 'ol'] },\n dt: { close: ['dt', 'dd'], within: ['dl'] },\n dd: { close: ['dt', 'dd'], within: ['dl'] },\n tr: { close: ['tr'], within: ['table', 'thead', 'tbody', 'tfoot'] },\n td: { close: ['td', 'th'], within: ['tr'] },\n th: { close: ['td', 'th'], within: ['tr'] },\n thead: { close: ['thead', 'tbody', 'tfoot'], within: ['table'] },\n tbody: { close: ['thead', 'tbody', 'tfoot'], within: ['table'] },\n tfoot: { close: ['thead', 'tbody', 'tfoot'], within: ['table'] },\n}\n\n/** Attributes any element may carry, by the name HTML writes: the property React takes. */\nconst GLOBAL: Record<string, string> = {\n abbr: 'abbr',\n accept: 'accept',\n 'accept-charset': 'acceptCharset',\n accesskey: 'accessKey',\n action: 'action',\n align: 'align',\n alt: 'alt',\n axis: 'axis',\n border: 'border',\n cellpadding: 'cellPadding',\n cellspacing: 'cellSpacing',\n char: 'char',\n charoff: 'charoff',\n charset: 'charSet',\n checked: 'checked',\n clear: 'clear',\n colspan: 'colSpan',\n color: 'color',\n cols: 'cols',\n compact: 'compact',\n coords: 'coords',\n datetime: 'dateTime',\n dir: 'dir',\n enctype: 'encType',\n frame: 'frame',\n hspace: 'hspace',\n headers: 'headers',\n height: 'height',\n hreflang: 'hrefLang',\n for: 'htmlFor',\n id: 'id',\n ismap: 'ismap',\n itemprop: 'itemProp',\n label: 'label',\n lang: 'lang',\n maxlength: 'maxLength',\n media: 'media',\n method: 'method',\n multiple: 'multiple',\n name: 'name',\n nohref: 'nohref',\n noshade: 'noshade',\n nowrap: 'nowrap',\n open: 'open',\n prompt: 'prompt',\n readonly: 'readOnly',\n rev: 'rev',\n rowspan: 'rowSpan',\n rows: 'rows',\n rules: 'rules',\n scope: 'scope',\n selected: 'selected',\n shape: 'shape',\n size: 'size',\n span: 'span',\n start: 'start',\n summary: 'summary',\n tabindex: 'tabIndex',\n title: 'title',\n usemap: 'useMap',\n valign: 'valign',\n value: 'value',\n width: 'width',\n // The dialect's own data attributes: a form written as HTML draws like one written as Markdown.\n 'data-callout': 'data-callout',\n 'data-callout-title': 'data-callout-title',\n 'data-verdict': 'data-verdict',\n 'data-label': 'data-label',\n 'data-title': 'data-title',\n}\n\nconst ARIA = {\n 'aria-describedby': 'aria-describedby',\n 'aria-label': 'aria-label',\n 'aria-labelledby': 'aria-labelledby',\n}\n\n/** Attributes only some elements may carry. */\nconst OWN: Record<string, Record<string, string>> = {\n a: {\n ...ARIA,\n 'data-footnote-backref': 'data-footnote-backref',\n 'data-footnote-ref': 'data-footnote-ref',\n href: 'href',\n target: 'target',\n rel: 'rel',\n },\n blockquote: { cite: 'cite' },\n del: { cite: 'cite' },\n ins: { cite: 'cite' },\n q: { cite: 'cite' },\n div: { itemscope: 'itemScope', itemtype: 'itemType' },\n dl: ARIA,\n ol: ARIA,\n ul: ARIA,\n summary: ARIA,\n table: ARIA,\n img: { ...ARIA, longdesc: 'longdesc', src: 'src' },\n section: { 'data-footnotes': 'data-footnotes' },\n source: { srcset: 'srcSet', media: 'media', type: 'type' },\n}\n\n/** Attributes that are on or off, as React wants them: `true`, not the empty string. */\nconst BOOLEAN = new Set(['checked', 'multiple', 'open', 'readOnly', 'selected', 'itemScope'])\n\n/** The classes content may set: the dialect's own on any element, and these on theirs. */\nconst CLASSES: Record<string, RegExp> = {\n code: /^(?:language-.+|math-inline|math-display)$/,\n ol: /^contains-task-list$/,\n ul: /^contains-task-list$/,\n li: /^task-list-item$/,\n a: /^data-footnote-backref$/,\n h2: /^sr-only$/,\n section: /^footnotes$/,\n}\nconst DIALECT_CLASS = /^fd-/\n\n/** Where a URL may lead, by attribute. A URL with no scheme is the page's own and passes. */\nconst PROTOCOLS: Record<string, RegExp> = {\n href: /^(?:https?|ircs?|mailto|xmpp)$/i,\n src: /^https?$/i,\n cite: /^https?$/i,\n longdesc: /^https?$/i,\n}\n\n/** Ids and the attributes that point at them are prefixed, so content cannot stand in for one of the page's own. */\nconst PREFIXED = new Set(['id', 'name', 'aria-describedby', 'aria-labelledby'])\nconst PREFIX = 'user-content-'\n\nconst safeUrl = (url: string, allowed: RegExp): boolean => {\n const colon = url.indexOf(':')\n if (colon === -1) return true\n const slash = url.indexOf('/')\n const question = url.indexOf('?')\n const hash = url.indexOf('#')\n if (\n (slash !== -1 && colon > slash) ||\n (question !== -1 && colon > question) ||\n (hash !== -1 && colon > hash)\n )\n return true\n return allowed.test(url.slice(0, colon))\n}\n\nconst reEntity = /&(?:#x[a-f0-9]{1,6}|#[0-9]{1,7}|[a-z][a-z0-9]{1,31});/gi\nconst decode = (text: string): string =>\n text.includes('&') ? text.replace(reEntity, (entity) => decodeEntity(entity) ?? entity) : text\n\ntype Attributes = Array<[name: string, value: string]>\n\n/** The properties an element of raw HTML is drawn with: only what the lists allow. */\nfunction properties(tag: string, attributes: Attributes): Record<string, unknown> | undefined {\n // A box in content is a task's box: it shows a state and cannot be changed.\n if (tag === 'input') {\n const checked = attributes.some(([name]) => name === 'checked')\n return {\n type: 'checkbox',\n disabled: true,\n ...(checked ? { checked } : null),\n }\n }\n let out: Record<string, unknown> | undefined\n const own = OWN[tag]\n for (const [name, raw] of attributes) {\n let value = raw\n if (name === 'class') {\n const allowed = CLASSES[tag]\n const kept = value\n .split(/\\s+/)\n .filter((token) => token !== '' && (DIALECT_CLASS.test(token) || allowed?.test(token)))\n if (kept.length > 0) (out ??= {}).className = kept.join(' ')\n continue\n }\n const property = own?.[name] ?? GLOBAL[name]\n if (property === undefined || (out !== undefined && property in out)) continue\n const protocols = PROTOCOLS[name]\n if (protocols !== undefined && !safeUrl(value, protocols)) continue\n // A list of image candidates is held to an image's rule, each of them: one bad URL drops the list.\n if (\n name === 'srcset' &&\n !value.split(',').every((candidate) => safeUrl(candidate.trim(), PROTOCOLS.src as RegExp))\n )\n continue\n if (PREFIXED.has(name))\n value = value\n .split(/\\s+/)\n .filter(Boolean)\n .map((id) => PREFIX + id)\n .join(' ')\n ;(out ??= {})[property] = BOOLEAN.has(property) ? true : value\n }\n // A link that opens elsewhere never hands the page it opens a handle on this one.\n if (tag === 'a' && out?.target !== undefined) {\n const rel = typeof out.rel === 'string' ? out.rel : ''\n if (!/(?:^|\\s)noopener(?:\\s|$)/i.test(rel)) out.rel = rel ? `${rel} noopener` : 'noopener'\n }\n return out\n}\n\ninterface Handlers {\n text: (value: string) => void\n open: (tag: string, attributes: Attributes) => void\n close: (tag: string) => void\n}\n\nconst isLetter = (code: number): boolean =>\n (code >= 97 && code <= 122) || (code >= 65 && code <= 90)\nconst isSpace = (code: number): boolean =>\n code === 32 || code === 10 || code === 9 || code === 13 || code === 12\n\n/**\n * Reads a run of HTML into tags and text the way a browser does, in one pass with no\n * backtracking: `<` and a letter start a tag, which runs to the next `>` outside a quoted value.\n * A tag the text ends inside is left out, so a reply that is still arriving does not flash\n * `<div align=\"cen` as text.\n */\nfunction tokenize(html: string, handlers: Handlers): void {\n const length = html.length\n let from = 0\n let at = 0\n const flush = (to: number): void => {\n if (to > from) handlers.text(decode(html.slice(from, to)))\n }\n while ((at = html.indexOf('<', at)) !== -1) {\n const next = html.charCodeAt(at + 1)\n // Comments, doctypes and processing instructions draw nothing.\n if (next === 33 || next === 63) {\n let end: number\n if (html.startsWith('<!--', at)) {\n end = html.indexOf('-->', at + 4)\n end = end === -1 ? length : end + 3\n } else {\n end = html.indexOf('>', at)\n end = end === -1 ? length : end + 1\n }\n flush(at)\n at = from = end\n continue\n }\n const closing = next === 47\n const start = at + (closing ? 2 : 1)\n if (!isLetter(html.charCodeAt(start))) {\n at++\n continue\n }\n let cursor = start\n while (cursor < length) {\n const code = html.charCodeAt(cursor)\n if (isSpace(code) || code === 47 || code === 62) break\n cursor++\n }\n const tag = html.slice(start, cursor).toLowerCase()\n const attributes: Attributes = []\n let complete = false\n while (cursor < length) {\n const code = html.charCodeAt(cursor)\n if (code === 62) {\n cursor++\n complete = true\n break\n }\n if (isSpace(code) || code === 47) {\n cursor++\n continue\n }\n const nameStart = cursor\n // A name runs to white space, `/`, `>` or `=` (an `=` that opens it is part of it).\n cursor++\n while (cursor < length) {\n const end = html.charCodeAt(cursor)\n if (isSpace(end) || end === 47 || end === 62 || end === 61) break\n cursor++\n }\n const name = html.slice(nameStart, cursor).toLowerCase()\n let value = ''\n let ahead = cursor\n while (ahead < length && isSpace(html.charCodeAt(ahead))) ahead++\n if (html.charCodeAt(ahead) === 61) {\n ahead++\n while (ahead < length && isSpace(html.charCodeAt(ahead))) ahead++\n const quote = html.charCodeAt(ahead)\n if (quote === 34 || quote === 39) {\n const close = html.indexOf(quote === 34 ? '\"' : \"'\", ahead + 1)\n // A value the text ends inside: the tag is unfinished.\n if (close === -1) {\n cursor = length\n break\n }\n value = html.slice(ahead + 1, close)\n cursor = close + 1\n } else {\n const valueStart = ahead\n while (\n ahead < length &&\n !isSpace(html.charCodeAt(ahead)) &&\n html.charCodeAt(ahead) !== 62\n )\n ahead++\n value = html.slice(valueStart, ahead)\n cursor = ahead\n }\n }\n attributes.push([name, decode(value)])\n }\n flush(at)\n at = from = cursor\n // The text ended inside the tag: nothing of it is drawn.\n if (!complete) break\n if (closing) {\n handlers.close(tag)\n continue\n }\n handlers.open(tag, attributes)\n if (RAW_TEXT.has(tag)) {\n // What these hold is text, up to their own closing tag.\n const end = html.toLowerCase().indexOf(`</${tag}`, cursor)\n const stop = end === -1 ? length : end\n if (stop > cursor) handlers.text(html.slice(cursor, stop))\n at = from = stop\n }\n }\n flush(length)\n}\n\nconst WHITESPACE = /^[ \\t\\n\\r\\f]*$/\nconst TABLE_PARTS = new Set(['table', 'thead', 'tbody', 'tfoot', 'tr'])\n/** The nodes whose children are blocks. */\nconst BLOCK_PARENTS = new Set(['blockquote', 'listItem', 'footnoteDefinition', 'root'])\n\n/** Whether a table part may hold the node directly. */\nconst fits = (parent: string, node: Node): boolean => {\n if (node.type !== 'element') return false\n const tag = node.data?.hName\n if (parent === 'tr') return tag === 'td' || tag === 'th'\n if (parent === 'table') return tag === 'thead' || tag === 'tbody' || tag === 'tfoot'\n return tag === 'tr'\n}\n\n/**\n * One run of sibling nodes with its raw HTML made into elements. A tag opened in one `html` node\n * and closed in a later one takes the Markdown between them as its children, which is how\n * `<details>` around paragraphs works; a tag left open is closed where its parent ends.\n */\nfunction rebuild(nodes: Node[], outer: readonly string[], block: boolean, root = false): Node[] {\n const out: Node[] = []\n const stack: Array<{ tag: string; node: Node }> = []\n /** Inside a `<script>` or `<style>`: nothing is kept until it closes. */\n let dropping: string | null = null\n\n /** A `<pre>` was just opened: the line break right after its tag is not content. */\n let fresh = false\n\n const top = (): string | undefined => stack[stack.length - 1]?.tag\n const add = (node: Node): void => {\n fresh = false\n const depth = stack.length - 1\n const parent = stack[depth]\n if (!parent) {\n out.push(node)\n return\n }\n // What a table cannot hold directly is put before the table, as a browser puts it.\n if (TABLE_PARTS.has(parent.tag) && !fits(parent.tag, node)) {\n let table = depth\n while (table > 0 && (stack[table] as { tag: string }).tag !== 'table') table--\n const around =\n table > 0 ? ((stack[table - 1] as { node: Node }).node.children as Node[]) : out\n const before = around.lastIndexOf((stack[table] as { node: Node }).node)\n if (before === -1) around.push(node)\n else around.splice(before, 0, node)\n return\n }\n ;(parent.node.children as Node[]).push(node)\n }\n const inside = (tag: string): boolean =>\n outer.includes(tag) || stack.some((open) => open.tag === tag)\n\n const handlers: Handlers = {\n text(value) {\n if (dropping !== null) return\n if (fresh && value.charCodeAt(0) === 10) value = value.slice(1)\n if (value === '') return\n // White space between the parts of a table is not content.\n const parent = top()\n if (parent !== undefined && TABLE_PARTS.has(parent) && WHITESPACE.test(value)) return\n add({ type: 'text', value })\n },\n open(tag, attributes) {\n if (dropping !== null) return\n if (DROPPED.has(tag)) {\n dropping = tag\n return\n }\n if (!TAGS.has(tag)) return\n if (NEEDS_TABLE.has(tag) && !inside('table')) return\n // A tag that ends the open one it cannot sit inside: `<li>` after `<li>`.\n const rule = CLOSES[tag]\n if (rule) {\n for (let depth = stack.length - 1; depth >= 0; depth--) {\n const open = (stack[depth] as { tag: string }).tag\n if (rule.within.includes(open)) break\n if (rule.close.includes(open)) {\n stack.length = depth\n break\n }\n }\n } else if (BLOCK.has(tag) && top() === 'p') stack.pop()\n // A row straight inside a table sits in a body, as a browser would put it.\n if (tag === 'tr' && top() === 'table') {\n const body: Node = {\n type: 'element',\n data: { hName: 'tbody' },\n children: [],\n }\n add(body)\n stack.push({ tag: 'tbody', node: body })\n }\n const made = properties(tag, attributes)\n const node: Node = {\n type: 'element',\n data: made === undefined ? { hName: tag } : { hName: tag, hProperties: made },\n children: [],\n }\n add(node)\n if (!VOID.has(tag)) stack.push({ tag, node })\n fresh = tag === 'pre'\n },\n close(tag) {\n if (dropping !== null) {\n if (dropping === tag) dropping = null\n return\n }\n for (let depth = stack.length - 1; depth >= 0; depth--) {\n if ((stack[depth] as { tag: string }).tag === tag) {\n stack.length = depth\n return\n }\n }\n },\n }\n\n for (let index = 0; index < nodes.length; index++) {\n const node = nodes[index] as Node\n // Blocks are a line apart. Inside an element that is still open the line is its content\n // (it shows in a `<pre>`); between the blocks this run gives back, the renderer writes it.\n if (block && index > 0) {\n if (stack.length > 0) handlers.text('\\n')\n // At the top of a document the line is written here, one for every block of the source even\n // when a block draws nothing (a stray `</div>`): a document drawn block by block has one\n // between every two blocks too, so both ways give the same text.\n else if (root) out.push({ type: 'text', value: '\\n' })\n }\n if (node.type === 'html') {\n tokenize(node.value ?? '', handlers)\n continue\n }\n if (dropping !== null) continue\n if (node.children !== undefined && node.children.length > 0) {\n const within = stack.length === 0 ? outer : [...outer, ...stack.map((open) => open.tag)]\n node.children = rebuild(node.children, within, BLOCK_PARENTS.has(node.type))\n // A paragraph cannot hold a block: one that got a block is a `<div>`.\n if (\n node.type === 'paragraph' &&\n node.data?.hName === undefined &&\n node.children.some(\n (child) => child.type === 'element' && BLOCK.has(child.data?.hName as string),\n )\n )\n node.data = { ...node.data, hName: 'div' }\n }\n add(node)\n }\n return out\n}\n\n// HTML is where the rarer named references are written: with this stage every HTML5 name is decoded.\naddEntities(ENTITY_TABLE)\n\n/** The raw HTML stage: what `<Markdown html>` takes. */\nexport const html: Extension = {\n name: 'html',\n transform(root) {\n if (root.children !== undefined) root.children = rebuild(root.children, [], true, true)\n },\n}\n\nexport default html\n"],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@localix-ai/flowdown`: the React renderer.
|
|
3
|
+
*
|
|
4
|
+
* import { Markdown } from '@localix-ai/flowdown'
|
|
5
|
+
* import '@localix-ai/flowdown/styles.css'
|
|
6
|
+
*
|
|
7
|
+
* <Markdown streaming={!done}>{reply}</Markdown>
|
|
8
|
+
*/
|
|
9
|
+
import { Markdown, MarkdownProps } from "./react/markdown.js";
|
|
10
|
+
import { Components } from "./react/render.js";
|
|
11
|
+
import { useSmoothText } from "./react/use-smooth-text.js";
|
|
12
|
+
import { TreeRow } from "./core/file-tree-parse.js";
|
|
13
|
+
import { Highlighter, getLanguage } from "./render/views.js";
|
|
14
|
+
import { MarkdownComponents } from "./react/context.js";
|
|
15
|
+
import { StageLoader, StageOption } from "./react/use-stage.js";
|
|
16
|
+
import { UrlTransform, defaultUrlTransform } from "./engine/url.js";
|
|
17
|
+
import { Extension, FenceRule, InlineRule, InlineState, Node } from "./engine/types.js";
|
|
18
|
+
import { MermaidLoader } from "./mermaid/loader.js";
|
|
19
|
+
import { DEFAULT_LABELS, LabelOverrides, MarkdownLabels } from "./render/labels.js";
|
|
20
|
+
import { Callout } from "./react/callout.js";
|
|
21
|
+
import { Code, CodeBlock } from "./react/code-block.js";
|
|
22
|
+
import { Diagram } from "./mermaid/diagram.js";
|
|
23
|
+
import { FileTree, FileTreeProps, TreeIcon } from "./react/file-tree.js";
|
|
24
|
+
import { Tab, Tabs } from "./react/tabs.js";
|
|
25
|
+
import { childrenToText, isCodeBlock } from "./react/helpers.js";
|
|
26
|
+
import { MarkdownHeading, extractMarkdownHeadings } from "./core/headings.js";
|
|
27
|
+
export { Callout, Code, CodeBlock, type Components, DEFAULT_LABELS, Diagram, type Extension, type FenceRule, FileTree, type FileTreeProps, type Highlighter, type InlineRule, type InlineState, type LabelOverrides, Markdown, type MarkdownComponents, type MarkdownHeading, type MarkdownLabels, type MarkdownProps, type MermaidLoader, type Node, type StageLoader, type StageOption, Tab, Tabs, type TreeIcon, type TreeRow, type UrlTransform, childrenToText, defaultUrlTransform, extractMarkdownHeadings as extractHeadings, getLanguage, isCodeBlock, useSmoothText };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Markdown } from "./react/markdown.js";
|
|
2
|
+
import { useSmoothText } from "./react/use-smooth-text.js";
|
|
3
|
+
import { defaultUrlTransform } from "./engine/url.js";
|
|
4
|
+
import { DEFAULT_LABELS } from "./render/labels.js";
|
|
5
|
+
import { getLanguage } from "./render/views.js";
|
|
6
|
+
import { Callout } from "./react/callout.js";
|
|
7
|
+
import { Code, CodeBlock } from "./react/code-block.js";
|
|
8
|
+
import { Diagram } from "./mermaid/diagram.js";
|
|
9
|
+
import { FileTree } from "./react/file-tree.js";
|
|
10
|
+
import { Tab, Tabs } from "./react/tabs.js";
|
|
11
|
+
import { childrenToText, isCodeBlock } from "./react/helpers.js";
|
|
12
|
+
import { extractMarkdownHeadings } from "./core/headings.js";
|
|
13
|
+
export { Callout, Code, CodeBlock, DEFAULT_LABELS, Diagram, FileTree, Markdown, Tab, Tabs, childrenToText, defaultUrlTransform, extractMarkdownHeadings as extractHeadings, getLanguage, isCodeBlock, useSmoothText };
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@localix-ai/flowdown/math`: `$…$`, `$$…$$` and a ```` ```math ```` fence typeset with KaTeX.
|
|
3
|
+
*
|
|
4
|
+
* Its own entry so that only a page that imports it bundles KaTeX (a quarter of a megabyte). Pass
|
|
5
|
+
* it to `<Markdown math={…}>` directly, or as a loader so it is fetched the first time a document
|
|
6
|
+
* holds a formula:
|
|
7
|
+
*
|
|
8
|
+
* <Markdown math={() => import('@localix-ai/flowdown/math')}>
|
|
9
|
+
*
|
|
10
|
+
* KaTeX's stylesheet is the page's to add (`import 'katex/dist/katex.min.css'`): a bundler-only
|
|
11
|
+
* CSS import inside a library breaks the hosts that do not run one.
|
|
12
|
+
*/
|
|
13
|
+
import { Extension } from "../engine/types.js";
|
|
14
|
+
import { Typeset, typesetMath } from "./typeset.js";
|
|
15
|
+
/** The math stage: what `<Markdown math>` takes. */
|
|
16
|
+
export declare const math: Extension;
|
|
17
|
+
export { type Typeset, math as default, typesetMath };
|