@promptctl/rich-js 0.24.0 → 0.24.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/THIRD-PARTY-NOTICES +29 -1
- package/dist/core/animation.d.ts +78 -0
- package/dist/core/animation.js +107 -0
- package/dist/core/cell-widths.d.ts +9 -0
- package/dist/core/cell-widths.js +140 -0
- package/dist/core/cells.d.ts +17 -11
- package/dist/core/cells.js +100 -25
- package/dist/core/console.js +5 -5
- package/dist/core/easing.d.ts +110 -10
- package/dist/core/easing.js +269 -36
- package/dist/core/json.js +2 -1
- package/dist/core/measure.js +2 -2
- package/dist/core/place.js +2 -1
- package/dist/core/pretty.js +2 -2
- package/dist/core/protocol.d.ts +73 -19
- package/dist/core/protocol.js +56 -15
- package/dist/core/render.js +2 -1
- package/dist/core/segment.d.ts +18 -1
- package/dist/core/segment.js +45 -44
- package/dist/core/strip.d.ts +3 -2
- package/dist/core/strip.js +11 -7
- package/dist/core/style.js +17 -6
- package/dist/core/text.js +22 -14
- package/dist/core/timeline.d.ts +84 -0
- package/dist/core/timeline.js +144 -0
- package/dist/core/tween.d.ts +49 -0
- package/dist/core/tween.js +69 -0
- package/dist/host/app.js +2 -2
- package/dist/index.d.ts +10 -7
- package/dist/index.js +13 -4
- package/dist/renderables/align.d.ts +3 -2
- package/dist/renderables/align.js +3 -0
- package/dist/renderables/columns.d.ts +3 -2
- package/dist/renderables/columns.js +7 -2
- package/dist/renderables/constrain.d.ts +3 -2
- package/dist/renderables/constrain.js +5 -1
- package/dist/renderables/effect.d.ts +43 -58
- package/dist/renderables/effect.js +58 -64
- package/dist/renderables/effects.d.ts +16 -14
- package/dist/renderables/effects.js +64 -45
- package/dist/renderables/embed.js +3 -2
- package/dist/renderables/flexStrip.d.ts +3 -2
- package/dist/renderables/flexStrip.js +6 -1
- package/dist/renderables/frames.d.ts +40 -19
- package/dist/renderables/frames.js +48 -21
- package/dist/renderables/group.d.ts +3 -2
- package/dist/renderables/group.js +5 -2
- package/dist/renderables/html-entities.d.ts +8 -0
- package/dist/renderables/html-entities.js +386 -0
- package/dist/renderables/layout.d.ts +3 -2
- package/dist/renderables/layout.js +15 -2
- package/dist/renderables/list-view.d.ts +3 -2
- package/dist/renderables/list-view.js +12 -5
- package/dist/renderables/live.js +2 -2
- package/dist/renderables/markdown-parse.d.ts +109 -0
- package/dist/renderables/markdown-parse.js +1421 -0
- package/dist/renderables/markdown-url.d.ts +19 -0
- package/dist/renderables/markdown-url.js +314 -0
- package/dist/renderables/markdown.d.ts +5 -7
- package/dist/renderables/markdown.js +356 -683
- package/dist/renderables/padding.d.ts +8 -20
- package/dist/renderables/padding.js +32 -50
- package/dist/renderables/panel.d.ts +26 -26
- package/dist/renderables/panel.js +67 -103
- package/dist/renderables/progress.d.ts +4 -2
- package/dist/renderables/progress.js +11 -2
- package/dist/renderables/progressBar.js +2 -2
- package/dist/renderables/python-space.d.ts +13 -0
- package/dist/renderables/python-space.js +24 -0
- package/dist/renderables/spinner.d.ts +26 -14
- package/dist/renderables/spinner.js +49 -37
- package/dist/renderables/syntax.js +2 -1
- package/dist/renderables/table.d.ts +19 -11
- package/dist/renderables/table.js +140 -156
- package/dist/renderables/traceback.d.ts +1 -1
- package/dist/renderables/traceback.js +6 -5
- package/dist/renderables/tree.d.ts +6 -5
- package/dist/renderables/tree.js +11 -7
- package/dist/renderables/viewport.d.ts +13 -5
- package/dist/renderables/viewport.js +41 -20
- package/dist/template-bindings/animation-funcs.js +2 -2
- package/dist/template-bindings/index.js +2 -1
- package/dist/themes/ramp.d.ts +18 -3
- package/dist/themes/ramp.js +23 -2
- package/dist/widgets/focus-manager.d.ts +21 -14
- package/dist/widgets/focus-manager.js +28 -29
- package/dist/widgets/index.d.ts +1 -1
- package/dist/widgets/static-item.d.ts +4 -3
- package/dist/widgets/static-item.js +9 -7
- package/dist/widgets/text-input.d.ts +28 -18
- package/dist/widgets/text-input.js +144 -101
- package/dist/widgets/widget-app.d.ts +19 -3
- package/dist/widgets/widget-app.js +39 -21
- package/dist/widgets/widget-base.js +0 -3
- package/package.json +2 -5
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Markdown link destinations as markdown-it — Rich's parser — normalizes them:
|
|
3
|
+
* `normalizeLink` for the href, `normalizeLinkText` for an autolink's text,
|
|
4
|
+
* `validateLink` for which schemes may be a link at all. Each is a port of
|
|
5
|
+
* markdown-it-py's own, with the two pieces of it they stand on: mdurl's
|
|
6
|
+
* `parse`/`format`/`encode`/`decode`, and Python's punycode codec for a host.
|
|
7
|
+
* A port rather than the platform's `URL`, because that one is WHATWG's and
|
|
8
|
+
* markdown-it's is not: WHATWG folds `http://ÄB.com` to `xn--b-…` where
|
|
9
|
+
* markdown-it keeps the capital, and refuses outright what markdown-it reads.
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* The href markdown-it gives a link to `destination`: its host punycoded and
|
|
13
|
+
* the rest percent-encoded, as `normalizeLink` does — or undefined where
|
|
14
|
+
* `validateLink` refuses it, a script, a file, or data that is not an image.
|
|
15
|
+
* markdown-it then draws no link at all.
|
|
16
|
+
*/
|
|
17
|
+
export declare function linkHref(destination: string): string | undefined;
|
|
18
|
+
/** markdown-it's `normalizeLinkText`: an autolink's text, its host and its escapes decoded for reading. */
|
|
19
|
+
export declare function normalizeLinkText(destination: string): string;
|
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Markdown link destinations as markdown-it — Rich's parser — normalizes them:
|
|
3
|
+
* `normalizeLink` for the href, `normalizeLinkText` for an autolink's text,
|
|
4
|
+
* `validateLink` for which schemes may be a link at all. Each is a port of
|
|
5
|
+
* markdown-it-py's own, with the two pieces of it they stand on: mdurl's
|
|
6
|
+
* `parse`/`format`/`encode`/`decode`, and Python's punycode codec for a host.
|
|
7
|
+
* A port rather than the platform's `URL`, because that one is WHATWG's and
|
|
8
|
+
* markdown-it's is not: WHATWG folds `http://ÄB.com` to `xn--b-…` where
|
|
9
|
+
* markdown-it keeps the capital, and refuses outright what markdown-it reads.
|
|
10
|
+
*/
|
|
11
|
+
import { pyStrip } from "./python-space.js";
|
|
12
|
+
/** mdurl's protocol, `[a-z0-9.+-]+:` under Python's IGNORECASE, which also folds İ, ı, ſ and K (Kelvin) into `[a-z]`. */
|
|
13
|
+
const PROTOCOL = /^[a-z0-9.+\-\u0130\u0131\u017f\u212a]+:/i;
|
|
14
|
+
const PORT = /:[0-9]*$/;
|
|
15
|
+
const HOST_ENDING_CHARS = "/?#";
|
|
16
|
+
const NON_HOST_CHARS = "%/?;#'{}|\\^`<>\" \r\n\t";
|
|
17
|
+
const HOSTNAME_PART = /^[+a-z0-9A-Z_-]{0,63}$/;
|
|
18
|
+
const HOSTNAME_PART_START = /^([+a-z0-9A-Z_-]{0,63})(.*)$/s;
|
|
19
|
+
const HOSTNAME_MAX_LEN = 255;
|
|
20
|
+
/** Protocols whose hostname markdown-it recodes; anything else may not be a host at all, as `skype:name` is not. */
|
|
21
|
+
const RECODE_HOSTNAME_FOR = ["http:", "https:", "mailto:"];
|
|
22
|
+
const SLASHED_PROTOCOL = ["http:", "https:", "ftp:", "gopher:", "file:"];
|
|
23
|
+
/** The first index in `text` of any character of `chars`, or -1. */
|
|
24
|
+
function firstOf(text, chars) {
|
|
25
|
+
for (let i = 0; i < text.length; i++)
|
|
26
|
+
if (chars.includes(text[i]))
|
|
27
|
+
return i;
|
|
28
|
+
return -1;
|
|
29
|
+
}
|
|
30
|
+
/** mdurl's `parse(url, slashes_denote_host=True)`, the one way markdown-it calls it. */
|
|
31
|
+
function parse(url) {
|
|
32
|
+
const result = { protocol: "", slashes: false, auth: "", hostname: "", port: "", pathname: "", search: "", hash: "" };
|
|
33
|
+
let rest = pyStrip(url);
|
|
34
|
+
result.protocol = PROTOCOL.exec(rest)?.[0] ?? "";
|
|
35
|
+
rest = rest.slice(result.protocol.length);
|
|
36
|
+
const hostless = result.protocol === "javascript:";
|
|
37
|
+
const slashes = rest.startsWith("//");
|
|
38
|
+
if (slashes && !hostless) {
|
|
39
|
+
rest = rest.slice(2);
|
|
40
|
+
result.slashes = true;
|
|
41
|
+
}
|
|
42
|
+
if (!hostless && (slashes || (result.protocol !== "" && !SLASHED_PROTOCOL.includes(result.protocol)))) {
|
|
43
|
+
// The last `@` before the host ends is the end of the auth.
|
|
44
|
+
const hostEnding = firstOf(rest, HOST_ENDING_CHARS);
|
|
45
|
+
const at = hostEnding === -1 ? rest.lastIndexOf("@") : rest.lastIndexOf("@", hostEnding);
|
|
46
|
+
if (at !== -1) {
|
|
47
|
+
result.auth = rest.slice(0, at);
|
|
48
|
+
rest = rest.slice(at + 1);
|
|
49
|
+
}
|
|
50
|
+
let hostEnd = firstOf(rest, NON_HOST_CHARS);
|
|
51
|
+
if (hostEnd === -1)
|
|
52
|
+
hostEnd = rest.length;
|
|
53
|
+
if (hostEnd > 0 && rest[hostEnd - 1] === ":")
|
|
54
|
+
hostEnd--;
|
|
55
|
+
let host = rest.slice(0, hostEnd);
|
|
56
|
+
rest = rest.slice(hostEnd);
|
|
57
|
+
const port = PORT.exec(host)?.[0];
|
|
58
|
+
if (port !== undefined) {
|
|
59
|
+
if (port !== ":")
|
|
60
|
+
result.port = port.slice(1);
|
|
61
|
+
host = host.slice(0, -port.length);
|
|
62
|
+
}
|
|
63
|
+
result.hostname = host;
|
|
64
|
+
const ipv6 = host.startsWith("[") && host.endsWith("]");
|
|
65
|
+
if (!ipv6) {
|
|
66
|
+
// A part that is no host label, even with each non-ASCII character
|
|
67
|
+
// standing in for a letter, ends the host there; the rest is path.
|
|
68
|
+
const parts = host.split(".");
|
|
69
|
+
for (const [i, part] of parts.entries()) {
|
|
70
|
+
if (HOSTNAME_PART.test(part) || HOSTNAME_PART.test(part.replace(/[^\0-\x7f]/gu, "x")))
|
|
71
|
+
continue;
|
|
72
|
+
const [, valid, notHost] = HOSTNAME_PART_START.exec(part);
|
|
73
|
+
rest = [notHost, ...parts.slice(i + 1)].join(".") + rest;
|
|
74
|
+
result.hostname = [...parts.slice(0, i), valid].join(".");
|
|
75
|
+
break;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
if ([...result.hostname].length > HOSTNAME_MAX_LEN)
|
|
79
|
+
result.hostname = "";
|
|
80
|
+
if (ipv6)
|
|
81
|
+
result.hostname = result.hostname.slice(1, -1);
|
|
82
|
+
}
|
|
83
|
+
const hash = rest.indexOf("#");
|
|
84
|
+
if (hash !== -1) {
|
|
85
|
+
result.hash = rest.slice(hash);
|
|
86
|
+
rest = rest.slice(0, hash);
|
|
87
|
+
}
|
|
88
|
+
const search = rest.indexOf("?");
|
|
89
|
+
if (search !== -1) {
|
|
90
|
+
result.search = rest.slice(search);
|
|
91
|
+
rest = rest.slice(0, search);
|
|
92
|
+
}
|
|
93
|
+
result.pathname = rest;
|
|
94
|
+
return result;
|
|
95
|
+
}
|
|
96
|
+
/** mdurl's `format`: the parts of `url` written back out. */
|
|
97
|
+
function format(url) {
|
|
98
|
+
return (url.protocol +
|
|
99
|
+
(url.slashes ? "//" : "") +
|
|
100
|
+
(url.auth ? `${url.auth}@` : "") +
|
|
101
|
+
(url.hostname.includes(":") ? `[${url.hostname}]` : url.hostname) +
|
|
102
|
+
(url.port ? `:${url.port}` : "") +
|
|
103
|
+
url.pathname +
|
|
104
|
+
url.search +
|
|
105
|
+
url.hash);
|
|
106
|
+
}
|
|
107
|
+
/** What mdurl's `encode` leaves as it is, besides ASCII letters and digits. */
|
|
108
|
+
const ENCODE_KEEP = ";/?:@&=+$,-_.!~*'()#";
|
|
109
|
+
const ALPHANUMERIC = /[A-Za-z0-9]/;
|
|
110
|
+
const HEX_PAIR = /^[0-9a-fA-F]{2}$/;
|
|
111
|
+
/** mdurl's `encode`: every character it does not keep percent-encoded as UTF-8, a `%` already starting an escape kept. */
|
|
112
|
+
function encode(text) {
|
|
113
|
+
let result = "";
|
|
114
|
+
for (let i = 0; i < text.length; i++) {
|
|
115
|
+
const ch = text[i];
|
|
116
|
+
const code = ch.charCodeAt(0);
|
|
117
|
+
if (ch === "%" && i + 2 < text.length && HEX_PAIR.test(text.slice(i + 1, i + 3))) {
|
|
118
|
+
result += text.slice(i, i + 3);
|
|
119
|
+
i += 2;
|
|
120
|
+
}
|
|
121
|
+
else if (code < 0x80) {
|
|
122
|
+
result += ALPHANUMERIC.test(ch) || ENCODE_KEEP.includes(ch) ? ch : `%${code.toString(16).toUpperCase().padStart(2, "0")}`;
|
|
123
|
+
}
|
|
124
|
+
else if (code >= 0xd800 && code <= 0xdfff) {
|
|
125
|
+
const low = text.charCodeAt(i + 1);
|
|
126
|
+
if (code <= 0xdbff && low >= 0xdc00 && low <= 0xdfff) {
|
|
127
|
+
result += encodeURIComponent(text.slice(i, i + 2));
|
|
128
|
+
i++;
|
|
129
|
+
}
|
|
130
|
+
else {
|
|
131
|
+
// A lone surrogate, as the replacement character.
|
|
132
|
+
result += "%EF%BF%BD";
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
else {
|
|
136
|
+
result += encodeURIComponent(ch);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return result;
|
|
140
|
+
}
|
|
141
|
+
/** What `normalizeLinkText` leaves escaped: mdurl's default reserved set, and `%` (markdown-it issue 720). */
|
|
142
|
+
const DECODE_KEEP = ";/?:@&=+$,#%";
|
|
143
|
+
const ESCAPE_RUN = /(?:%[a-f0-9]{2})+/gi;
|
|
144
|
+
/**
|
|
145
|
+
* The code point of the UTF-8 sequence `bytes`, or undefined where Python's
|
|
146
|
+
* strict decoder refuses it: an overlong form, a surrogate, past U+10FFFF.
|
|
147
|
+
*/
|
|
148
|
+
function utf8(bytes) {
|
|
149
|
+
const [lead, ...rest] = bytes;
|
|
150
|
+
const point = rest.reduce((cp, b) => (cp << 6) | (b & 0x3f), lead & (0xff >> (bytes.length + 1)));
|
|
151
|
+
const least = [0, 0, 0x80, 0x800, 0x10000][bytes.length];
|
|
152
|
+
return point < least || point > 0x10ffff || (point >= 0xd800 && point <= 0xdfff) ? undefined : String.fromCodePoint(point);
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* mdurl's `decode` with `DECODE_KEEP` excluded: each run of percent escapes
|
|
156
|
+
* read as UTF-8, a byte that is no part of a well-formed sequence as U+FFFD,
|
|
157
|
+
* and an escape of a kept character left as an escape, its hex uppercased.
|
|
158
|
+
*/
|
|
159
|
+
function decode(text) {
|
|
160
|
+
return text.replace(ESCAPE_RUN, (run) => {
|
|
161
|
+
const bytes = run.split("%").slice(1).map((hex) => parseInt(hex, 16));
|
|
162
|
+
let result = "";
|
|
163
|
+
for (let i = 0; i < bytes.length;) {
|
|
164
|
+
const b1 = bytes[i];
|
|
165
|
+
if (b1 < 0x80) {
|
|
166
|
+
const ch = String.fromCharCode(b1);
|
|
167
|
+
result += DECODE_KEEP.includes(ch) ? `%${b1.toString(16).toUpperCase().padStart(2, "0")}` : ch;
|
|
168
|
+
i++;
|
|
169
|
+
continue;
|
|
170
|
+
}
|
|
171
|
+
// A lead byte and the continuation bytes it calls for, as many as there are.
|
|
172
|
+
const length = (b1 & 0xe0) === 0xc0 ? 2 : (b1 & 0xf0) === 0xe0 ? 3 : (b1 & 0xf8) === 0xf0 ? 4 : 1;
|
|
173
|
+
const sequence = bytes.slice(i, i + length);
|
|
174
|
+
if (length > 1 && sequence.length === length && sequence.slice(1).every((b) => (b & 0xc0) === 0x80)) {
|
|
175
|
+
result += utf8(sequence) ?? "�".repeat(length);
|
|
176
|
+
i += length;
|
|
177
|
+
}
|
|
178
|
+
else {
|
|
179
|
+
result += "�";
|
|
180
|
+
i++;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
return result;
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
// --- Punycode, as Python's codec writes it (RFC 3492) ---
|
|
187
|
+
const BASE = 36;
|
|
188
|
+
const DIGITS = "abcdefghijklmnopqrstuvwxyz0123456789";
|
|
189
|
+
/** RFC 3492's threshold for digit `j` of a generalized integer. */
|
|
190
|
+
function threshold(j, bias) {
|
|
191
|
+
return Math.min(26, Math.max(1, BASE * (j + 1) - bias));
|
|
192
|
+
}
|
|
193
|
+
/** RFC 3492's bias adaptation. */
|
|
194
|
+
function adapt(delta, first, points) {
|
|
195
|
+
delta = Math.floor(delta / (first ? 700 : 2));
|
|
196
|
+
delta += Math.floor(delta / points);
|
|
197
|
+
let divisions = 0;
|
|
198
|
+
for (; delta > 455; divisions += BASE)
|
|
199
|
+
delta = Math.floor(delta / 35);
|
|
200
|
+
return divisions + Math.floor((BASE * delta) / (delta + 38));
|
|
201
|
+
}
|
|
202
|
+
/** Python's `codecs.encode(label, "punycode")`. */
|
|
203
|
+
function punycodeEncode(label) {
|
|
204
|
+
const points = Array.from(label, (ch) => ch.codePointAt(0));
|
|
205
|
+
const base = points.filter((p) => p < 0x80);
|
|
206
|
+
const deltas = [];
|
|
207
|
+
let oldChar = 0x80;
|
|
208
|
+
let oldIndex = -1;
|
|
209
|
+
for (const char of [...new Set(points.filter((p) => p >= 0x80))].sort((a, b) => a - b)) {
|
|
210
|
+
let delta = (points.filter((p) => p < char).length + 1) * (char - oldChar);
|
|
211
|
+
let index = -1;
|
|
212
|
+
for (const p of points) {
|
|
213
|
+
if (p < char)
|
|
214
|
+
index++;
|
|
215
|
+
else if (p === char) {
|
|
216
|
+
index++;
|
|
217
|
+
deltas.push(delta + index - oldIndex - 1);
|
|
218
|
+
oldIndex = index;
|
|
219
|
+
delta = 0;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
oldChar = char;
|
|
223
|
+
}
|
|
224
|
+
let extended = "";
|
|
225
|
+
let bias = 72;
|
|
226
|
+
for (const [point, delta] of deltas.entries()) {
|
|
227
|
+
for (let n = delta, j = 0;; j++) {
|
|
228
|
+
const t = threshold(j, bias);
|
|
229
|
+
if (n < t) {
|
|
230
|
+
extended += DIGITS[n];
|
|
231
|
+
break;
|
|
232
|
+
}
|
|
233
|
+
extended += DIGITS[t + ((n - t) % (BASE - t))];
|
|
234
|
+
n = Math.floor((n - t) / (BASE - t));
|
|
235
|
+
}
|
|
236
|
+
bias = adapt(delta, point === 0, base.length + point + 1);
|
|
237
|
+
}
|
|
238
|
+
return base.length > 0 ? `${String.fromCodePoint(...base)}-${extended}` : extended;
|
|
239
|
+
}
|
|
240
|
+
/** Python's `codecs.decode(label, "punycode")`, or undefined where it raises. */
|
|
241
|
+
function punycodeDecode(label) {
|
|
242
|
+
if (/[^\0-\x7f]/.test(label))
|
|
243
|
+
return undefined;
|
|
244
|
+
const dash = label.lastIndexOf("-");
|
|
245
|
+
const output = Array.from(dash === -1 ? "" : label.slice(0, dash));
|
|
246
|
+
const extended = label.slice(dash + 1).toUpperCase();
|
|
247
|
+
let char = 0x80;
|
|
248
|
+
let pos = -1;
|
|
249
|
+
let bias = 72;
|
|
250
|
+
for (let at = 0; at < extended.length;) {
|
|
251
|
+
const first = at === 0;
|
|
252
|
+
let delta = 0;
|
|
253
|
+
for (let w = 1, j = 0;; j++) {
|
|
254
|
+
if (at >= extended.length)
|
|
255
|
+
return undefined;
|
|
256
|
+
const code = extended.charCodeAt(at++);
|
|
257
|
+
const digit = code >= 0x41 && code <= 0x5a ? code - 0x41 : code >= 0x30 && code <= 0x39 ? code - 22 : undefined;
|
|
258
|
+
if (digit === undefined)
|
|
259
|
+
return undefined;
|
|
260
|
+
const t = threshold(j, bias);
|
|
261
|
+
delta += digit * w;
|
|
262
|
+
if (digit < t)
|
|
263
|
+
break;
|
|
264
|
+
w *= BASE - t;
|
|
265
|
+
}
|
|
266
|
+
pos += delta + 1;
|
|
267
|
+
char += Math.floor(pos / (output.length + 1));
|
|
268
|
+
if (char > 0x10ffff)
|
|
269
|
+
return undefined;
|
|
270
|
+
pos %= output.length + 1;
|
|
271
|
+
output.splice(pos, 0, String.fromCodePoint(char));
|
|
272
|
+
bias = adapt(delta, first, output.length);
|
|
273
|
+
}
|
|
274
|
+
return output.join("");
|
|
275
|
+
}
|
|
276
|
+
/** A host's labels, split at any of the four full stops IDNA reads as one, and joined back with the ASCII one. */
|
|
277
|
+
const LABEL_SEPARATOR = /[.。.。]/;
|
|
278
|
+
/** markdown-it's `to_ascii`: each label carrying a non-ASCII character punycoded. A hostname carries no `@`, so the email half of its `map_domain` never applies. */
|
|
279
|
+
function toAscii(hostname) {
|
|
280
|
+
return hostname
|
|
281
|
+
.split(LABEL_SEPARATOR)
|
|
282
|
+
.map((label) => (/[^\0-\x7e]/.test(label) ? `xn--${punycodeEncode(label)}` : label))
|
|
283
|
+
.join(".");
|
|
284
|
+
}
|
|
285
|
+
/** markdown-it's `to_unicode`: each `xn--` label decoded, or undefined where one does not decode. */
|
|
286
|
+
function toUnicode(hostname) {
|
|
287
|
+
const labels = hostname.split(LABEL_SEPARATOR).map((label) => (label.startsWith("xn--") ? punycodeDecode(label.slice(4).toLowerCase()) : label));
|
|
288
|
+
return labels.every((label) => label !== undefined) ? labels.join(".") : undefined;
|
|
289
|
+
}
|
|
290
|
+
/** `url` parsed, its hostname recoded by `recode` where markdown-it recodes one, and written back out. */
|
|
291
|
+
function recodeHostname(url, recode) {
|
|
292
|
+
const parsed = parse(url);
|
|
293
|
+
if (parsed.hostname && (!parsed.protocol || RECODE_HOSTNAME_FOR.includes(parsed.protocol))) {
|
|
294
|
+
parsed.hostname = recode(parsed.hostname) ?? parsed.hostname;
|
|
295
|
+
}
|
|
296
|
+
return format(parsed);
|
|
297
|
+
}
|
|
298
|
+
const BAD_PROTOCOL = /^(?:vbscript|javascript|file|data):/;
|
|
299
|
+
const GOOD_DATA = /^data:image\/(?:gif|png|jpeg|webp);/;
|
|
300
|
+
/**
|
|
301
|
+
* The href markdown-it gives a link to `destination`: its host punycoded and
|
|
302
|
+
* the rest percent-encoded, as `normalizeLink` does — or undefined where
|
|
303
|
+
* `validateLink` refuses it, a script, a file, or data that is not an image.
|
|
304
|
+
* markdown-it then draws no link at all.
|
|
305
|
+
*/
|
|
306
|
+
export function linkHref(destination) {
|
|
307
|
+
const href = encode(recodeHostname(destination, toAscii));
|
|
308
|
+
const scheme = pyStrip(href).toLowerCase();
|
|
309
|
+
return BAD_PROTOCOL.test(scheme) && !GOOD_DATA.test(scheme) ? undefined : href;
|
|
310
|
+
}
|
|
311
|
+
/** markdown-it's `normalizeLinkText`: an autolink's text, its host and its escapes decoded for reading. */
|
|
312
|
+
export function normalizeLinkText(destination) {
|
|
313
|
+
return decode(recodeHostname(destination, toUnicode));
|
|
314
|
+
}
|
|
@@ -1,13 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Markdown — renders Markdown content to the terminal.
|
|
3
|
-
* Uses built-in parsing (no external dependency).
|
|
4
3
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* CommonMark has none of, and an HTML block are read as markdown-it — Rich's
|
|
10
|
-
* parser — reads them. Where CommonMark leaves the drawing open, Rich's is
|
|
4
|
+
* `markdown-parse.ts` reads the source, once, when a `Markdown` is made; this
|
|
5
|
+
* module draws what it read. A blockquote and a list item are drawn beside a
|
|
6
|
+
* gutter — the quote's bar, the item's marker then its hang — and a table as
|
|
7
|
+
* Rich draws one. Where CommonMark leaves the drawing open, Rich's is
|
|
11
8
|
* followed: HTML, a block of it or a tag inline, is drawn as Rich draws it,
|
|
12
9
|
* not at all.
|
|
13
10
|
*/
|
|
@@ -31,6 +28,7 @@ export declare class Markdown implements Renderable, Measurable {
|
|
|
31
28
|
readonly inlineCodeStyle: string | Style;
|
|
32
29
|
readonly hyperlinks: boolean;
|
|
33
30
|
readonly justify: Justify;
|
|
31
|
+
private readonly parsed;
|
|
34
32
|
constructor(markdown: string, options?: MarkdownOptions);
|
|
35
33
|
render(rawOptions: RenderOptions): Iterable<Segment>;
|
|
36
34
|
measure(options: RenderOptions): {
|