postext 1.22.1 → 1.22.3

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.
Files changed (65) hide show
  1. package/dist/__tests__/exports.test.js +4 -0
  2. package/dist/__tests__/exports.test.js.map +1 -1
  3. package/dist/comics/lettering/__tests__/panelCache.test.d.ts +2 -0
  4. package/dist/comics/lettering/__tests__/panelCache.test.d.ts.map +1 -0
  5. package/dist/comics/lettering/__tests__/panelCache.test.js +43 -0
  6. package/dist/comics/lettering/__tests__/panelCache.test.js.map +1 -0
  7. package/dist/comics/lettering/letter.d.ts.map +1 -1
  8. package/dist/comics/lettering/letter.js +73 -0
  9. package/dist/comics/lettering/letter.js.map +1 -1
  10. package/dist/comics/lettering/shape-text.d.ts.map +1 -1
  11. package/dist/comics/lettering/shape-text.js +64 -16
  12. package/dist/comics/lettering/shape-text.js.map +1 -1
  13. package/dist/fonts/usage.d.ts +46 -0
  14. package/dist/fonts/usage.d.ts.map +1 -0
  15. package/dist/fonts/usage.js +205 -0
  16. package/dist/fonts/usage.js.map +1 -0
  17. package/dist/index.d.ts +2 -0
  18. package/dist/index.d.ts.map +1 -1
  19. package/dist/index.js +1 -0
  20. package/dist/index.js.map +1 -1
  21. package/dist/word/analyze.d.ts +33 -0
  22. package/dist/word/analyze.d.ts.map +1 -0
  23. package/dist/word/analyze.js +167 -0
  24. package/dist/word/analyze.js.map +1 -0
  25. package/dist/word/docxRead.d.ts +10 -0
  26. package/dist/word/docxRead.d.ts.map +1 -0
  27. package/dist/word/docxRead.js +584 -0
  28. package/dist/word/docxRead.js.map +1 -0
  29. package/dist/word/index.d.ts +14 -0
  30. package/dist/word/index.d.ts.map +1 -0
  31. package/dist/word/index.js +12 -0
  32. package/dist/word/index.js.map +1 -0
  33. package/dist/word/inline.d.ts +48 -0
  34. package/dist/word/inline.d.ts.map +1 -0
  35. package/dist/word/inline.js +606 -0
  36. package/dist/word/inline.js.map +1 -0
  37. package/dist/word/model.d.ts +141 -0
  38. package/dist/word/model.d.ts.map +1 -0
  39. package/dist/word/model.js +23 -0
  40. package/dist/word/model.js.map +1 -0
  41. package/dist/word/omml.d.ts +4 -0
  42. package/dist/word/omml.d.ts.map +1 -0
  43. package/dist/word/omml.js +139 -0
  44. package/dist/word/omml.js.map +1 -0
  45. package/dist/word/template.d.ts +111 -0
  46. package/dist/word/template.d.ts.map +1 -0
  47. package/dist/word/template.js +255 -0
  48. package/dist/word/template.js.map +1 -0
  49. package/dist/word/toDocx.d.ts +28 -0
  50. package/dist/word/toDocx.d.ts.map +1 -0
  51. package/dist/word/toDocx.js +684 -0
  52. package/dist/word/toDocx.js.map +1 -0
  53. package/dist/word/toMarkdown.d.ts +53 -0
  54. package/dist/word/toMarkdown.d.ts.map +1 -0
  55. package/dist/word/toMarkdown.js +632 -0
  56. package/dist/word/toMarkdown.js.map +1 -0
  57. package/dist/word/word.test.d.ts +2 -0
  58. package/dist/word/word.test.d.ts.map +1 -0
  59. package/dist/word/word.test.js +302 -0
  60. package/dist/word/word.test.js.map +1 -0
  61. package/dist/word/xml.d.ts +30 -0
  62. package/dist/word/xml.d.ts.map +1 -0
  63. package/dist/word/xml.js +127 -0
  64. package/dist/word/xml.js.map +1 -0
  65. package/package.json +5 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/word/index.ts"],"names":[],"mappings":"AAAA,yEAAyE;AACzE,2EAA2E;AAC3E,0EAA0E;AAC1E,mEAAmE;AAEnE,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AACzF,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAElH,OAAO,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC;AAE9D,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAExC,OAAO,EACL,sBAAsB,EACtB,YAAY,EACZ,iBAAiB,EACjB,aAAa,EACb,aAAa,EACb,eAAe,EACf,YAAY,EACZ,gBAAgB,EAChB,MAAM,EACN,oBAAoB,EACpB,oBAAoB,EACpB,aAAa,EACb,YAAY,EACZ,aAAa,GACd,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAErD,OAAO,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC"}
@@ -0,0 +1,48 @@
1
+ export interface InlineRun {
2
+ text: string;
3
+ bold?: boolean;
4
+ italic?: boolean;
5
+ smallCaps?: boolean;
6
+ script?: 'sup' | 'sub';
7
+ /** Postext Markdown written as is. */
8
+ raw?: boolean;
9
+ /** Link destination. */
10
+ href?: string;
11
+ /** Chip style id. */
12
+ chip?: string;
13
+ /** Footnote marker (`[^id]`); `text` is empty. */
14
+ note?: string;
15
+ }
16
+ export declare const WORD_JOINER = "\u2060";
17
+ /** Escape the characters with inline meaning so they print literally. An
18
+ * `_` between two letters is text already and stays bare. */
19
+ export declare function escapeText(text: string, before?: string, after?: string): string;
20
+ /** A word joiner in front of a paragraph that would otherwise open a list,
21
+ * heading, quote, fence or note definition. */
22
+ export declare function guardLineStart(line: string): string;
23
+ /** A link destination Postext reads back whole: spaces, `$`, backslashes,
24
+ * angle brackets and unbalanced parentheses percent-encoded. */
25
+ export declare function linkDestination(url: string): string;
26
+ /** Style boundaries on word boundaries: spaces and punctuation between two
27
+ * runs of one style take that style; a style change inside a word moves to
28
+ * the word's edge (the longer side wins). */
29
+ export declare function normaliseRuns(input: InlineRun[], tidy?: boolean): InlineRun[];
30
+ /** Postext inline Markdown for runs. Links and chips wrap their own runs. */
31
+ export declare function renderInline(input: InlineRun[], opts?: {
32
+ collapseSpaces?: boolean;
33
+ tidy?: boolean;
34
+ }): string;
35
+ export interface ParseInlineOptions {
36
+ /** Footnote ids to turn into Word notes (the rest stay verbatim). */
37
+ notes?: ReadonlySet<string>;
38
+ /** Called for each note marker taken; return false to keep it verbatim
39
+ * (a note cited twice is one Word note). */
40
+ takeNote?: (id: string) => boolean;
41
+ /** Heading text: `\\` (a title break) stays verbatim. */
42
+ heading?: boolean;
43
+ }
44
+ /** Postext inline Markdown → runs. Every character with inline meaning the
45
+ * runs cannot say (an unpaired `*`, a `$` that opens nothing) is kept as a
46
+ * verbatim run, so `renderInline(parseInline(x))` reads as `x`. */
47
+ export declare function parseInline(text: string, opts?: ParseInlineOptions): InlineRun[];
48
+ //# sourceMappingURL=inline.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inline.d.ts","sourceRoot":"","sources":["../../src/word/inline.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,MAAM,CAAC,EAAE,KAAK,GAAG,KAAK,CAAC;IACvB,sCAAsC;IACtC,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,wBAAwB;IACxB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,qBAAqB;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,kDAAkD;IAClD,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,eAAO,MAAM,WAAW,WAAM,CAAC;AAS/B;8DAC8D;AAC9D,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,SAAK,EAAE,KAAK,SAAK,GAAG,MAAM,CAkBxE;AAID;gDACgD;AAChD,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEnD;AAED;iEACiE;AACjE,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAgBnD;AAwBD;;8CAE8C;AAC9C,wBAAgB,aAAa,CAAC,KAAK,EAAE,SAAS,EAAE,EAAE,IAAI,UAAO,GAAG,SAAS,EAAE,CAwC1E;AA2GD,6EAA6E;AAC7E,wBAAgB,YAAY,CAAC,KAAK,EAAE,SAAS,EAAE,EAAE,IAAI,GAAE;IAAE,cAAc,CAAC,EAAE,OAAO,CAAC;IAAC,IAAI,CAAC,EAAE,OAAO,CAAA;CAAO,GAAG,MAAM,CA4BhH;AAiBD,MAAM,WAAW,kBAAkB;IACjC,qEAAqE;IACrE,KAAK,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAC5B;iDAC6C;IAC7C,QAAQ,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC;IACnC,yDAAyD;IACzD,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AA8OD;;oEAEoE;AACpE,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,GAAE,kBAAuB,GAAG,SAAS,EAAE,CAiEpF"}
@@ -0,0 +1,606 @@
1
+ // Inline text both ways: Word runs → Postext inline marks (import) and
2
+ // Postext inline marks → runs Word can carry (export). Whatever has no Word
3
+ // equivalent (`:ref{…}`, maths, `:index`, annotations, a stray `*`) travels
4
+ // as a verbatim run, so a manuscript that goes to Word and back keeps it.
5
+ export const WORD_JOINER = '⁠';
6
+ // ---------------------------------------------------------------------------
7
+ // Runs → Postext Markdown
8
+ // ---------------------------------------------------------------------------
9
+ /** Letters that keep an `_` beside them from opening emphasis (CJK do not). */
10
+ const UNDERSCORE_BLOCKER = /^(?:_|(?![\p{sc=Han}\p{sc=Hira}\p{sc=Kana}\p{sc=Hang}])[\p{L}\p{N}])$/u;
11
+ /** Escape the characters with inline meaning so they print literally. An
12
+ * `_` between two letters is text already and stays bare. */
13
+ export function escapeText(text, before = '', after = '') {
14
+ let out = '';
15
+ const chars = [...text];
16
+ for (let i = 0; i < chars.length; i++) {
17
+ const ch = chars[i];
18
+ if (ch === '*' || ch === '^' || ch === '~' || ch === '$' || ch === '`')
19
+ out += '\\' + ch;
20
+ else if (ch === '_') {
21
+ const prev = i > 0 ? chars[i - 1] : [...before].pop() ?? '';
22
+ const next = i + 1 < chars.length ? chars[i + 1] : [...after][0] ?? '';
23
+ out += UNDERSCORE_BLOCKER.test(prev) && UNDERSCORE_BLOCKER.test(next) ? '_' : '\\_';
24
+ }
25
+ else
26
+ out += ch;
27
+ }
28
+ // `[text](url)` and `:name[` / `:name{` typed as text would become a link
29
+ // or a directive: an invisible word joiner keeps them literal.
30
+ return out
31
+ .replace(/\]\(/g, `]${WORD_JOINER}(`)
32
+ .replace(/(?<![\\:]):(?=[a-z][a-z0-9-]*[[{])/g, `:${WORD_JOINER}`)
33
+ .replace(/\[(?=\^)/g, `[${WORD_JOINER}`);
34
+ }
35
+ const LINE_START_TRAP = /^(?:\d+[.)]\s|[٠-٩۰-۹]+[.)]\s|[-*+]\s|>|#{1,6}\s|:::|::resource|\$\$|\[\^[^\]]*\]:)/;
36
+ /** A word joiner in front of a paragraph that would otherwise open a list,
37
+ * heading, quote, fence or note definition. */
38
+ export function guardLineStart(line) {
39
+ return LINE_START_TRAP.test(line) ? WORD_JOINER + line : line;
40
+ }
41
+ /** A link destination Postext reads back whole: spaces, `$`, backslashes,
42
+ * angle brackets and unbalanced parentheses percent-encoded. */
43
+ export function linkDestination(url) {
44
+ const u = url.trim();
45
+ let depth = 0;
46
+ let balanced = true;
47
+ for (const ch of u) {
48
+ if (ch === '(')
49
+ depth++;
50
+ else if (ch === ')' && --depth < 0)
51
+ balanced = false;
52
+ }
53
+ balanced = balanced && depth === 0;
54
+ let out = '';
55
+ for (const ch of u) {
56
+ if (/\s/.test(ch))
57
+ out += '%20';
58
+ else if ('$\\<>'.includes(ch) || ('()'.includes(ch) && !balanced))
59
+ out += '%' + ch.charCodeAt(0).toString(16).toUpperCase().padStart(2, '0');
60
+ else
61
+ out += ch;
62
+ }
63
+ return out;
64
+ }
65
+ const sameFlags = (a, b) => !!a.bold === !!b.bold && !!a.italic === !!b.italic && !!a.smallCaps === !!b.smallCaps && a.script === b.script
66
+ && !!a.raw === !!b.raw && a.href === b.href && a.chip === b.chip && a.note === undefined && b.note === undefined;
67
+ function merge(runs) {
68
+ const out = [];
69
+ for (const r of runs) {
70
+ if (!r.text && r.note === undefined)
71
+ continue;
72
+ const last = out[out.length - 1];
73
+ if (last && sameFlags(last, r))
74
+ last.text += r.text;
75
+ else
76
+ out.push({ ...r });
77
+ }
78
+ return out;
79
+ }
80
+ const NEUTRAL_RE = /^\s*$/u;
81
+ const WORD_RE = '(?:(?![\\u3040-\\u30ff\\u3100-\\u312f\\u31a0-\\u31ff\\u3400-\\u4dbf\\u4e00-\\u9fff\\uf900-\\ufaff])[\\p{L}\\p{N}_])+';
82
+ const WORD_TAIL = new RegExp(`(${WORD_RE})$`, 'u');
83
+ const WORD_HEAD = new RegExp(`^(${WORD_RE})`, 'u');
84
+ const plainRun = (r) => !r.raw && !r.script && !r.chip && r.note === undefined && !r.href;
85
+ /** Style boundaries on word boundaries: spaces and punctuation between two
86
+ * runs of one style take that style; a style change inside a word moves to
87
+ * the word's edge (the longer side wins). */
88
+ export function normaliseRuns(input, tidy = true) {
89
+ let runs = merge(input.map((r) => ({ ...r })));
90
+ if (!tidy)
91
+ return runs;
92
+ for (let i = 0; i < runs.length; i++) {
93
+ const r = runs[i];
94
+ if (!plainRun(r) || !NEUTRAL_RE.test(r.text))
95
+ continue;
96
+ const prev = runs[i - 1];
97
+ const next = runs[i + 1];
98
+ if (prev && next && plainRun(prev) && plainRun(next) && !!prev.bold === !!next.bold && !!prev.italic === !!next.italic && !!prev.smallCaps === !!next.smallCaps) {
99
+ r.bold = prev.bold;
100
+ r.italic = prev.italic;
101
+ r.smallCaps = prev.smallCaps;
102
+ }
103
+ else if (r.text.trim() === '') {
104
+ r.bold = r.italic = r.smallCaps = undefined;
105
+ }
106
+ }
107
+ runs = merge(runs);
108
+ for (let guard = 0, changed = true; changed && guard < 4 * Math.max(1, runs.length); guard++) {
109
+ changed = false;
110
+ for (let i = 0; i < runs.length - 1; i++) {
111
+ const a = runs[i];
112
+ const b = runs[i + 1];
113
+ if (!plainRun(a) || !plainRun(b) || sameFlags(a, b))
114
+ continue;
115
+ const ma = WORD_TAIL.exec(a.text);
116
+ const mb = WORD_HEAD.exec(b.text);
117
+ if (!ma || !mb)
118
+ continue;
119
+ const left = ma[1];
120
+ const right = mb[1];
121
+ if (left.length >= right.length) {
122
+ a.text += right;
123
+ b.text = b.text.slice(right.length);
124
+ }
125
+ else {
126
+ b.text = left + b.text;
127
+ a.text = a.text.slice(0, -left.length);
128
+ }
129
+ changed = true;
130
+ }
131
+ runs = merge(runs);
132
+ }
133
+ return runs;
134
+ }
135
+ /** Split leading and trailing whitespace off a group so markers hug text. */
136
+ function edges(runs, tidy = true) {
137
+ const inner = runs.map((r) => ({ ...r }));
138
+ if (!tidy && inner.some((r) => r.text.trim() || r.raw))
139
+ return { lead: '', inner, trail: '' };
140
+ let lead = '';
141
+ let trail = '';
142
+ const first = inner[0];
143
+ if (first && !first.raw && first.note === undefined) {
144
+ const m = /^\s+/.exec(first.text);
145
+ if (m) {
146
+ lead = m[0];
147
+ first.text = first.text.slice(lead.length);
148
+ }
149
+ }
150
+ const last = inner[inner.length - 1];
151
+ if (last && !last.raw && last.note === undefined) {
152
+ const m = /\s+$/.exec(last.text);
153
+ if (m) {
154
+ trail = m[0];
155
+ last.text = last.text.slice(0, -trail.length);
156
+ }
157
+ }
158
+ return { lead, inner: inner.filter((r) => r.text || r.note !== undefined), trail };
159
+ }
160
+ /** Group consecutive runs by a key (null: runs outside any group). */
161
+ function groups(runs, keyOf) {
162
+ const out = [];
163
+ for (const r of runs) {
164
+ const k = keyOf(r);
165
+ const last = out[out.length - 1];
166
+ if (last && last.key === k)
167
+ last.runs.push(r);
168
+ else
169
+ out.push({ key: k, runs: [r] });
170
+ }
171
+ return out;
172
+ }
173
+ function renderLeaf(runs, ctx) {
174
+ let out = '';
175
+ runs.forEach((r, i) => {
176
+ if (r.note !== undefined)
177
+ out += `[^${r.note}]`;
178
+ else if (r.raw)
179
+ out += r.text;
180
+ else if (r.script) {
181
+ const { lead, inner, trail } = edges([{ ...r, script: undefined }]);
182
+ const text = inner.map((x) => x.text).join('');
183
+ const mark = r.script === 'sup' ? '^' : '~';
184
+ out += text ? `${lead}${mark}${escapeText(text)}${mark}${trail}` : escapeText(lead + trail);
185
+ }
186
+ else {
187
+ const next = runs[i + 1];
188
+ out += escapeText(r.text, ctx.before + out, next && !next.raw ? next.text : '');
189
+ }
190
+ });
191
+ ctx.before += out;
192
+ return out;
193
+ }
194
+ function renderSmallCaps(runs, ctx) {
195
+ return groups(runs, (r) => (r.smallCaps ? 'sc' : null)).map((g) => {
196
+ if (g.key === null)
197
+ return renderLeaf(g.runs, ctx);
198
+ const { lead, inner, trail } = edges(g.runs.map((r) => ({ ...r, smallCaps: undefined })), ctx.tidy);
199
+ if (!inner.length)
200
+ return renderLeaf(g.runs.map((r) => ({ ...r, smallCaps: undefined })), ctx);
201
+ const body = renderLeaf(inner, { before: '', tidy: ctx.tidy }).replace(/\]/g, '\\]');
202
+ const s = `${lead}:smallcaps[${body}]${trail}`;
203
+ ctx.before += s;
204
+ return s;
205
+ }).join('');
206
+ }
207
+ function renderItalic(runs, ctx) {
208
+ return groups(runs, (r) => (r.italic ? 'i' : null)).map((g) => {
209
+ if (g.key === null)
210
+ return renderSmallCaps(g.runs, ctx);
211
+ const { lead, inner, trail } = edges(g.runs.map((r) => ({ ...r, italic: undefined })), ctx.tidy);
212
+ if (!inner.length || inner.every((r) => r.raw && !r.text.trim()))
213
+ return renderSmallCaps(g.runs.map((r) => ({ ...r, italic: undefined })), ctx);
214
+ ctx.before += lead + '*';
215
+ const s = `${lead}*${renderSmallCaps(inner, ctx)}*${trail}`;
216
+ ctx.before += '*' + trail;
217
+ return s;
218
+ }).join('');
219
+ }
220
+ function renderEmphasis(runs, ctx) {
221
+ return groups(runs, (r) => (r.bold ? 'b' : null)).map((g) => {
222
+ if (g.key === null)
223
+ return renderItalic(g.runs, ctx);
224
+ const { lead, inner, trail } = edges(g.runs.map((r) => ({ ...r, bold: undefined })), ctx.tidy);
225
+ if (!inner.length)
226
+ return renderItalic(g.runs.map((r) => ({ ...r, bold: undefined })), ctx);
227
+ if (inner.every((r) => r.italic)) {
228
+ ctx.before += lead + '***';
229
+ const s = `${lead}***${renderSmallCaps(inner.map((r) => ({ ...r, italic: undefined })), ctx)}***${trail}`;
230
+ ctx.before += '***' + trail;
231
+ return s;
232
+ }
233
+ ctx.before += lead + '**';
234
+ const s = `${lead}**${renderItalic(inner, ctx)}**${trail}`;
235
+ ctx.before += '**' + trail;
236
+ return s;
237
+ }).join('');
238
+ }
239
+ /** Postext inline Markdown for runs. Links and chips wrap their own runs. */
240
+ export function renderInline(input, opts = {}) {
241
+ const runs = normaliseRuns(input, opts.tidy ?? true);
242
+ const ctx = { before: '', tidy: opts.tidy ?? true };
243
+ let out = '';
244
+ for (const g of groups(runs, (r) => (r.href !== undefined ? `h:${r.href}` : r.chip !== undefined ? `c:${r.chip}` : null))) {
245
+ if (g.key === null) {
246
+ out += renderEmphasis(g.runs, ctx);
247
+ continue;
248
+ }
249
+ const first = g.runs[0];
250
+ const inner = g.runs.map((r) => ({ ...r, href: undefined, chip: undefined }));
251
+ if (first.href !== undefined) {
252
+ const { lead, inner: body, trail } = edges(inner, ctx.tidy);
253
+ const text = renderInline(body, { tidy: opts.tidy, collapseSpaces: false });
254
+ const s = text ? `${lead}[${text}](${linkDestination(first.href)})${trail}` : lead + trail;
255
+ out += s;
256
+ ctx.before += s;
257
+ }
258
+ else {
259
+ const { lead, inner: body, trail } = edges(inner, ctx.tidy);
260
+ const text = renderInline(body, { tidy: opts.tidy }).replace(/\]/g, '\\]').replace(/\s+/g, ' ');
261
+ const s = text ? `${lead}:chip[${text}]{style="${first.chip}"}${trail}` : lead + trail;
262
+ out += s;
263
+ ctx.before += s;
264
+ }
265
+ }
266
+ if (opts.collapseSpaces === false)
267
+ return out;
268
+ // Spaces typed twice are a Word habit; an export keeps the source's.
269
+ return ctx.tidy ? out.replace(/[ \t\r\n]+/g, ' ') : out.replace(/\r?\n/g, ' ');
270
+ }
271
+ const PH_BASE = 0xe800;
272
+ const PH_END = 0xf8ff;
273
+ /** Index of the `]` that closes the `[` at `open`, honouring `\]` and
274
+ * nested brackets; -1 when none. */
275
+ function closeBracket(text, open) {
276
+ let depth = 0;
277
+ for (let i = open; i < text.length; i++) {
278
+ const c = text[i];
279
+ if (c === '\\') {
280
+ i++;
281
+ continue;
282
+ }
283
+ if (c === '\n')
284
+ return -1;
285
+ if (c === '[')
286
+ depth++;
287
+ else if (c === ']' && --depth === 0)
288
+ return i;
289
+ }
290
+ return -1;
291
+ }
292
+ /** End (exclusive) of a link destination opened at `from` (after `(`). */
293
+ function destinationEnd(text, from) {
294
+ if (text[from] === '<') {
295
+ const close = text.indexOf('>', from);
296
+ if (close < 0)
297
+ return -1;
298
+ return text[close + 1] === ')' ? close + 2 : -1;
299
+ }
300
+ let depth = 0;
301
+ for (let i = from; i < text.length; i++) {
302
+ const c = text[i];
303
+ if (c === '\\') {
304
+ i++;
305
+ continue;
306
+ }
307
+ if (c === '(')
308
+ depth++;
309
+ else if (c === ')') {
310
+ if (depth === 0)
311
+ return i + 1;
312
+ depth--;
313
+ }
314
+ else if (c === '\n')
315
+ return -1;
316
+ }
317
+ return -1;
318
+ }
319
+ const ESCAPABLE = new Set(['*', '_', '^', '~', '$', '`']);
320
+ const CJK_BASE = /[\p{sc=Han}\p{sc=Hira}\p{sc=Kana}㄀-ㄯ]/u;
321
+ /** Tokenise the constructs Word has no emphasis for into atoms, leaving a
322
+ * string where each atom is one private-use placeholder character. */
323
+ function protect(text, opts) {
324
+ const atoms = [];
325
+ let s = '';
326
+ const add = (a) => {
327
+ s += String.fromCharCode(PH_BASE + atoms.length);
328
+ atoms.push(a);
329
+ };
330
+ let i = 0;
331
+ while (i < text.length) {
332
+ const c = text[i];
333
+ const rest = text.slice(i);
334
+ const code = c.charCodeAt(0);
335
+ if (code >= PH_BASE && code <= PH_END) {
336
+ add({ kind: 'char', text: c });
337
+ i++;
338
+ continue;
339
+ }
340
+ if (c === '\\') {
341
+ if (opts.heading && text[i + 1] === '\\') {
342
+ const m = /^[ \t]*\\\\[ \t]*/.exec(rest);
343
+ add({ kind: 'raw', text: m[0] });
344
+ i += m[0].length;
345
+ continue;
346
+ }
347
+ const n = text[i + 1];
348
+ if (n !== undefined && ESCAPABLE.has(n)) {
349
+ add({ kind: 'char', text: n });
350
+ i += 2;
351
+ continue;
352
+ }
353
+ s += c;
354
+ i++;
355
+ continue;
356
+ }
357
+ if (c === '`') {
358
+ const m = /^`[^`\n]+?`/.exec(rest);
359
+ if (m) {
360
+ add({ kind: 'raw', text: m[0] });
361
+ i += m[0].length;
362
+ continue;
363
+ }
364
+ }
365
+ if (c === '$') {
366
+ if (text[i + 1] === '$') {
367
+ add({ kind: 'raw', text: '$$' });
368
+ i += 2;
369
+ continue;
370
+ }
371
+ let j = i + 1;
372
+ let close = -1;
373
+ while (j < text.length) {
374
+ if (text[j] === '\\' && text[j + 1] === '$') {
375
+ j += 2;
376
+ continue;
377
+ }
378
+ if (text[j] === '$') {
379
+ close = j;
380
+ break;
381
+ }
382
+ j++;
383
+ }
384
+ add({ kind: 'raw', text: close > i + 1 ? text.slice(i, close + 1) : '$' });
385
+ i = close > i + 1 ? close + 1 : i + 1;
386
+ continue;
387
+ }
388
+ if (c === '[') {
389
+ const note = /^\[\^([\p{L}\p{N}_.:-]+)\]/u.exec(rest);
390
+ if (note) {
391
+ const id = note[1];
392
+ if (opts.notes?.has(id) && (opts.takeNote?.(id) ?? true))
393
+ add({ kind: 'note', id });
394
+ else
395
+ add({ kind: 'raw', text: note[0] });
396
+ i += note[0].length;
397
+ continue;
398
+ }
399
+ const close = closeBracket(text, i);
400
+ if (close > i && text[close + 1] === '(') {
401
+ const end = destinationEnd(text, close + 2);
402
+ if (end > 0) {
403
+ const dest = text.slice(close + 2, end - 1);
404
+ if (!/\s"/.test(dest) && !dest.startsWith('<'))
405
+ add({ kind: 'link', inner: text.slice(i + 1, close), dest });
406
+ else
407
+ add({ kind: 'raw', text: text.slice(i, end) });
408
+ i = end;
409
+ continue;
410
+ }
411
+ }
412
+ }
413
+ if (c === ':') {
414
+ const chip = /^:chip\[((?:\\.|[^\]\\\n])+)\](?:\{([^}\n]*)\})?/.exec(rest);
415
+ if (chip) {
416
+ const attrs = chip[2] ?? '';
417
+ const style = /^\s*style\s*=\s*"([^"]*)"\s*$/.exec(attrs)?.[1] ?? /^\s*style\s*=\s*([^\s"]+)\s*$/.exec(attrs)?.[1];
418
+ if (style !== undefined && !/[$`:[]/.test(chip[1]))
419
+ add({ kind: 'chip', inner: chip[1], style });
420
+ else
421
+ add({ kind: 'raw', text: chip[0] });
422
+ i += chip[0].length;
423
+ continue;
424
+ }
425
+ const sc = /^:smallcaps\[((?:\\.|[^\]\\\n])+)\]/.exec(rest);
426
+ if (sc && !/[[:]/.test(sc[1])) {
427
+ add({ kind: 'smallcaps', inner: sc[1] });
428
+ i += sc[0].length;
429
+ continue;
430
+ }
431
+ const dir = /^:[a-z][a-zA-Z0-9-]*/.exec(rest);
432
+ if (dir && (rest[dir[0].length] === '[' || rest[dir[0].length] === '{') && text[i - 1] !== '\\') {
433
+ let end = i + dir[0].length;
434
+ if (text[end] === '[') {
435
+ const close = closeBracket(text, end);
436
+ end = close > 0 ? close + 1 : end;
437
+ }
438
+ if (text[end] === '{') {
439
+ const close = text.indexOf('}', end);
440
+ if (close > 0 && !text.slice(end, close).includes('\n'))
441
+ end = close + 1;
442
+ }
443
+ if (end > i + dir[0].length) {
444
+ add({ kind: 'raw', text: text.slice(i, end) });
445
+ i = end;
446
+ continue;
447
+ }
448
+ }
449
+ }
450
+ if (c === '{') {
451
+ const ruby = /^\{([^{}|\n]+)\|([^{}\n]+)\}/.exec(rest);
452
+ if (ruby && CJK_BASE.test(ruby[1])) {
453
+ add({ kind: 'raw', text: ruby[0] });
454
+ i += ruby[0].length;
455
+ continue;
456
+ }
457
+ }
458
+ s += c;
459
+ i++;
460
+ }
461
+ return { s, atoms };
462
+ }
463
+ const UB = '(?:_|(?![\\p{sc=Han}\\p{sc=Hira}\\p{sc=Kana}\\p{sc=Hang}])[\\p{L}\\p{N}])';
464
+ const usOpen = (n) => `(?<!${UB})${'_'.repeat(n)}(?!_)`;
465
+ const usClose = (n) => `(?<!_)${'_'.repeat(n)}(?!${UB})`;
466
+ const SUP = '\\^(\\S(?:[^^\\n]*?\\S)?)\\^';
467
+ const EA = '(?:[\\u3000-\\u30ff\\u3400-\\u4dbf\\u4e00-\\u9fff\\uf900-\\ufaff\\uff01-\\uff60]|[\\ud840-\\ud8bf][\\udc00-\\udfff])';
468
+ const TILDE = '(?!(?<=[0-9])~[0-9])~';
469
+ const TILDE_OPEN = `(?!(?<=${EA})~${EA})${TILDE}`;
470
+ const SUB = `${TILDE_OPEN}(\\S(?:[^~\\n]*?\\S)?)${TILDE}`;
471
+ function scripts(text, bold, italic, out) {
472
+ const re = new RegExp(`${SUP}|${SUB}`, 'g');
473
+ let last = 0;
474
+ let m;
475
+ while ((m = re.exec(text)) !== null) {
476
+ if (m.index > last)
477
+ out.push({ text: text.slice(last, m.index), bold, italic });
478
+ if (m[1] !== undefined)
479
+ out.push({ text: m[1], bold, italic, script: 'sup' });
480
+ else
481
+ out.push({ text: m[2], bold, italic, script: 'sub' });
482
+ last = m.index + m[0].length;
483
+ }
484
+ if (last < text.length)
485
+ out.push({ text: text.slice(last), bold, italic });
486
+ }
487
+ function italics(text, bold, forced, out) {
488
+ if (forced) {
489
+ scripts(text, bold, true, out);
490
+ return;
491
+ }
492
+ const re = new RegExp(`\\*(.+?)\\*|${usOpen(1)}(.+?)${usClose(1)}`, 'gu');
493
+ let last = 0;
494
+ let m;
495
+ while ((m = re.exec(text)) !== null) {
496
+ if (m.index > last)
497
+ scripts(text.slice(last, m.index), bold, false, out);
498
+ scripts(m[1] ?? m[2], bold, true, out);
499
+ last = m.index + m[0].length;
500
+ }
501
+ if (last < text.length)
502
+ scripts(text.slice(last), bold, false, out);
503
+ }
504
+ /** The engine's emphasis scan (bold first, then italics, then scripts). */
505
+ function emphasis(text) {
506
+ const re = new RegExp(`\\*\\*\\*(.+?)\\*\\*\\*|${usOpen(3)}(.+?)${usClose(3)}|\\*\\*(.+?)\\*\\*|${usOpen(2)}(.+?)${usClose(2)}`, 'gu');
507
+ const out = [];
508
+ let last = 0;
509
+ let m;
510
+ while ((m = re.exec(text)) !== null) {
511
+ if (m.index > last)
512
+ italics(text.slice(last, m.index), false, false, out);
513
+ const triple = m[1] ?? m[2];
514
+ if (triple !== undefined)
515
+ italics(triple, true, true, out);
516
+ else
517
+ italics(m[3] ?? m[4], true, false, out);
518
+ last = m.index + m[0].length;
519
+ }
520
+ if (last < text.length)
521
+ italics(text.slice(last), false, false, out);
522
+ return out;
523
+ }
524
+ /** Postext inline Markdown → runs. Every character with inline meaning the
525
+ * runs cannot say (an unpaired `*`, a `$` that opens nothing) is kept as a
526
+ * verbatim run, so `renderInline(parseInline(x))` reads as `x`. */
527
+ export function parseInline(text, opts = {}) {
528
+ const { s, atoms } = protect(text, opts);
529
+ const out = [];
530
+ const push = (r) => {
531
+ const last = out[out.length - 1];
532
+ if (last && r.note === undefined && sameFlags(last, r))
533
+ last.text += r.text;
534
+ else
535
+ out.push(r);
536
+ };
537
+ for (const span of emphasis(s)) {
538
+ const base = { text: '', ...(span.bold ? { bold: true } : {}), ...(span.italic ? { italic: true } : {}), ...(span.script ? { script: span.script } : {}) };
539
+ const chars = [...span.text];
540
+ chars.forEach((ch, ci) => {
541
+ const code = ch.charCodeAt(0);
542
+ if (code >= PH_BASE && code <= PH_END && ch.length === 1) {
543
+ const atom = atoms[code - PH_BASE];
544
+ switch (atom.kind) {
545
+ case 'char':
546
+ push({ ...base, text: atom.text });
547
+ return;
548
+ case 'raw':
549
+ push({ ...base, text: atom.text, raw: true });
550
+ return;
551
+ case 'note':
552
+ push({ text: '', note: atom.id });
553
+ return;
554
+ case 'link':
555
+ for (const r of parseInline(atom.inner, opts))
556
+ push({ ...r, bold: r.bold || base.bold || undefined, italic: r.italic || base.italic || undefined, href: atom.dest });
557
+ return;
558
+ case 'chip':
559
+ for (const r of parseInline(atom.inner.replace(/\\\]/g, ']'), opts))
560
+ push({ ...r, bold: r.bold || base.bold || undefined, italic: r.italic || base.italic || undefined, chip: atom.style });
561
+ return;
562
+ case 'smallcaps':
563
+ for (const r of parseInline(atom.inner.replace(/\\([[\]])/g, '$1'), opts))
564
+ push({ ...r, bold: r.bold || base.bold || undefined, italic: r.italic || base.italic || undefined, smallCaps: true });
565
+ return;
566
+ }
567
+ }
568
+ // A marker character the scan left: verbatim, unless re-importing
569
+ // would write it bare anyway (an `_` inside a word).
570
+ if (ch === '*' || ch === '^' || ch === '~' || ch === '`') {
571
+ push({ ...base, text: ch, raw: true });
572
+ return;
573
+ }
574
+ if (ch === '_') {
575
+ const prev = chars[ci - 1] ?? '';
576
+ const next = chars[ci + 1] ?? '';
577
+ if (!(UNDERSCORE_BLOCKER.test(prev) && UNDERSCORE_BLOCKER.test(next))) {
578
+ push({ ...base, text: ch, raw: true });
579
+ return;
580
+ }
581
+ }
582
+ push({ ...base, text: ch });
583
+ });
584
+ }
585
+ return out.map((r) => {
586
+ const clean = { text: r.text };
587
+ if (r.bold)
588
+ clean.bold = true;
589
+ if (r.italic)
590
+ clean.italic = true;
591
+ if (r.smallCaps)
592
+ clean.smallCaps = true;
593
+ if (r.script)
594
+ clean.script = r.script;
595
+ if (r.raw)
596
+ clean.raw = true;
597
+ if (r.href !== undefined)
598
+ clean.href = r.href;
599
+ if (r.chip !== undefined)
600
+ clean.chip = r.chip;
601
+ if (r.note !== undefined)
602
+ clean.note = r.note;
603
+ return clean;
604
+ });
605
+ }
606
+ //# sourceMappingURL=inline.js.map