xmmath 1.0.9 → 1.1.2

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/src/main.ts CHANGED
@@ -1,1940 +1,977 @@
1
1
  /// <reference types="vite/client" />
2
2
 
3
- function createEl<K extends keyof HTMLElementTagNameMap>(tagName: K): HTMLElementTagNameMap[K];
4
- function createEl<K extends keyof HTMLElementDeprecatedTagNameMap>(tagName: K): HTMLElementDeprecatedTagNameMap[K];
5
- function createEl(tagName: string): HTMLElement;
6
- function createEl(tagname: string) {
7
- return document.createElement(tagname);
8
- }
9
-
10
- function createMath<K extends keyof MathMLElementTagNameMap>(
11
- tagname: K,
12
- innerText?: string,
13
- attr?: { [name: string]: string }
14
- ) {
15
- let el = document.createElementNS("http://www.w3.org/1998/Math/MathML", tagname);
16
- if (innerText) el.textContent = innerText;
17
- if (attr) {
18
- for (let i in attr) {
19
- el.setAttribute(i, attr[i]);
20
- }
21
- }
22
- return el;
23
- }
3
+ import { ast, init_c, type tree } from "./ast.js";
4
+ import {
5
+ ast2,
6
+ ast3,
7
+ dh,
8
+ emojix,
9
+ eqq,
10
+ f_attr,
11
+ type fdic,
12
+ in_kh,
13
+ is_br,
14
+ is_limit,
15
+ is_true,
16
+ isDisplay,
17
+ opl,
18
+ setDisplay,
19
+ ss,
20
+ transfer_kh,
21
+ trim,
22
+ v_f,
23
+ } from "./normalize.js";
24
+ import {
25
+ createFragment,
26
+ createMath,
27
+ toDom,
28
+ toHtml,
29
+ type VEl,
30
+ type VFragment,
31
+ } from "./vdom.js";
24
32
 
25
33
  const mathvariant = "mathvariant";
26
34
 
27
- const Segmenter = Intl.Segmenter;
28
- const segmenter = new Segmenter("emoji", { granularity: "grapheme" });
29
-
30
- type vtype = "" | "str" | "v" | "f" | "blank" | "group" | "group1" | "sharp"; // group1组合基本类型,提高优先级
31
- type tree = { type: vtype; value: string; children?: tree; esc?: boolean; kh?: string; src?: string }[];
32
-
33
- function ast(str: string) {
34
- let v = /[a-zA-Z]/;
35
- let kh = /[\(\)\{\}\[\]]/;
36
- const khl = /[\(\{\[]/;
37
- const khr = /[\)\}\]]/;
38
- let blank = /^[ \t\n\r]+$/;
39
- let type: vtype = "";
40
- let ignore: false | "line" | "block" = false;
41
- let o: tree = [];
42
- let p_tree: { tree: tree; close: boolean }[] = [];
43
- let now_tree = o;
44
- let tmp_str = "";
45
-
46
- str = str.replace(/(\\u\{[\dA-Fa-f]+\})/g, (v) => {
47
- const codePoint = parseInt(v.slice(3, -1), 16);
48
- return String.fromCodePoint(codePoint);
49
- });
50
-
51
- let lkh_stack: number[] = [];
52
- let rkh_stack: number[] = [];
53
- for (let i = 0; i < str.length; i++) {
54
- const t = str[i];
55
- const last = str[i - 1];
56
- // 字符
57
- if (t === '"' && last != "\\" && !ignore) {
58
- if (type != "str") {
59
- type = "str";
60
- } else {
61
- type = "";
62
- }
63
- continue;
64
- }
65
- if (type === "str") {
66
- continue;
67
- } else {
68
- const next = str[i + 1];
69
- if (t === "/" && next === "/") ignore = "line";
70
- if (ignore === "line" && t === "\n") {
71
- i++;
72
- ignore = false;
73
- continue;
74
- }
75
- if (t === "/" && next === "*") ignore = "block";
76
- if (ignore === "block" && t === "*" && next === "/") {
77
- i++;
78
- ignore = false;
79
- continue;
80
- }
81
- }
82
-
83
- if (t.match(khl) && !ignore && last != "\\") {
84
- lkh_stack.push(i);
85
- }
86
- if (t.match(khr) && !ignore && last != "\\") {
87
- if (lkh_stack.length === 0) {
88
- rkh_stack.push(i);
89
- } else {
90
- lkh_stack.pop();
91
- }
92
- }
93
- }
94
- ignore = false;
95
-
96
- let strl = init_c.emoji ? init_c.emoji(str) : Array.from(segmenter.segment(str)).map((w) => w.segment);
97
- let strl2 = [];
98
- for (let i of strl) {
99
- if (i.length > 1 && i.includes(",")) strl2.push(...i);
100
- else strl2.push(i);
101
- }
102
- strl = strl2;
103
-
104
- for (let i = 0; i < strl.length; i++) {
105
- const t = strl[i];
106
- const next = strl[i + 1];
107
- // 注释
108
- if (t === "/" && next === "/") ignore = "line";
109
- if (ignore === "line" && t === "\n") {
110
- i++;
111
- ignore = false;
112
- continue;
113
- }
114
- if (t === "/" && next === "*") ignore = "block";
115
- if (ignore === "block" && t === "*" && next === "/") {
116
- i++;
117
- ignore = false;
118
- continue;
119
- }
120
-
121
- if (ignore) continue;
122
-
123
- // 空白
124
- if (type === "blank" && !t.match(blank)) {
125
- now_tree.push({ type, value: "" });
126
- type = "";
127
- }
128
- // 字符
129
- if (t === '"' && strl[i - 1] != "\\") {
130
- if (type != "str") {
131
- type = "str";
132
- } else {
133
- now_tree.push({ type, value: tmp_str });
134
- type = "";
135
- tmp_str = "";
136
- }
137
- continue;
138
- }
139
- if (type == "str") {
140
- tmp_str += t;
141
- continue;
142
- }
143
-
144
- // 空白
145
- if (t.match(blank)) {
146
- type = "blank";
147
- }
148
-
149
- if (t === "#") {
150
- now_tree.push({ type: "sharp", value: "" });
151
- continue;
152
- }
153
-
154
- // 原始值(字母变量、数字、符号)
155
- if (
156
- type == "" &&
157
- (!strl[i - 1] || !strl[i - 1].match(v)) &&
158
- (!strl[i + 1] || !strl[i + 1].match(v)) &&
159
- !t.match(kh)
160
- ) {
161
- now_tree.push({ type: "v", value: t });
162
- continue;
163
- }
164
- if (type == "" && !t.match(v) && !t.match(kh)) {
165
- now_tree.push({ type: "v", value: t });
166
- continue;
167
- }
168
-
169
- // 函数名
170
- if (((strl[i - 1] && strl[i - 1].match(v)) || (strl[i + 1] && strl[i + 1].match(v))) && t.match(v)) {
171
- type = "f";
172
- }
173
- if (type == "f") {
174
- tmp_str += t;
175
- }
176
- if (type == "f" && (!strl[i + 1] || !strl[i + 1].match(v))) {
177
- now_tree.push({ type, value: tmp_str });
178
- type = "";
179
- tmp_str = "";
180
- }
181
-
182
- if (t.match(khl)) {
183
- if (strl[i - 1] === "\\") {
184
- now_tree.push({ type: "v", value: t, esc: true });
185
- } else if (lkh_stack.includes(i)) {
186
- now_tree.push({ type: "v", value: t });
187
- } else {
188
- p_tree.push({ tree: now_tree, close: false });
189
- now_tree.push({ type: "group", value: tmp_str, children: [], kh: t });
190
- now_tree = now_tree.at(-1).children;
191
- tmp_str = "";
192
- continue;
193
- }
194
- }
195
- if (t.match(khr)) {
196
- if (strl[i - 1] === "\\") {
197
- now_tree.push({ type: "v", value: t, esc: true });
198
- } else if (p_tree.at(-1)) {
199
- if (p_tree.at(-1).close === false) {
200
- p_tree.at(-1).close = true;
201
- now_tree = p_tree.at(-1).tree;
202
- now_tree.at(-1).kh += t;
203
- p_tree.pop();
204
- continue;
205
- }
206
- } else {
207
- now_tree.push({ type: "v", value: t });
208
- }
209
- }
210
- }
211
-
212
- return o;
213
- }
214
-
215
- import symbols from "./symbols.json?raw";
216
- /** @see https://github.com/typst/typst/blob/v0.11.1/crates/typst/src/symbols/sym.rs */
217
- let s = JSON.parse(symbols);
218
-
219
- // symbols路径简写
220
-
221
- function simple_dot(s: any) {
222
- const ss: { [id: string]: string } = {};
223
- for (let i in s) {
224
- if (typeof s[i] == "string") {
225
- ss[i] = s[i];
226
- } else {
227
- for (let objOrStr of s[i]) {
228
- // 第二层的array
229
- if (typeof objOrStr == "string") {
230
- ss[i] = objOrStr;
231
- } else {
232
- if (!ss[i]) ss[i] = objOrStr[Object.keys(objOrStr)[0]];
233
- for (let j in objOrStr) {
234
- // 第三层的obj
235
- ss[`${i}.${j}`] = objOrStr[j];
236
- // 允许部分索引,但要保证唯一
237
- let l = j.split(".");
238
- for (let n = 1; n < l.length; n++) {
239
- let shotKey = l.slice(0, n).join(".");
240
- if (!objOrStr[shotKey]) {
241
- ss[`${i}.${shotKey}`] = objOrStr[j];
242
- }
243
- }
244
- }
245
- }
246
- }
247
- }
248
- }
249
- return ss;
250
- }
251
- let ss = simple_dot(s);
252
-
253
- import emoji from "./emoji.json?raw";
254
- /** @see https://github.com/typst/typst/blob/v0.11.1/crates/typst/src/symbols/emoji.rs */
255
- let emojix = simple_dot(JSON.parse(emoji));
256
-
257
- let shorthand = {
258
- "->": "arrow.r",
259
- "|->": "arrow.r.bar",
260
- "=>": "arrow.r.double",
261
- "|=>": "arrow.r.double.bar",
262
- "==>": "arrow.r.double.long",
263
- "-->": "arrow.r.long",
264
- "~~>": "arrow.r.long.squiggly",
265
- "~>": "arrow.r.squiggly",
266
- ">->": "arrow.r.tail",
267
- "->>": "arrow.r.twohead",
268
- "<-": "arrow.l",
269
- "<==": "arrow.l.double.long",
270
- "<--": "arrow.l.long",
271
- "<~~": "arrow.l.long.squiggly",
272
- "<~": "arrow.l.squiggly",
273
- "<-<": "arrow.l.tail",
274
- "<<-": "arrow.l.twohead",
275
- "<->": "arrow.l.r",
276
- "<=>": "arrow.l.r.double",
277
- "<==>": "arrow.l.r.double.long",
278
- "<-->": "arrow.l.r.long",
279
- "*": "convolve",
280
- "||": "bar.v.double",
281
- "[|": "bracket.l.double",
282
- "|]": "bracket.r.double",
283
- ":=": "colon.eq",
284
- "::=": "colon.double.eq",
285
- "--": "dash.en",
286
- "---": "dash.em",
287
- "...": "dots.h",
288
- "=:": "eq.colon",
289
- "!=": "eq.not",
290
- ">>": "gt.double",
291
- ">=": "gt.eq",
292
- ">>>": "gt.triple",
293
- "-?": "hyph.soft",
294
- "<<": "lt.double",
295
- "<=": "lt.eq",
296
- "<<<": "lt.triple",
297
- "-": "minus",
298
- "'": "prime",
299
- "~": "space.nobreak",
300
- };
301
- let max_shorthand_len = 0;
302
- for (let i in shorthand) {
303
- if (i.length > max_shorthand_len) {
304
- max_shorthand_len = i.length;
305
- }
306
- }
307
-
308
35
  const delimPair = {
309
- "(": ["(", ")"],
310
- "[": ["[", "]"],
311
- "{": ["{", "}"],
312
- "|": ["|", "|"],
313
- "||": ["‖", "‖"],
314
- "": ["", ""],
36
+ "(": ["(", ")"],
37
+ "[": ["[", "]"],
38
+ "{": ["{", "}"],
39
+ "|": ["|", "|"],
40
+ "||": ["‖", "‖"],
41
+ "": ["", ""],
315
42
  };
316
43
 
44
+ // attach 底座的 limits 是否生效。子树要等 render 才做 ast3 的「处理f」收编,
45
+ // 这里 dispatch 到 attach 时 `limits(a)` 还是 [f(limits), group(a)] 两个并列节点,
46
+ // 故先按 ast3 同样的规则收编(f 紧跟 kh === "()" 的组;prime 除外)再判定,
47
+ // 使 `limits(a)`/`scripts(a)`/`op(.., limits: #true)` 与 `_`/`^` 走同一口径。
48
+ function limits_active(base: tree) {
49
+ const t: tree =
50
+ base.length === 2 &&
51
+ base[0].type === "f" &&
52
+ base[0].value !== "prime" &&
53
+ base[1].type === "group" &&
54
+ base[1].kh === "()"
55
+ ? [{ ...base[0], children: base[1].children }]
56
+ : base;
57
+ return !!is_limit(t);
58
+ }
59
+
317
60
  function delim(dic: fdic, _default: string) {
318
- const d = get_value(dic, "delim") as string;
319
- let x = "";
320
- if (d !== null)
321
- if (d === undefined) x = _default;
322
- else x = d;
323
- return delimPair[x] as [string, string];
61
+ const d = get_value(dic, "delim") as string;
62
+ let x = "";
63
+ if (d !== null)
64
+ if (d === undefined) x = _default;
65
+ else x = d;
66
+ return delimPair[x] as [string, string];
324
67
  }
325
68
 
326
- let f: {
327
- [name: string]: (attr: tree[], dic?: fdic, e?: fonts) => MathMLElement | DocumentFragment;
69
+ const f: {
70
+ [name: string]: (attr: tree[], dic?: fdic, e?: fonts) => VEl | VFragment;
328
71
  } = {
329
- accent: (attr: tree[], dic: fdic, e) => {
330
- let base = createMath("mrow");
331
- base.append(render(attr[0], e));
332
- let a = createMath("mrow");
333
- a.append(render(attr[1], e));
334
- a.children[0].innerHTML = accent_match_str(a.children[0].innerHTML);
335
- let over = createMath("mover");
336
- over.setAttribute("accent", "true");
337
- over.append(base, a);
338
- return over;
339
- },
340
- attach: (attr: tree[], dic: fdic, e) => {
341
- let base = createMath("mrow");
342
- base.append(render(attr[0]));
343
- let el: MathMLElement;
344
- let tl = createMath("mrow");
345
- if (dic.tl) tl.append(render(dic.tl, e));
346
- let bl = createMath("mrow");
347
- if (dic.bl) bl.append(render(dic.bl, e));
348
- let tr = createMath("mrow");
349
- if (dic.tr) tr.append(render(dic.tr, e));
350
- let br = createMath("mrow");
351
- if (dic.br) br.append(render(dic.br, e));
352
- if (dic.tl || dic.bl || dic.tr || dic.br) {
353
- if (dic.tl || dic.bl) {
354
- el = createMath("mmultiscripts");
355
- el.append(base, br, tr, createMath("mprescripts"), bl, tl);
356
- } else if (dic.tr && dic.br) {
357
- el = createMath("msubsup");
358
- el.append(base, br, tr);
359
- } else if (dic.tr) {
360
- el = createMath("msup");
361
- el.append(base, tr);
362
- } else {
363
- el = createMath("msub");
364
- el.append(base, br);
365
- }
366
- }
367
- if (dic.t || dic.b) {
368
- let uo = createMath("munderover");
369
- if (!el) {
370
- uo.append(base);
371
- } else {
372
- uo.append(el);
373
- }
374
- let t = createMath("mrow");
375
- if (dic.t) t.append(render(dic.t, e));
376
- let b = createMath("mrow");
377
- if (dic.b) b.append(render(dic.b, e));
378
- uo.append(b, t);
379
- el = uo;
380
- }
381
- return el;
382
- },
383
- scripts: (attr: tree[], dic: fdic, e) => {
384
- return render(attr[0], e);
385
- },
386
- limits: (attr: tree[], dic: fdic, e) => {
387
- return render(attr[0], e);
388
- },
389
- binom: (attr: tree[], dic: fdic, e) => {
390
- let row = createMath("mrow");
391
- let a = createMath("mrow");
392
- a.append(render(attr[0], e));
393
- let b = createMath("mrow");
394
- const s: tree = [];
395
- for (let x of attr.slice(1)) s.push(...x, dh);
396
- b.append(render(s.slice(0, -1), e));
397
- let f = createMath("mfrac", null, { linethickness: "0" });
398
- f.append(a, b);
399
- let l = createMath("mo", "(");
400
- let r = createMath("mo", ")");
401
- row.append(l, f, r);
402
- return row;
403
- },
404
- cancel: (attr: tree[], dic: fdic, e) => {
405
- let r = createMath("mrow");
406
- const bg = (x: boolean) =>
407
- `linear-gradient(to ${
408
- x ? "left" : "right"
409
- } top, transparent 47.75%, currentColor 49.5%, currentColor 50.5%, transparent 52.25%)`;
410
- let s = "";
411
- if (is_true(dic.cross)) {
412
- s = bg(true) + "," + bg(false);
413
- } else if (is_true(dic.inverted)) {
414
- s = bg(false);
415
- } else {
416
- s = bg(true);
417
- }
418
- r.style.backgroundImage = s;
419
- r.append(render(attr[0], e));
420
- return r;
421
- },
422
- cases: (attr: tree[], dic: fdic, e) => {
423
- let r = createMath("mrow");
424
- const d = delim(dic, "{");
425
- let t = f.x_table(attr, { cases: [] }, e) as MathMLElement;
426
- const gap = (get_value(dic, "gap") as string) || "0.5em";
427
- t.setAttribute("rowspacing", gap);
428
- if (is_true(dic?.reverse)) {
429
- const l = createMath("mo", d[1]);
430
- r.append(t, l);
431
- } else {
432
- const l = createMath("mo", d[0]);
433
- r.append(l, t);
434
- }
435
- return r;
436
- },
437
- frac: (attr: tree[], dic: fdic, e) => {
438
- let a = createMath("mrow");
439
- a.append(render(attr[0], e));
440
- let b = createMath("mrow");
441
- b.append(render(attr[1], e));
442
- let f = createMath("mfrac");
443
- f.append(a, b);
444
- return f;
445
- },
446
- lr: (attr: tree[], dic: fdic, e) => {
447
- let list = attr_join(attr.map((i) => transfer_kh(i)));
448
- const tList = trim(list);
449
-
450
- const size = get_value(dic, "size") as string;
451
- const c = render(tList, e);
452
- const row = createMath("mrow");
453
- row.append(c);
454
- if (size && size != "auto") {
455
- const lm = row.children[0];
456
- const rm = row.children[row.children.length - 1];
457
- lm?.setAttribute("maxsize", size);
458
- lm?.setAttribute("minsize", size);
459
- rm?.setAttribute("maxsize", size);
460
- rm?.setAttribute("minsize", size);
461
- }
462
- return row;
463
- },
464
- mid: (attr: tree[], dic: fdic) => {
465
- const o = createMath("mo", attr?.[0]?.[0]?.value, { stretchy: "true" });
466
- return o;
467
- },
468
- mat: (attr: tree[] | tree[][], dic: fdic, e) => {
469
- let d = delim(dic, "(");
470
- let row = createMath("mrow");
471
- let l = createMath("mo", d[0]);
472
- let r = createMath("mo", d[1]);
473
- let t = createMath("mtable");
474
- let array: tree[][];
475
- if ((attr[0][0] as tree[0])?.type) array = [attr as tree[]];
476
- else array = attr as tree[][];
477
-
478
- let augment = get_value(dic, "augment");
479
- if (augment) {
480
- let xa = NaN;
481
- let ya = NaN;
482
- if (typeof augment != "object") {
483
- xa = Number(augment);
484
- } else {
485
- if (augment["hline"]) ya = Number((augment["hline"] as tree).map((i) => i.value).join(""));
486
- if (augment["vline"]) xa = Number((augment["vline"] as tree).map((i) => i.value).join(""));
487
- }
488
- if (xa < 0) xa = array[0].length + xa;
489
- if (ya < 0) ya = array.length + ya;
490
-
491
- if (ya) {
492
- let l: boolean[] = [];
493
- for (let i = 1; i < array.length; i++) l.push(i === ya);
494
- t.setAttribute("rowlines", l.map((i) => (i ? "solid" : "none")).join(" "));
495
- }
496
- if (xa) {
497
- let l: boolean[] = [];
498
- for (let i = 1; i < array[0].length; i++) l.push(i === xa);
499
- t.setAttribute("columnlines", l.map((i) => (i ? "solid" : "none")).join(" "));
500
- }
501
- }
502
- const gap = get_value(dic, "gap") as string;
503
- const rowgap = get_value(dic, "row-gap") as string;
504
- const colgap = get_value(dic, "column-gap") as string;
505
- t.setAttribute("columnspacing", colgap || gap || "0.5em");
506
- t.setAttribute("rowspacing", rowgap || gap || "0.5em");
507
-
508
- for (let i of array) {
509
- let tr = createMath("mtr");
510
- for (let j of i) {
511
- let td = createMath("mtd");
512
- td.append(render(j, e));
513
- tr.append(td);
514
- }
515
- t.append(tr);
516
- }
517
- row.append(l, t, r);
518
- return row;
519
- },
520
- root: (attr: tree[], dic: fdic, e) => {
521
- let row = createMath("mrow");
522
- row.append(render(attr[0], e));
523
- let base = createMath("mrow");
524
- base.append(render(attr[1], e));
525
- let root = createMath("mroot");
526
- root.append(base, row);
527
- return root;
528
- },
529
- sqrt: (attr: tree[], dic: fdic, e) => {
530
- return f.root([[], attr[0]], null, e);
531
- },
532
- display: (attr: tree[], dic: fdic, e) => {
533
- let m = createMath("mrow", null, { displaystyle: "true" });
534
- m.append(render(attr[0], e));
535
- return m;
536
- },
537
- inline: (attr: tree[], dic: fdic, e) => {
538
- let m = createMath("mrow", null, { displaystyle: "false", scriptlevel: "0" });
539
- m.append(render(attr[0], e));
540
- return m;
541
- },
542
- script: (attr: tree[], dic: fdic, e) => {
543
- let m = createMath("mrow", null, { displaystyle: "false", scriptlevel: "1" });
544
- m.append(render(attr[0], e));
545
- return m;
546
- },
547
- sscript: (attr: tree[], dic: fdic, e) => {
548
- let m = createMath("mrow", null, { displaystyle: "false", scriptlevel: "2" });
549
- m.append(render(attr[0], e));
550
- return m;
551
- },
552
- upright: (attr: tree[], dic: fdic, e) => {
553
- let r = createMath("mrow");
554
- r.append(render(attr[0], e));
555
- r.querySelectorAll("mi").forEach((el) => {
556
- if (!el.getAttribute(mathvariant)) el.setAttribute(mathvariant, "normal");
557
- });
558
- r.querySelectorAll("ms").forEach((el) => {
559
- if (!el.getAttribute(mathvariant)) el.setAttribute(mathvariant, "normal");
560
- });
561
- return r;
562
- },
563
- italic: (attr: tree[], dic: fdic, e) => {
564
- let r = createMath("mrow");
565
- r.append(render(attr[0], e));
566
- r.querySelectorAll("mi").forEach((el) => {
567
- if (!el.getAttribute(mathvariant)) el.setAttribute(mathvariant, "italic");
568
- });
569
- r.querySelectorAll("ms").forEach((el) => {
570
- if (!el.getAttribute(mathvariant)) el.setAttribute(mathvariant, "italic");
571
- });
572
- return r;
573
- },
574
- bold: (attr: tree[], dic: fdic, e) => {
575
- let r = createMath("mrow");
576
- r.style.fontWeight = "bold";
577
- r.append(render(attr[0], e));
578
- return r;
579
- },
580
- op: (attr: tree[], dic: fdic, e) => {
581
- let f = createMath("mrow");
582
- let str = createMath("ms");
583
- str.append(render(attr[0], e));
584
- f.append(str);
585
- return f;
586
- },
587
- underline: (attr: tree[], dic: fdic, e) => {
588
- return underover_line_f("under", attr[0], e);
589
- },
590
- overline: (attr: tree[], dic: fdic, e) => {
591
- return underover_line_f("over", attr[0], e);
592
- },
593
- underbrace: (attr: tree[], dic: fdic, e) => {
594
- return underover_f("under", attr[0], "⏟", attr?.[1], e);
595
- },
596
- overbrace: (attr: tree[], dic: fdic, e) => {
597
- return underover_f("over", attr[0], "⏞", attr?.[1], e);
598
- },
599
- underbracket: (attr: tree[], dic: fdic, e) => {
600
- return underover_f("under", attr[0], "⎵", attr?.[1], e);
601
- },
602
- overbracket: (attr: tree[], dic: fdic, e) => {
603
- return underover_f("over", attr[0], "⎴", attr?.[1], e);
604
- },
605
- serif: (attr: tree[]) => {
606
- return render(attr[0], "serif");
607
- },
608
- sans: (attr: tree[]) => {
609
- return render(attr[0], "sans");
610
- },
611
- frak: (attr: tree[]) => {
612
- return render(attr[0], "frak");
613
- },
614
- mono: (attr: tree[]) => {
615
- return render(attr[0], "mono");
616
- },
617
- bb: (attr: tree[]) => {
618
- return render(attr[0], "bb");
619
- },
620
- cal: (attr: tree[]) => {
621
- return render(attr[0], "cal");
622
- },
623
- vec: (attr: tree[], dic: fdic, e) => {
624
- let d = delim(dic, "(");
625
- let row = createMath("mrow");
626
- let l = createMath("mo", d[0]);
627
- let r = createMath("mo", d[1]);
628
- let t = x_table(attr, e);
629
- const gap = (get_value(dic, "gap") as string) || "0.5em";
630
- t.setAttribute("rowspacing", gap);
631
- t.setAttribute("columnspacing", "0.5em");
632
- row.append(l, t, r);
633
- return row;
634
- },
635
- h: (attr: tree[], dic: fdic) => {
636
- return createMath("mspace", "", { width: attr[0][0].value });
637
- },
638
- // 额外
639
- //
640
- x_table: (attr: tree[], dic: fdic) => {
641
- let t = x_table(attr);
642
- if (dic.cases) t.setAttribute("columnalign", "left");
643
- return t;
644
- },
72
+ accent: (attr: tree[], _dic: fdic, e) => {
73
+ const base = createMath("mrow");
74
+ base.append(render(attr[0], e));
75
+ const a = createMath("mrow");
76
+ a.append(render(attr[1], e));
77
+ a.children[0].innerHTML = accent_match_str(a.children[0].innerHTML);
78
+ const over = createMath("mover");
79
+ over.setAttribute("accent", "true");
80
+ over.append(base, a);
81
+ return over;
82
+ },
83
+ attach: (attr: tree[], dic: fdic, e) => {
84
+ // typst v0.11.1 attach.rs::layout_math 的「聪明定位」:
85
+ // let limits = base.limits().active(styles);
86
+ // let (t, tr) = if limits || tr.is_some() { (t, tr) } else { (None, t) };
87
+ // let (b, br) = if limits || br.is_some() { (b, br) } else { (None, b) };
88
+ // 即底座 limits 不生效时 t/b 让位到右上/右下脚标位(与 `_`/`^` 同一套判定),
89
+ // 只有 tr/br 已被占用时才留在正上/正下(外层套 mover/munder);
90
+ // limits 生效时 t/b 恒落正上/正下,角标仍各占自己的角。
91
+ const d: fdic = { ...dic };
92
+ if (!limits_active(attr[0])) {
93
+ if (d.t && !d.tr) {
94
+ d.tr = d.t;
95
+ delete d.t;
96
+ }
97
+ if (d.b && !d.br) {
98
+ d.br = d.b;
99
+ delete d.b;
100
+ }
101
+ }
102
+ const base = createMath("mrow");
103
+ base.append(render(attr[0]));
104
+ let el: VEl;
105
+ const tl = createMath("mrow");
106
+ if (d.tl) tl.append(render(d.tl, e));
107
+ const bl = createMath("mrow");
108
+ if (d.bl) bl.append(render(d.bl, e));
109
+ const tr = createMath("mrow");
110
+ if (d.tr) tr.append(render(d.tr, e));
111
+ const br = createMath("mrow");
112
+ if (d.br) br.append(render(d.br, e));
113
+ if (d.tl || d.bl || d.tr || d.br) {
114
+ if (d.tl || d.bl) {
115
+ el = createMath("mmultiscripts");
116
+ el.append(base, br, tr, createMath("mprescripts"), bl, tl);
117
+ } else if (d.tr && d.br) {
118
+ el = createMath("msubsup");
119
+ el.append(base, br, tr);
120
+ } else if (d.tr) {
121
+ el = createMath("msup");
122
+ el.append(base, tr);
123
+ } else {
124
+ el = createMath("msub");
125
+ el.append(base, br);
126
+ }
127
+ }
128
+ if (d.t || d.b) {
129
+ const uo = createMath("munderover");
130
+ if (!el) {
131
+ uo.append(base);
132
+ } else {
133
+ uo.append(el);
134
+ }
135
+ const t = createMath("mrow");
136
+ if (d.t) t.append(render(d.t, e));
137
+ const b = createMath("mrow");
138
+ if (d.b) b.append(render(d.b, e));
139
+ uo.append(b, t);
140
+ el = uo;
141
+ }
142
+ return el;
143
+ },
144
+ scripts: (attr: tree[], _dic: fdic, e) => {
145
+ return render(attr[0], e);
146
+ },
147
+ limits: (attr: tree[], _dic: fdic, e) => {
148
+ return render(attr[0], e);
149
+ },
150
+ binom: (attr: tree[], _dic: fdic, e) => {
151
+ const row = createMath("mrow");
152
+ const a = createMath("mrow");
153
+ a.append(render(attr[0], e));
154
+ const b = createMath("mrow");
155
+ const s: tree = [];
156
+ for (const x of attr.slice(1)) s.push(...x, dh);
157
+ b.append(render(s.slice(0, -1), e));
158
+ const f = createMath("mfrac", null, { linethickness: "0" });
159
+ f.append(a, b);
160
+ const l = createMath("mo", "(");
161
+ const r = createMath("mo", ")");
162
+ row.append(l, f, r);
163
+ return row;
164
+ },
165
+ cancel: (attr: tree[], dic: fdic, e) => {
166
+ const r = createMath("mrow");
167
+ const bg = (x: boolean) =>
168
+ `linear-gradient(to ${
169
+ x ? "left" : "right"
170
+ } top, transparent 47.75%, currentColor 49.5%, currentColor 50.5%, transparent 52.25%)`;
171
+ let s = "";
172
+ if (is_true(dic.cross)) {
173
+ s = `${bg(true)},${bg(false)}`;
174
+ } else if (is_true(dic.inverted)) {
175
+ s = bg(false);
176
+ } else {
177
+ s = bg(true);
178
+ }
179
+ r.style.backgroundImage = s;
180
+ r.append(render(attr[0], e));
181
+ return r;
182
+ },
183
+ cases: (attr: tree[], dic: fdic, e) => {
184
+ const r = createMath("mrow");
185
+ const d = delim(dic, "{");
186
+ const t = f.x_table(attr, { cases: [] }, e) as VEl;
187
+ const gap = (get_value(dic, "gap") as string) || "0.5em";
188
+ t.setAttribute("rowspacing", gap);
189
+ if (is_true(dic?.reverse)) {
190
+ const l = createMath("mo", d[1]);
191
+ r.append(t, l);
192
+ } else {
193
+ const l = createMath("mo", d[0]);
194
+ r.append(l, t);
195
+ }
196
+ return r;
197
+ },
198
+ frac: (attr: tree[], _dic: fdic, e) => {
199
+ const a = createMath("mrow");
200
+ a.append(render(attr[0], e));
201
+ const b = createMath("mrow");
202
+ b.append(render(attr[1], e));
203
+ const f = createMath("mfrac");
204
+ f.append(a, b);
205
+ return f;
206
+ },
207
+ lr: (attr: tree[], dic: fdic, e) => {
208
+ const list = attr_join(attr.map((i) => transfer_kh(i)));
209
+ const tList = trim(list);
210
+
211
+ const size = get_value(dic, "size") as string;
212
+ const c = render(tList, e);
213
+ const row = createMath("mrow");
214
+ row.append(c);
215
+ if (size && size !== "auto") {
216
+ const lm = row.children[0];
217
+ const rm = row.children[row.children.length - 1];
218
+ lm?.setAttribute("maxsize", size);
219
+ lm?.setAttribute("minsize", size);
220
+ rm?.setAttribute("maxsize", size);
221
+ rm?.setAttribute("minsize", size);
222
+ }
223
+ return row;
224
+ },
225
+ mid: (attr: tree[], _dic: fdic) => {
226
+ const o = createMath("mo", attr?.[0]?.[0]?.value, { stretchy: "true" });
227
+ return o;
228
+ },
229
+ mat: (attr: tree[] | tree[][], dic: fdic, e) => {
230
+ const d = delim(dic, "(");
231
+ const row = createMath("mrow");
232
+ const l = createMath("mo", d[0]);
233
+ const r = createMath("mo", d[1]);
234
+ const t = createMath("mtable");
235
+ let array: tree[][];
236
+ if ((attr[0][0] as tree[0])?.type) array = [attr as tree[]];
237
+ else array = attr as tree[][];
238
+
239
+ const augment = get_value(dic, "augment");
240
+ if (augment) {
241
+ let xa = NaN;
242
+ let ya = NaN;
243
+ if (typeof augment !== "object") {
244
+ xa = Number(augment);
245
+ } else {
246
+ if ("hline" in augment && augment.hline)
247
+ ya = Number((augment.hline as tree).map((i) => i.value).join(""));
248
+ if ("vline" in augment && augment.vline)
249
+ xa = Number((augment.vline as tree).map((i) => i.value).join(""));
250
+ }
251
+ if (xa < 0) xa = array[0].length + xa;
252
+ if (ya < 0) ya = array.length + ya;
253
+
254
+ if (ya) {
255
+ const l: boolean[] = [];
256
+ for (let i = 1; i < array.length; i++) l.push(i === ya);
257
+ t.setAttribute(
258
+ "rowlines",
259
+ l.map((i) => (i ? "solid" : "none")).join(" "),
260
+ );
261
+ }
262
+ if (xa) {
263
+ const l: boolean[] = [];
264
+ for (let i = 1; i < array[0].length; i++) l.push(i === xa);
265
+ t.setAttribute(
266
+ "columnlines",
267
+ l.map((i) => (i ? "solid" : "none")).join(" "),
268
+ );
269
+ }
270
+ }
271
+ const gap = get_value(dic, "gap") as string;
272
+ const rowgap = get_value(dic, "row-gap") as string;
273
+ const colgap = get_value(dic, "column-gap") as string;
274
+ t.setAttribute("columnspacing", colgap || gap || "0.5em");
275
+ t.setAttribute("rowspacing", rowgap || gap || "0.5em");
276
+
277
+ for (const i of array) {
278
+ const tr = createMath("mtr");
279
+ for (const j of i) {
280
+ const td = createMath("mtd");
281
+ td.append(render(j, e));
282
+ tr.append(td);
283
+ }
284
+ t.append(tr);
285
+ }
286
+ row.append(l, t, r);
287
+ return row;
288
+ },
289
+ root: (attr: tree[], _dic: fdic, e) => {
290
+ const row = createMath("mrow");
291
+ row.append(render(attr[0], e));
292
+ const base = createMath("mrow");
293
+ base.append(render(attr[1], e));
294
+ const root = createMath("mroot");
295
+ root.append(base, row);
296
+ return root;
297
+ },
298
+ // msqrt(与 typst html 导出一致:<msqrt>…</msqrt>),被开方数直接作为子元素,
299
+ // 不包 mrow、不走 mroot——空指数的 mroot 会被 MathML 校验/渲染当成错误结构。
300
+ sqrt: (attr: tree[], _dic: fdic, e) => {
301
+ const s = createMath("msqrt");
302
+ s.append(render(attr[0], e));
303
+ return s;
304
+ },
305
+ display: (attr: tree[], _dic: fdic, e) => {
306
+ const m = createMath("mrow", null, { displaystyle: "true" });
307
+ m.append(render(attr[0], e));
308
+ return m;
309
+ },
310
+ inline: (attr: tree[], _dic: fdic, e) => {
311
+ const m = createMath("mrow", null, {
312
+ displaystyle: "false",
313
+ scriptlevel: "0",
314
+ });
315
+ m.append(render(attr[0], e));
316
+ return m;
317
+ },
318
+ script: (attr: tree[], _dic: fdic, e) => {
319
+ const m = createMath("mrow", null, {
320
+ displaystyle: "false",
321
+ scriptlevel: "1",
322
+ });
323
+ m.append(render(attr[0], e));
324
+ return m;
325
+ },
326
+ sscript: (attr: tree[], _dic: fdic, e) => {
327
+ const m = createMath("mrow", null, {
328
+ displaystyle: "false",
329
+ scriptlevel: "2",
330
+ });
331
+ m.append(render(attr[0], e));
332
+ return m;
333
+ },
334
+ upright: (attr: tree[], _dic: fdic, e) => {
335
+ const r = createMath("mrow");
336
+ r.append(render(attr[0], e));
337
+ r.querySelectorAll("mi").forEach((el) => {
338
+ if (!el.getAttribute(mathvariant)) el.setAttribute(mathvariant, "normal");
339
+ });
340
+ r.querySelectorAll("ms").forEach((el) => {
341
+ if (!el.getAttribute(mathvariant)) el.setAttribute(mathvariant, "normal");
342
+ });
343
+ return r;
344
+ },
345
+ italic: (attr: tree[], _dic: fdic, e) => {
346
+ const r = createMath("mrow");
347
+ r.append(render(attr[0], e));
348
+ r.querySelectorAll("mi").forEach((el) => {
349
+ if (!el.getAttribute(mathvariant)) el.setAttribute(mathvariant, "italic");
350
+ });
351
+ r.querySelectorAll("ms").forEach((el) => {
352
+ if (!el.getAttribute(mathvariant)) el.setAttribute(mathvariant, "italic");
353
+ });
354
+ return r;
355
+ },
356
+ bold: (attr: tree[], _dic: fdic, e) => {
357
+ const r = createMath("mrow");
358
+ r.style.fontWeight = "bold";
359
+ r.append(render(attr[0], e));
360
+ return r;
361
+ },
362
+ op: (attr: tree[], _dic: fdic, e) => {
363
+ const f = createMath("mrow");
364
+ const str = createMath("ms");
365
+ str.append(render(attr[0], e));
366
+ f.append(str);
367
+ return f;
368
+ },
369
+ underline: (attr: tree[], _dic: fdic, e) => {
370
+ return underover_line_f("under", attr[0], e);
371
+ },
372
+ overline: (attr: tree[], _dic: fdic, e) => {
373
+ return underover_line_f("over", attr[0], e);
374
+ },
375
+ underbrace: (attr: tree[], _dic: fdic, e) => {
376
+ return underover_f("under", attr[0], "⏟", attr?.[1], e);
377
+ },
378
+ overbrace: (attr: tree[], _dic: fdic, e) => {
379
+ return underover_f("over", attr[0], "⏞", attr?.[1], e);
380
+ },
381
+ underbracket: (attr: tree[], _dic: fdic, e) => {
382
+ return underover_f("under", attr[0], "⎵", attr?.[1], e);
383
+ },
384
+ overbracket: (attr: tree[], _dic: fdic, e) => {
385
+ return underover_f("over", attr[0], "⎴", attr?.[1], e);
386
+ },
387
+ serif: (attr: tree[]) => {
388
+ return render(attr[0], "serif");
389
+ },
390
+ sans: (attr: tree[]) => {
391
+ return render(attr[0], "sans");
392
+ },
393
+ frak: (attr: tree[]) => {
394
+ return render(attr[0], "frak");
395
+ },
396
+ mono: (attr: tree[]) => {
397
+ return render(attr[0], "mono");
398
+ },
399
+ bb: (attr: tree[]) => {
400
+ return render(attr[0], "bb");
401
+ },
402
+ cal: (attr: tree[]) => {
403
+ return render(attr[0], "cal");
404
+ },
405
+ vec: (attr: tree[], dic: fdic, e) => {
406
+ const d = delim(dic, "(");
407
+ const row = createMath("mrow");
408
+ const l = createMath("mo", d[0]);
409
+ const r = createMath("mo", d[1]);
410
+ const t = x_table(attr, e);
411
+ const gap = (get_value(dic, "gap") as string) || "0.5em";
412
+ t.setAttribute("rowspacing", gap);
413
+ t.setAttribute("columnspacing", "0.5em");
414
+ row.append(l, t, r);
415
+ return row;
416
+ },
417
+ h: (attr: tree[], _dic: fdic) => {
418
+ return createMath("mspace", "", { width: attr[0][0].value });
419
+ },
420
+ // 额外
421
+ //
422
+ x_table: (attr: tree[], dic: fdic) => {
423
+ const t = x_table(attr);
424
+ if (dic.cases) t.setAttribute("columnalign", "left");
425
+ return t;
426
+ },
645
427
  };
646
428
 
647
- function x_table(trees: tree[], e?: fonts) {
648
- let max = 0;
649
- let t = createMath("mtable");
650
- for (let i of trees) {
651
- const n = i.filter((x) => eqq(x, v_f("&"))).length;
652
- if (n > max) max = n;
653
- }
654
- for (let i of trees) {
655
- let r = createMath("mtr");
656
-
657
- // 按&拆分
658
- let result: tree[] = [[]];
659
- for (let n = 0; n < i.length; n++) {
660
- if (eqq(i[n], v_f("&"))) {
661
- result.push([]);
662
- } else {
663
- result.at(-1).push(i[n]);
664
- }
665
- }
666
- for (let i of result) {
667
- let d = createMath("mtd");
668
- r.append(d);
669
- d.append(render(i, e));
670
- }
671
-
672
- // 交替对齐
673
- const al = [];
674
- for (let i = 0; i <= max; i++) {
675
- if (i % 2 === 0) {
676
- al.push("right");
677
- } else {
678
- al.push("left");
679
- }
680
- }
681
- t.setAttribute("columnalign", al.join(" "));
682
- t.setAttribute("columnspacing", "0");
683
- t.append(r);
684
- }
685
- return t;
429
+ function x_table(trees: tree[], e?: fonts, inline?: boolean) {
430
+ let max = 0;
431
+ const t = createMath("mtable");
432
+ for (const i of trees) {
433
+ const n = i.filter((x) => eqq(x, v_f("&"))).length;
434
+ if (n > max) max = n;
435
+ }
436
+ for (const i of trees) {
437
+ const r = createMath("mtr");
438
+
439
+ // 按&拆分
440
+ const result: tree[] = [[]];
441
+ for (let n = 0; n < i.length; n++) {
442
+ if (eqq(i[n], v_f("&"))) {
443
+ result.push([]);
444
+ } else {
445
+ result.at(-1).push(i[n]);
446
+ }
447
+ }
448
+ for (const i of result) {
449
+ const d = createMath("mtd");
450
+ r.append(d);
451
+ d.append(render(i, e));
452
+ }
453
+
454
+ // 有 & 时围绕对齐点 right/left 交替;没有 & 时 typst 块级居中、行内贴左
455
+ // (mat/vec 的单元格不随块/行内变化,始终居中,MathML 默认列对齐也是 center)
456
+ const al = [];
457
+ for (let i = 0; i <= max; i++) {
458
+ if (max > 0) al.push(i % 2 === 0 ? "right" : "left");
459
+ else al.push(inline ? "left" : "center");
460
+ }
461
+ t.setAttribute("columnalign", al.join(" "));
462
+ t.setAttribute("columnspacing", "0");
463
+ t.append(r);
464
+ }
465
+ return t;
466
+ }
467
+
468
+ // typst 把 1–4 个连续素号折成连字,5 个及以上重复 ′
469
+ function primeRun(n: number) {
470
+ const lig = [
471
+ ss.prime,
472
+ ss["prime.double"],
473
+ ss["prime.triple"],
474
+ ss["prime.quad"],
475
+ ];
476
+ return n <= 4 ? lig[n - 1] : ss.prime.repeat(n);
686
477
  }
687
478
 
688
- let opl: { id: string; str?: string; limits?: boolean }[] = [
689
- { id: "arccos" },
690
- { id: "arcsin" },
691
- { id: "arctan" },
692
- { id: "arg" },
693
- { id: "cos" },
694
- { id: "cosh" },
695
- { id: "cot" },
696
- { id: "ctg" },
697
- { id: "coth" },
698
- { id: "csc" },
699
- { id: "csch" },
700
- { id: "deg" },
701
- { id: "det", limits: true },
702
- { id: "dim" },
703
- { id: "exp" },
704
- { id: "gcd", limits: true },
705
- { id: "hom" },
706
- { id: "mod" },
707
- { id: "id" },
708
- { id: "im" },
709
- { id: "inf", limits: true },
710
- { id: "ker" },
711
- { id: "lg" },
712
- { id: "lim", limits: true },
713
- { id: "ln" },
714
- { id: "log" },
715
- { id: "max", limits: true },
716
- { id: "min", limits: true },
717
- { id: "Pr", limits: true },
718
- { id: "sec" },
719
- { id: "sech" },
720
- { id: "sin" },
721
- { id: "sinc" },
722
- { id: "sinh" },
723
- { id: "sup", limits: true },
724
- { id: "tan" },
725
- { id: "tg" },
726
- { id: "tr" },
727
- { id: "tanh" },
728
- { id: "liminf", str: "lim inf", limits: true },
729
- { id: "limsup", str: "lim sup", limits: true },
730
- { id: "dif", str: "d" },
731
- { id: "Dif", str: "D" },
732
- ];
733
479
  function op_f() {
734
- for (let i of opl) {
735
- f[i.id] = (attr: tree[], a, e) => {
736
- let s = f.op([[{ type: "str", value: i.str || i.id }]], {}, e);
737
- if (attr) {
738
- let f = document.createDocumentFragment();
739
- f.append(s, kh(attr_join(attr)));
740
- return f;
741
- } else {
742
- return s;
743
- }
744
- };
745
- }
480
+ for (const i of opl) {
481
+ f[i.id] = (attr: tree[], _a, e) => {
482
+ const s = f.op([[{ type: "str", value: i.str || i.id }]], {}, e);
483
+ if (attr) {
484
+ const f = createFragment();
485
+ f.append(s, kh(attr_join(attr)));
486
+ return f;
487
+ } else {
488
+ return s;
489
+ }
490
+ };
491
+ }
746
492
  }
747
493
  op_f();
748
494
 
749
495
  const spaceConst = {
750
- quad: "1em",
751
- med: "0.222em",
752
- thin: "0.17em",
753
- thick: "0.28em",
754
- wide: "2em",
496
+ quad: "1em",
497
+ med: "0.222em",
498
+ thin: "0.17em",
499
+ thick: "0.28em",
500
+ wide: "2em",
755
501
  };
756
502
 
757
- for (let i in spaceConst) {
758
- f[i] = () => {
759
- return f.h([[{ type: "str", value: spaceConst[i] }]]);
760
- };
761
- }
762
-
763
- let limits_f = [];
764
- let limits_sy = ["∏", "∐", "∑", "⋀", "⋁", "⋂", "⋃", "⨀", "⨁", "⨂", "⨃", "⨄", "⨅", "⨆"];
765
- for (let i in ss) {
766
- for (let j of limits_sy) {
767
- if (ss[i] == j) {
768
- limits_f.push(i);
769
- }
770
- }
503
+ for (const i in spaceConst) {
504
+ f[i] = () => {
505
+ return f.h([[{ type: "str", value: spaceConst[i] }]]);
506
+ };
771
507
  }
772
508
 
773
509
  function accent_match_str(c: string) {
774
- switch (c) {
775
- case "\u{0300}":
776
- case "`":
777
- return "\u{0300}";
778
- case "\u{0301}":
779
- case "´":
780
- return "\u{0301}";
781
- case "\u{0302}":
782
- case "^":
783
- case "ˆ":
784
- return "\u{0302}";
785
- case "\u{0303}":
786
- case "~":
787
- case "∼":
788
- case "˜":
789
- return "\u{0303}";
790
- case "\u{0304}":
791
- case "¯":
792
- return "\u{0304}";
793
- case "\u{0305}":
794
- case "-":
795
- case "‾":
796
- case "−":
797
- return "\u{0305}";
798
- case "\u{0306}":
799
- case "˘":
800
- return "\u{0306}";
801
- case "\u{0307}":
802
- case ".":
803
- case "˙":
804
- case "⋅":
805
- return "\u{0307}";
806
- case "\u{0308}":
807
- case "¨":
808
- return "\u{0308}";
809
- case "\u{20db}":
810
- return "\u{20db}";
811
- case "\u{20dc}":
812
- return "\u{20dc}";
813
- case "\u{030a}":
814
- case "∘":
815
- case "○":
816
- return "\u{030a}";
817
- case "\u{030b}":
818
- case "˝":
819
- return "\u{030b}";
820
- case "\u{030c}":
821
- case "ˇ":
822
- return "\u{030c}";
823
- case "\u{20d6}":
824
- case "←":
825
- return "\u{20d6}";
826
- case "\u{20d7}":
827
- case "→":
828
- case "⟶":
829
- return "\u{20d7}";
830
- case "\u{20e1}":
831
- case "↔":
832
- case "⟷":
833
- return "\u{20e1}";
834
- case "\u{20d0}":
835
- case "↼":
836
- return "\u{20d0}";
837
- case "\u{20d1}":
838
- case "⇀":
839
- return "\u{20d1}";
840
- default:
841
- return c;
842
- }
510
+ switch (c) {
511
+ case "\u{0300}":
512
+ case "`":
513
+ return "\u{0300}";
514
+ case "\u{0301}":
515
+ case "´":
516
+ return "\u{0301}";
517
+ case "\u{0302}":
518
+ case "^":
519
+ case "ˆ":
520
+ return "\u{0302}";
521
+ case "\u{0303}":
522
+ case "~":
523
+ case "∼":
524
+ case "˜":
525
+ return "\u{0303}";
526
+ case "\u{0304}":
527
+ case "¯":
528
+ return "\u{0304}";
529
+ case "\u{0305}":
530
+ case "-":
531
+ case "‾":
532
+ case "−":
533
+ return "\u{0305}";
534
+ case "\u{0306}":
535
+ case "˘":
536
+ return "\u{0306}";
537
+ case "\u{0307}":
538
+ case ".":
539
+ case "˙":
540
+ case "⋅":
541
+ return "\u{0307}";
542
+ case "\u{0308}":
543
+ case "¨":
544
+ return "\u{0308}";
545
+ case "\u{20db}":
546
+ return "\u{20db}";
547
+ case "\u{20dc}":
548
+ return "\u{20dc}";
549
+ case "\u{030a}":
550
+ case "∘":
551
+ case "○":
552
+ return "\u{030a}";
553
+ case "\u{030b}":
554
+ case "˝":
555
+ return "\u{030b}";
556
+ case "\u{030c}":
557
+ case "ˇ":
558
+ return "\u{030c}";
559
+ case "\u{20d6}":
560
+ case "←":
561
+ return "\u{20d6}";
562
+ case "\u{20d7}":
563
+ case "→":
564
+ case "⟶":
565
+ return "\u{20d7}";
566
+ case "\u{20e1}":
567
+ case "↔":
568
+ case "⟷":
569
+ return "\u{20e1}";
570
+ case "\u{20d0}":
571
+ case "↼":
572
+ return "\u{20d0}";
573
+ case "\u{20d1}":
574
+ case "⇀":
575
+ return "\u{20d1}";
576
+ default:
577
+ return c;
578
+ }
843
579
  }
844
580
 
845
581
  function accent_f() {
846
- const l = [
847
- "grave",
848
- "acute",
849
- "acute.double",
850
- "hat",
851
- "tilde",
852
- "macron",
853
- "breve",
854
- "dot",
855
- "dot.double",
856
- "dot.triple",
857
- "dot.quad",
858
- "diaer",
859
- "circle",
860
- "arrow",
861
- "arrow.r",
862
- "arrow.l",
863
- "arrow.l.r",
864
- "caron",
865
- "harpoon",
866
- "harpoon.rt",
867
- "harpoon.lt",
868
- ];
869
- for (let i of l) {
870
- f[i] = (attr: tree[], dic, e) => {
871
- let s = f.accent([attr[0], [{ type: "f", value: i }]], {}, e);
872
- return s;
873
- };
874
- }
582
+ const l = [
583
+ "grave",
584
+ "acute",
585
+ "acute.double",
586
+ "hat",
587
+ "tilde",
588
+ "macron",
589
+ "breve",
590
+ "dot",
591
+ "dot.double",
592
+ "dot.triple",
593
+ "dot.quad",
594
+ "diaer",
595
+ "circle",
596
+ "arrow",
597
+ "arrow.r",
598
+ "arrow.l",
599
+ "arrow.l.r",
600
+ "caron",
601
+ "harpoon",
602
+ "harpoon.rt",
603
+ "harpoon.lt",
604
+ ];
605
+ for (const i of l) {
606
+ f[i] = (attr: tree[], _dic, e) => {
607
+ const s = f.accent([attr[0], [{ type: "f", value: i }]], {}, e);
608
+ return s;
609
+ };
610
+ }
875
611
  }
876
612
  accent_f();
877
613
 
878
614
  function lr_f() {
879
- let l: { name: string; l: tree[0]; r: tree[0] }[] = [
880
- { name: "abs", l: v_f("|"), r: v_f("|") },
881
- { name: "norm", l: v_f("‖"), r: v_f("‖") },
882
- { name: "floor", l: v_f("⌊"), r: v_f("⌋") },
883
- { name: "ceil", l: v_f("⌈"), r: v_f("⌉") },
884
- { name: "round", l: v_f("⌊"), r: v_f("⌉") },
885
- ];
886
- for (let i of l) {
887
- f[i.name] = (attr: tree[], dic, e) => {
888
- let s = f.lr([[i.l, ...attr[0], i.r]], dic, e);
889
- return s;
890
- };
891
- }
615
+ const l: { name: string; l: tree[0]; r: tree[0] }[] = [
616
+ { name: "abs", l: v_f("|"), r: v_f("|") },
617
+ { name: "norm", l: v_f("‖"), r: v_f("‖") },
618
+ { name: "floor", l: v_f("⌊"), r: v_f("⌋") },
619
+ { name: "ceil", l: v_f("⌈"), r: v_f("⌉") },
620
+ { name: "round", l: v_f("⌊"), r: v_f("⌉") },
621
+ ];
622
+ for (const i of l) {
623
+ f[i.name] = (attr: tree[], dic, e) => {
624
+ const s = f.lr([[i.l, ...attr[0], i.r]], dic, e);
625
+ return s;
626
+ };
627
+ }
892
628
  }
893
629
  lr_f();
894
630
 
895
- function underover_f(type: "under" | "over", tree: tree, x: string, str: tree, e: fonts) {
896
- let m =
897
- type === "under"
898
- ? createMath("munder", null, { accentunder: "true" })
899
- : createMath("mover", null, { accent: "true" });
900
- let base = createMath("mrow");
901
- base.append(render(tree, e));
902
- if (str) {
903
- let s = createMath("mrow");
904
- s.append(render(str, e));
905
- let mm = type === "under" ? createMath("munder") : createMath("mover");
906
- let xx = createMath("mo", x);
907
- mm.append(xx, s);
908
- m.append(base, mm);
909
- } else {
910
- let xx = createMath("mo", x);
911
- m.append(base, xx);
912
- }
913
- return m;
631
+ function underover_f(
632
+ type: "under" | "over",
633
+ tree: tree,
634
+ x: string,
635
+ str: tree,
636
+ e: fonts,
637
+ ) {
638
+ const m =
639
+ type === "under"
640
+ ? createMath("munder", null, { accentunder: "true" })
641
+ : createMath("mover", null, { accent: "true" });
642
+ const base = createMath("mrow");
643
+ base.append(render(tree, e));
644
+ if (str) {
645
+ const s = createMath("mrow");
646
+ s.append(render(str, e));
647
+ const mm = type === "under" ? createMath("munder") : createMath("mover");
648
+ const xx = createMath("mo", x);
649
+ mm.append(xx, s);
650
+ m.append(base, mm);
651
+ } else {
652
+ const xx = createMath("mo", x);
653
+ m.append(base, xx);
654
+ }
655
+ return m;
914
656
  }
915
657
 
916
658
  function underover_line_f(type: "under" | "over", tree: tree, e: fonts) {
917
- let m =
918
- type === "under"
919
- ? createMath("munder", null, { accentunder: "true" })
920
- : createMath("mover", null, { accent: "true" });
921
- let base = createMath("mrow");
922
- base.append(render(tree, e));
923
- if (type === "under") base.style.borderBottom = "1px solid black";
924
- if (type === "over") base.style.borderTop = "1px solid black";
925
- m.append(base);
926
- return m;
659
+ const m =
660
+ type === "under"
661
+ ? createMath("munder", null, { accentunder: "true" })
662
+ : createMath("mover", null, { accent: "true" });
663
+ const base = createMath("mrow");
664
+ base.append(render(tree, e));
665
+ if (type === "under") base.style.borderBottom = "1px solid black";
666
+ if (type === "over") base.style.borderTop = "1px solid black";
667
+ m.append(base);
668
+ return m;
927
669
  }
928
670
 
929
671
  const ff: typeof f = {
930
- "√": f["sqrt"],
931
- "∛": (attr, _, e) => f.root([[v_f("3")], attr[0]], null, e),
932
- "∜": (attr, _, e) => f.root([[v_f("4")], attr[0]], null, e),
672
+ "√": f.sqrt,
673
+ "∛": (attr, _, e) => f.root([[v_f("3")], attr[0]], null, e),
674
+ "∜": (attr, _, e) => f.root([[v_f("4")], attr[0]], null, e),
933
675
  };
934
676
 
935
677
  function kh(tree: tree) {
936
- let f = createMath("mrow");
937
- let l = createMath("mo");
938
- l.textContent = "(";
939
- let c = render(tree);
940
- let r = createMath("mo");
941
- r.textContent = ")";
942
- f.append(l, c, r);
943
- return f;
944
- }
945
-
946
- function out_kh(x: tree[0]) {
947
- if (x.type === "group") {
948
- if (x.kh === "()") return x.children;
949
- else return [x];
950
- } else {
951
- return [x];
952
- }
953
- }
954
-
955
- function transfer_kh(list: tree) {
956
- if (list.length === 1 && list[0].type === "group")
957
- list = [v_f(list[0].kh[0]), ...list[0].children, v_f(list[0].kh[1])];
958
- return list;
959
- }
960
-
961
- function in_kh(x: tree) {
962
- let k: tree = [{ type: "group", value: "", children: x, kh: "()" }];
963
- return k;
964
- }
965
-
966
- function is_sup(x: tree[0]) {
967
- return eqq(x, { type: "v", value: "^" });
968
- }
969
-
970
- function is_sub(x: tree[0]) {
971
- return eqq(x, { type: "v", value: "_" });
972
- }
973
-
974
- function is_frac(x: tree[0]) {
975
- return eqq(x, { type: "v", value: "/" });
976
- }
977
-
978
- function is_br(x: tree[0]) {
979
- return x && x.value == "br" && x.esc;
980
- }
981
-
982
- function is_limit(tree: tree) {
983
- if (tree.length == 1) {
984
- let x = tree[0];
985
- if (x.type == "f") {
986
- if (limits_f.includes(x.value)) {
987
- return true;
988
- }
989
- if (x.value.split(".").at(0) === "arrow") return true;
990
- for (let i of opl) {
991
- if (i.limits && x.value == i.id) {
992
- return true;
993
- }
994
- }
995
- if (x.value === "scripts") return false;
996
- if (x.value === "limits") return true;
997
- if (x.value === "op") {
998
- let { dic } = f_attr(x);
999
- return is_true(dic.limits);
1000
- }
1001
- }
1002
- if (x.type == "v" && limits_sy.includes(x.value)) {
1003
- return true;
1004
- }
1005
- } else {
1006
- return false;
1007
- }
1008
- }
1009
-
1010
- function is_f_mark(x: tree[0], str: string) {
1011
- return x.value == str && x.type == "v" && !x.esc;
1012
- }
1013
-
1014
- function is_dot(x: tree[0]) {
1015
- return eq(x, { type: "v", value: "." });
1016
- }
1017
-
1018
- function is_dot_f(x: tree[0]) {
1019
- return x && (x.type == "f" || (x.type == "v" && x.value.match(/[a-z]/)));
1020
- }
1021
-
1022
- function is_factorial(x: tree[0]) {
1023
- return x && x.type == "v" && x.value == "!" && !x.esc;
1024
- }
1025
-
1026
- let dh: tree[0] = { type: "v", value: "," };
1027
-
1028
- function v_f(str: string): tree[0] {
1029
- return { type: "v", value: str };
1030
- }
1031
-
1032
- function eq(x0: tree[0], x1: tree[0]) {
1033
- if (!x0 || !x1) return false;
1034
- return x0.type === x1.type && x0.value === x1.value;
1035
- }
1036
-
1037
- function eqq(x0: tree[0], x1: tree[0]) {
1038
- if (!x0 || !x1) return false;
1039
- return x0.type === x1.type && x0.value === x1.value && x0?.esc === x1?.esc;
1040
- }
1041
-
1042
- function is_type(x: tree[0], ...type: vtype[]) {
1043
- return type.includes(x?.type);
1044
- }
1045
-
1046
- function trim(tree: tree) {
1047
- if (!tree || !tree[0]) return [];
1048
- let start = 0;
1049
- let end = tree.length;
1050
- if (tree[0].type === "blank") start = 1;
1051
- if (tree.at(-1).type === "blank") end--;
1052
- return tree.slice(start, end);
1053
- }
1054
-
1055
- type fdic = { [id: string]: tree };
1056
-
1057
- function dic_to_ast(dic: { [id: string]: tree }) {
1058
- let l: tree = [];
1059
- for (let i in dic) {
1060
- l.push({ type: "f", value: i });
1061
- l.push({ type: "v", value: ":" });
1062
- for (let x of dic[i]) {
1063
- if (eq(x, dh)) x.esc = true;
1064
- }
1065
- l.push(...dic[i]);
1066
- if (Number(i) + 1 != Object.keys(dic).length) {
1067
- l.push(dh);
1068
- }
1069
- }
1070
- return l;
678
+ const f = createMath("mrow");
679
+ const l = createMath("mo");
680
+ l.textContent = "(";
681
+ const c = render(tree);
682
+ const r = createMath("mo");
683
+ r.textContent = ")";
684
+ f.append(l, c, r);
685
+ return f;
1071
686
  }
1072
687
 
1073
688
  function lan_dic(tree: tree) {
1074
- tree = trim(tree);
1075
- const o = new Object();
1076
- let key = "";
1077
- let value: tree = [];
1078
- tree.push(dh);
1079
- for (let i = 0; i < tree.length; i++) {
1080
- if (!key && tree[i].type != "blank") {
1081
- key = tree[i].value;
1082
- } else {
1083
- if (value.length) {
1084
- if (eqq(tree[i], dh)) {
1085
- o[key] = trim(value);
1086
- key = "";
1087
- value = [];
1088
- } else value.push(tree[i]);
1089
- } else {
1090
- if (eqq(tree[i], v_f(":"))) {
1091
- value.push(tree[i + 1]);
1092
- }
1093
- }
1094
- }
1095
- }
1096
- return o;
689
+ tree = trim(tree);
690
+ const o = new Object();
691
+ let key = "";
692
+ let value: tree = [];
693
+ tree.push(dh);
694
+ for (let i = 0; i < tree.length; i++) {
695
+ if (!key && tree[i].type !== "blank") {
696
+ key = tree[i].value;
697
+ } else {
698
+ if (value.length) {
699
+ if (eqq(tree[i], dh)) {
700
+ o[key] = trim(value);
701
+ key = "";
702
+ value = [];
703
+ } else value.push(tree[i]);
704
+ } else {
705
+ if (eqq(tree[i], v_f(":"))) {
706
+ value.push(tree[i + 1]);
707
+ }
708
+ }
709
+ }
710
+ }
711
+ return o;
1097
712
  }
1098
713
 
1099
714
  function get_value(dic: fdic, o: string) {
1100
- if (!dic?.[o]?.[0]) return undefined;
1101
- const x = dic[o][0];
1102
- if (x.type === "sharp") {
1103
- if (x.value === "true") return true;
1104
- if (x.value === "false") return false;
1105
- if (x.value === "none") return null;
1106
- if (x.children) {
1107
- if (!x.children.find((v) => eqq(v, v_f(":")))) return x.children.map((v) => v.value).join("");
1108
- return lan_dic(x.children);
1109
- }
1110
- }
1111
- return dic[o].map((i) => i.value).join("");
1112
- }
1113
-
1114
- function is_true(t: tree) {
1115
- return eqq(trim(t)?.[0], { type: "sharp", value: "true" });
1116
- }
1117
-
1118
- function ast2(tree: tree) {
1119
- // 处理数字和小数
1120
- {
1121
- let t: tree = [];
1122
- let num = /[0-9]/;
1123
- let number = "";
1124
- for (let n = 0; n < tree.length; n++) {
1125
- let x = tree[n];
1126
- if (x.value.match(num) || (tree[n - 1]?.value.match(num) && is_dot(x) && tree[n + 1]?.value.match(num))) {
1127
- number += x.value;
1128
- } else {
1129
- t.push(x);
1130
- }
1131
- if (
1132
- x.value.match(num) &&
1133
- (!tree[n + 1] || !tree[n + 1].value.match(num)) &&
1134
- !(is_dot(tree[n + 1]) && tree[n + 2]?.value.match(num))
1135
- ) {
1136
- t.push({ type: "v", value: number });
1137
- number = "";
1138
- }
1139
- }
1140
- tree = t;
1141
- }
1142
-
1143
- // 处理\转义
1144
- {
1145
- let t: tree = [];
1146
- for (let n = 0; n < tree.length; n++) {
1147
- let x = tree[n];
1148
-
1149
- if (eqq(x, v_f("\\"))) {
1150
- if (tree?.[n + 1]) {
1151
- let next = tree[n + 1];
1152
- if (eq(next, v_f("\\"))) {
1153
- t.push({ type: "v", value: "\\", esc: true });
1154
- n++;
1155
- } else if (next.type === "blank") {
1156
- t.push({ type: "v", value: "br", esc: true });
1157
- n++;
1158
- } else if (next.type === "f") {
1159
- let v = next.value;
1160
- t.push({ type: "v", value: v[0] });
1161
- t.push({ type: v.length == 2 ? "v" : "f", value: v.slice(1) });
1162
- n++;
1163
- } else if (next.type === "sharp") {
1164
- t.push({ type: "v", value: "#" });
1165
- n++;
1166
- } else {
1167
- let v = next;
1168
- v.esc = true;
1169
- t.push(v);
1170
- n++;
1171
- }
1172
- }
1173
- } else {
1174
- t.push(x);
1175
- }
1176
- }
1177
- tree = t;
1178
- }
1179
-
1180
- return tree;
1181
- }
1182
-
1183
- function ast3(tree: tree) {
1184
- // 处理符号简写(shorthand)
1185
- {
1186
- let t: tree = [];
1187
- for (let n = 0; n < tree.length; n++) {
1188
- let x = tree[n];
1189
-
1190
- if (!x.esc) {
1191
- if (x.type == "v") {
1192
- let nn = n;
1193
- let shortkey = x.value;
1194
- let l = [];
1195
- if (shorthand[shortkey]) l.push(shortkey);
1196
- // 连起来的字符
1197
- while (tree?.[nn + 1]?.type == "v" && shortkey.length <= max_shorthand_len) {
1198
- shortkey += tree[nn + 1].value;
1199
- if (shorthand[shortkey]) l.push(shortkey);
1200
- nn++;
1201
- }
1202
- if (l.length) {
1203
- let nx: tree[0] = { type: "f", value: shorthand[l.at(-1)], src: l.at(-1) };
1204
- t.push(nx);
1205
-
1206
- // 不处理已经后面连起来的字符
1207
- n += l.at(-1).length - 1;
1208
- } else {
1209
- t.push(x);
1210
- }
1211
- } else {
1212
- t.push(x);
1213
- }
1214
- } else {
1215
- t.push(x);
1216
- }
1217
- }
1218
- tree = t;
1219
- }
1220
-
1221
- // 处理字符与v之间空格
1222
- {
1223
- let t: tree = [];
1224
- for (let n = 0; n < tree.length; n++) {
1225
- let x = tree[n];
1226
-
1227
- if (x.type == "v" && tree?.[n + 1]?.type == "blank" && tree?.[n + 2]?.type == "str") {
1228
- t.push(x);
1229
- t.push({ type: "f", value: "space" });
1230
- } else if (x.type == "str" && tree?.[n + 1]?.type == "blank" && tree?.[n + 2]?.type == "v") {
1231
- t.push(x);
1232
- t.push({ type: "f", value: "space" });
1233
- } else {
1234
- t.push(x);
1235
- }
1236
- }
1237
- tree = t;
1238
- }
1239
-
1240
- // 处理!
1241
- {
1242
- let t: tree = [];
1243
- let tmpx: tree = [];
1244
- for (let n = 0; n < tree.length; n++) {
1245
- let x = tree[n];
1246
-
1247
- if (is_factorial(tree?.[n + 1]) && !is_sup(x) && !is_sub(x) && !is_frac(x)) {
1248
- tmpx.push(x);
1249
- } else {
1250
- if (tmpx.length) {
1251
- tmpx.push(x);
1252
- t.push({ type: "group1", value: "", children: tmpx });
1253
- tmpx = [];
1254
- } else {
1255
- t.push(x);
1256
- }
1257
- }
1258
- }
1259
-
1260
- tree = t;
1261
- }
1262
-
1263
- // 处理带.的f
1264
- {
1265
- let t: tree = [];
1266
- let f = "";
1267
- for (let n = 0; n < tree.length; n++) {
1268
- let x = tree[n];
1269
-
1270
- if (is_dot_f(x) && is_dot(tree[n + 1])) {
1271
- if (f || (!f && x.type == "f")) {
1272
- f += x.value + ".";
1273
- n++;
1274
- continue;
1275
- } else {
1276
- t.push(x);
1277
- }
1278
- } else if (f && is_dot_f(x)) {
1279
- f += x.value;
1280
- if (!is_dot(tree[n + 1])) {
1281
- t.push({ type: "f", value: f });
1282
- f = "";
1283
- }
1284
- } else {
1285
- t.push(x);
1286
- }
1287
- }
1288
- tree = t;
1289
- }
1290
- // 处理后面直接跟参数的f
1291
- {
1292
- const t: tree = [];
1293
- for (let n = 0; n < tree.length; n++) {
1294
- const x = tree[n];
1295
- if (x.type === "v" && ff[x.value] && tree[n + 1]) {
1296
- x.type = "f";
1297
- if (!(is_type(tree[n + 1], "group") && tree[n + 1].kh === "()")) {
1298
- t.push(x);
1299
- t.push({ type: "group", children: [tree[n + 1]], value: "", kh: "()" });
1300
- n++;
1301
- continue;
1302
- }
1303
- }
1304
- t.push(x);
1305
- }
1306
- tree = t;
1307
- }
1308
- // 处理f
1309
- {
1310
- let t: tree = [];
1311
- for (let n = 0; n < tree.length; n++) {
1312
- let x = tree[n];
1313
- const next = tree[n + 1];
1314
-
1315
- // 带有括号(参数)的函数
1316
- if (x.type === "f" && next && next.type === "group") {
1317
- if (next.kh === "()") {
1318
- x.children = tree[n + 1].children;
1319
- t.push(x);
1320
-
1321
- // 不处理group
1322
- n++;
1323
- continue;
1324
- } else {
1325
- if (next.kh[0] === "(") {
1326
- // 向后找
1327
- const tt: tree = [];
1328
- tt.push(...next.children);
1329
- tt.push(v_f(next.kh[1]));
1330
- for (let i = n + 2; i < tree.length; i++) {
1331
- if (tree[i]) {
1332
- if (eqq(tree[i], v_f(")"))) {
1333
- n = i;
1334
- break;
1335
- } else {
1336
- tt.push(tree[i]);
1337
- }
1338
- }
1339
- }
1340
- x.children = tt;
1341
- t.push(x);
1342
- } else {
1343
- t.push(x);
1344
- }
1345
- }
1346
- for (let i in x.children || []) {
1347
- if (x.children[i].value === "\\" && x.children[i].type === "v") {
1348
- if (x.children[Number(i) + 1]) {
1349
- x.children[Number(i) + 1]["esc"] = true;
1350
- }
1351
- }
1352
- }
1353
- } else {
1354
- t.push(x);
1355
- }
1356
- }
1357
- tree = t;
1358
- }
1359
-
1360
- // sin[]/2=(sin[])/2 a_i()=a_(i()) 之类的
1361
- {
1362
- const t: tree = [];
1363
- const un_list_str = [
1364
- "`",
1365
- "~",
1366
- "!",
1367
- "@",
1368
- "%",
1369
- "*",
1370
- "(",
1371
- ")",
1372
- "-",
1373
- "+",
1374
- "=",
1375
- "[",
1376
- "]",
1377
- "{",
1378
- "}",
1379
- "|",
1380
- ":",
1381
- ";",
1382
- "<",
1383
- ",",
1384
- ">",
1385
- ".",
1386
- "?",
1387
- ]; // typst 似乎是直接排除了这些字符,不排除转义,shorthand之类的
1388
- for (let n = 0; n < tree.length; n++) {
1389
- const x = tree[n];
1390
- const next = tree[n + 1];
1391
- if (
1392
- is_type(next, "group") &&
1393
- next.kh != "()" &&
1394
- is_type(x, "f", "str", "v") &&
1395
- !x.value.match(/[0-9]/) &&
1396
- !is_sub(x) &&
1397
- !is_sup(x) &&
1398
- (!(
1399
- (is_type(x, "f") && un_list_str.includes(x.src)) ||
1400
- (is_type(x, "v") && un_list_str.includes(x.value))
1401
- ) ||
1402
- x.esc)
1403
- ) {
1404
- t.push({ type: "group1", value: "", children: [x, next] });
1405
- n++;
1406
- continue;
1407
- }
1408
- t.push(x);
1409
- }
1410
- tree = t;
1411
- }
1412
-
1413
- // 处理#
1414
- {
1415
- const t: tree = [];
1416
- for (let n = 0; n < tree.length; n++) {
1417
- const x = tree[n];
1418
- if (x.type === "sharp") {
1419
- if (!x.value)
1420
- if (tree[n + 1]?.type === "group") {
1421
- x.children = tree[n + 1].children;
1422
- for (let i in x.children) {
1423
- if (x.children[i].value === "\\" && x.children[i].type === "v") {
1424
- if (x.children[Number(i) + 1]) {
1425
- x.children[Number(i) + 1]["esc"] = true;
1426
- }
1427
- }
1428
- }
1429
- const v = x.children.map((i) => i.value).join("");
1430
- if (v.match(/^[0-9\+\-\*\/()]+$/)) {
1431
- x.value = v; // todo 数学运算
1432
- } else {
1433
- x.value = v;
1434
- }
1435
- } else {
1436
- console.log("+1", tree[n + 1]);
1437
-
1438
- x.value = tree[n + 1]?.value;
1439
- if (tree[n + 2]?.type === "f") {
1440
- x.value += tree[n + 2].value;
1441
- n++;
1442
- }
1443
- }
1444
- t.push(x);
1445
- n++;
1446
- continue;
1447
- } else {
1448
- t.push(x);
1449
- }
1450
- }
1451
- tree = t;
1452
- }
1453
-
1454
- // '->^'
1455
- {
1456
- let t: tree = [];
1457
- for (let n = 0; n < tree.length; n++) {
1458
- const next = tree[n + 1];
1459
- const x = tree[n];
1460
- let nn = n + 1;
1461
- if (eqq(next, { type: "f", value: "prime" })) {
1462
- while (tree[nn] && eqq(tree[nn], { type: "f", value: "prime" })) nn++;
1463
- const primes = nn - (n + 1);
1464
- if (!is_sup(x) && !is_sub(x) && x.type != "blank") {
1465
- t.push({
1466
- type: "f",
1467
- value: "attach",
1468
- children: [
1469
- x,
1470
- dh,
1471
- ...dic_to_ast({
1472
- tr: [{ type: "v", value: "'".repeat(primes) }],
1473
- }),
1474
- ],
1475
- });
1476
- } else {
1477
- t.push(x);
1478
- for (let i = 0; i < primes; i++) t.push({ type: "f", value: "prime" });
1479
- }
1480
- n += primes;
1481
- continue;
1482
- }
1483
- t.push(x);
1484
- }
1485
- tree = t;
1486
- }
1487
-
1488
- // 移除blank
1489
- {
1490
- let t: tree = [];
1491
- for (let i of tree) {
1492
- if (i.type == "blank") continue;
1493
- t.push(i);
1494
- }
1495
- tree = t;
1496
- }
1497
-
1498
- // 处理^_
1499
- {
1500
- // 获取^_嵌套索引
1501
- let index: [number, number][] = [];
1502
- let start = NaN;
1503
- for (let n = 0; n < tree.length; n++) {
1504
- const x = tree[n];
1505
- if (!is_sup(x) && !is_sub(x)) {
1506
- if ((is_sup(tree[n + 1]) || is_sub(tree[n + 1])) && !(is_sup(tree[n - 1]) || is_sub(tree[n - 1]))) {
1507
- if (!start) start = n;
1508
- }
1509
- if ((is_sup(tree[n - 1]) || is_sub(tree[n - 1])) && !(is_sup(tree[n + 1]) || is_sub(tree[n + 1]))) {
1510
- index.push([start, n]);
1511
- start = NaN;
1512
- }
1513
- }
1514
- }
1515
-
1516
- let t: tree = [];
1517
- for (let n = 0; n < tree.length; n++) {
1518
- if (n == index?.[0]?.[0]) {
1519
- {
1520
- let tmp: tree[0] = tree[index[0][1]];
1521
- for (let i = index[0][1]; i >= index[0][0]; i--) {
1522
- if (is_sup(tree[i - 1]) && is_sub(tree[i - 3])) {
1523
- let o = is_limit([tree[i - 4]])
1524
- ? { t: out_kh(tmp), b: out_kh(tree[i - 2]) }
1525
- : { tr: out_kh(tmp), br: out_kh(tree[i - 2]) };
1526
- tmp = {
1527
- type: "f",
1528
- value: "attach",
1529
- children: [tree[i - 4], dh, ...dic_to_ast(o)],
1530
- };
1531
- i -= 4 - 1;
1532
- continue;
1533
- }
1534
- if (is_sub(tree[i - 1]) && is_sup(tree[i - 3])) {
1535
- let o = is_limit([tree[i - 4]])
1536
- ? { t: out_kh(tree[i - 2]), b: out_kh(tmp) }
1537
- : { tr: out_kh(tree[i - 2]), br: out_kh(tmp) };
1538
- tmp = {
1539
- type: "f",
1540
- value: "attach",
1541
- children: [tree[i - 4], dh, ...dic_to_ast(o)],
1542
- };
1543
- i -= 4 - 1;
1544
- continue;
1545
- }
1546
- if (is_sup(tree[i - 1])) {
1547
- let o = is_limit([tree[i - 2]]) ? { t: out_kh(tmp) } : { tr: out_kh(tmp) };
1548
- tmp = {
1549
- type: "f",
1550
- value: "attach",
1551
- children: [tree[i - 2], dh, ...dic_to_ast(o)],
1552
- };
1553
- i -= 2 - 1;
1554
- continue;
1555
- }
1556
- if (is_sub(tree[i - 1])) {
1557
- let o = is_limit([tree[i - 2]]) ? { b: out_kh(tmp) } : { br: out_kh(tmp) };
1558
- tmp = {
1559
- type: "f",
1560
- value: "attach",
1561
- children: [tree[i - 2], dh, ...dic_to_ast(o)],
1562
- };
1563
- i -= 2 - 1;
1564
- continue;
1565
- }
1566
- }
1567
- t.push(tmp);
1568
- }
1569
- n += index[0][1] - index[0][0];
1570
- index = index.slice(1);
1571
- continue;
1572
- }
1573
-
1574
- t.push(tree[n]);
1575
- }
1576
- tree = t;
1577
- }
1578
-
1579
- // 处理/
1580
- {
1581
- // 获取/嵌套索引
1582
- let index: [number, number][] = [];
1583
- let start = NaN;
1584
- for (let n = 0; n < tree.length; n++) {
1585
- const x = tree[n];
1586
- if (!is_frac(x)) {
1587
- if (is_frac(tree[n + 1]) && !is_frac(tree[n - 1])) {
1588
- if (!start) start = n;
1589
- }
1590
- if (is_frac(tree[n - 1]) && !is_frac(tree[n + 1])) {
1591
- index.push([start, n]);
1592
- start = NaN;
1593
- }
1594
- }
1595
- }
1596
-
1597
- let t: tree = [];
1598
- for (let n = 0; n < tree.length; n++) {
1599
- if (n == index?.[0]?.[0]) {
1600
- {
1601
- let tmp: tree[0] = tree[index[0][0]];
1602
- for (let i = index[0][0]; i <= index[0][1]; i++) {
1603
- if (is_frac(tree[i + 1])) {
1604
- tmp = {
1605
- type: "f",
1606
- value: "frac",
1607
- children: [...out_kh(tmp), dh, ...out_kh(tree[i + 2])],
1608
- };
1609
- i += 2 - 1;
1610
- continue;
1611
- }
1612
- }
1613
- console.log(1, tmp);
1614
-
1615
- t.push(tmp);
1616
- }
1617
- n += index[0][1] - index[0][0];
1618
- index.splice(0, 1);
1619
- continue;
1620
- }
1621
-
1622
- t.push(tree[n]);
1623
- }
1624
- tree = t;
1625
- }
1626
- return tree;
1627
- }
1628
-
1629
- function f_attr(x: tree[0]) {
1630
- const list: (tree | "," | ";")[] = [[]];
1631
- for (let i of x.children) {
1632
- if (eqq(i, { type: "v", value: "," }) || eqq(i, { type: "v", value: ";" })) {
1633
- list.push(i.value as "," | ";");
1634
- list.push([]);
1635
- } else {
1636
- (list.at(-1) as tree).push(i);
1637
- }
1638
- }
1639
- const dicl: tree[] = [];
1640
- const xattr: typeof list = [];
1641
- for (let x of list) {
1642
- if (typeof x === "string") {
1643
- xattr.push(x);
1644
- } else {
1645
- if (x.find((v, i) => is_type(x[i - 1], "f", "v") && eqq(v, { type: "v", value: ":" }))) {
1646
- dicl.push(x);
1647
- } else {
1648
- xattr.push(x);
1649
- }
1650
- }
1651
- }
1652
-
1653
- let nl: typeof xattr = [];
1654
- for (let i = 0; i < xattr.length; i++) {
1655
- const n = xattr[i],
1656
- next = xattr[i + 1];
1657
- if ((n === "," && next === ";") || (n === ";" && next === ",")) {
1658
- nl.push(";");
1659
- i++;
1660
- } else {
1661
- nl.push(xattr[i]);
1662
- }
1663
- }
1664
-
1665
- let attr: tree[] | tree[][];
1666
- if (typeof nl.at(-1) === "string") nl = nl.slice(0, -1);
1667
- const has_fenhao = nl.includes(";");
1668
- if (has_fenhao) {
1669
- let x = array_split(nl, (x) => x === ";");
1670
- if (x.at(-1)[0].length === 0) x = x.slice(0, -1);
1671
- attr = x.map((x) => x.filter((i) => typeof i != "string")) as tree[][];
1672
- } else {
1673
- attr = (nl.filter((i) => typeof i != "string") as tree[]).map((i) => trim(i));
1674
- }
1675
-
1676
- // 将dicl的tree转为键对
1677
- let dic: fdic = {};
1678
- for (let i of dicl) {
1679
- let x = false;
1680
- let n = "";
1681
- let t: tree = [];
1682
- for (let el of i) {
1683
- if (eqq(el, { type: "v", value: ":" })) {
1684
- x = true;
1685
- continue;
1686
- }
1687
- if (!x) {
1688
- n += el.value;
1689
- } else {
1690
- t.push(el);
1691
- }
1692
- }
1693
- dic[n] = ast3(ast2(t));
1694
- }
1695
- return { attr, dic };
1696
- }
1697
-
1698
- function array_split<i>(list: i[], f: (i: i) => boolean) {
1699
- const l: i[][] = [[]];
1700
- for (let x of list) {
1701
- if (f(x)) {
1702
- l.push([]);
1703
- } else {
1704
- l.at(-1).push(x);
1705
- }
1706
- }
1707
- return l;
715
+ if (!dic?.[o]?.[0]) return undefined;
716
+ const x = dic[o][0];
717
+ if (x.type === "sharp") {
718
+ if (x.value === "true") return true;
719
+ if (x.value === "false") return false;
720
+ if (x.value === "none") return null;
721
+ if (x.children) {
722
+ if (!x.children.find((v) => eqq(v, v_f(":"))))
723
+ return x.children.map((v) => v.value).join("");
724
+ return lan_dic(x.children);
725
+ }
726
+ }
727
+ return dic[o].map((i) => i.value).join("");
1708
728
  }
1709
729
 
1710
730
  function attr_join(attr: tree[]) {
1711
- const t: tree = [];
1712
- for (let i of attr) {
1713
- t.push(...i, v_f(","));
1714
- }
1715
- return t.slice(0, -1);
731
+ const t: tree = [];
732
+ for (const i of attr) {
733
+ t.push(...i, v_f(","));
734
+ }
735
+ return t.slice(0, -1);
1716
736
  }
1717
737
 
1718
738
  type fonts = "serif" | "sans" | "frak" | "mono" | "bb" | "cal";
1719
739
  function font(str: string, type: fonts = "serif") {
1720
- function index_c(c: string) {
1721
- const l = "abcdefghijklmnopqrstuvwxyz";
1722
- return l.indexOf(c.toLowerCase());
1723
- }
1724
- const map: { [font in "sans" | "frak" | "mono" | "bb" | "cal"]: { num?: string; up: string; low: string } } = {
1725
- sans: {
1726
- num: "𝟢𝟣𝟤𝟥𝟦𝟧𝟨𝟩𝟪𝟫",
1727
- low: "𝖺𝖻𝖼𝖽𝖾𝖿𝗀𝗁𝗂𝗃𝗄𝗅𝗆𝗇𝗈𝗉𝗊𝗋𝗌𝗍𝗎𝗏𝗐𝗑𝗒𝗓",
1728
- up: "𝖠𝖡𝖢𝖣𝖤𝖥𝖦𝖧𝖨𝖩𝖪𝖫𝖬𝖭𝖮𝖯𝖰𝖱𝖲𝖳𝖴𝖵𝖶𝖷𝖸𝖹",
1729
- },
1730
- frak: {
1731
- low: "𝔞𝔟𝔠𝔡𝔢𝔣𝔤𝔥𝔦𝔧𝔨𝔩𝔪𝔫𝔬𝔭𝔮𝔯𝔰𝔱𝔲𝔳𝔴𝔵𝔶𝔷",
1732
- up: "𝔄𝔅ℭ𝔇𝔈𝔉𝔊ℌℑ𝔍𝔎𝔏𝔐𝔑𝔒𝔓𝔔ℜ𝔖𝔗𝔘𝔙𝔚𝔛𝔜ℨ",
1733
- },
1734
- mono: {
1735
- num: "𝟶𝟷𝟸𝟹𝟺𝟻𝟼𝟽𝟾𝟿",
1736
- low: "𝚊𝚋𝚌𝚍𝚎𝚏𝚐𝚑𝚒𝚓𝚔𝚕𝚖𝚗𝚘𝚙𝚚𝚛𝚜𝚝𝚞𝚟𝚠𝚡𝚢𝚣",
1737
- up: "𝙰𝙱𝙲𝙳𝙴𝙵𝙶𝙷𝙸𝙹𝙺𝙻𝙼𝙽𝙾𝙿𝚀𝚁𝚂𝚃𝚄𝚅𝚆𝚇𝚈𝚉",
1738
- },
1739
- bb: {
1740
- num: "𝟘𝟙𝟚𝟛𝟜𝟝𝟞𝟟𝟠𝟡",
1741
- low: "𝕒𝕓𝕔𝕕𝕖𝕗𝕘𝕙𝕚𝕛𝕜𝕝𝕞𝕟𝕠𝕡𝕢𝕣𝕤𝕥𝕦𝕧𝕨𝕩𝕪𝕫",
1742
- up: "𝔸𝔹ℂ𝔻𝔼𝔽𝔾ℍ𝕀𝕁𝕂𝕃𝕄ℕ𝕆ℙℚℝ𝕊𝕋𝕌𝕍𝕎𝕏𝕐ℤ",
1743
- },
1744
- cal: {
1745
- low: "𝒶𝒷𝒸𝒹ℯ𝒻ℊ𝒽𝒾𝒿𝓀𝓁𝓂𝓃ℴ𝓅𝓆𝓇𝓈𝓉𝓊𝓋𝓌𝓍𝓎𝓏",
1746
- up: "𝒜ℬ𝒞𝒟ℰℱ𝒢ℋℐ𝒥𝒦ℒℳ𝒩𝒪𝒫𝒬ℛ𝒮𝒯𝒰𝒱𝒲𝒳𝒴𝒵",
1747
- },
1748
- };
1749
- if (type === "serif") return str;
1750
- else {
1751
- str = str.replace(/[0-9]/g, (s) => [...map[type].num][s]);
1752
- str = str.replace(/[a-z]/g, (s) => [...map[type].low][index_c(s)]);
1753
- str = str.replace(/[A-Z]/g, (s) => [...map[type].up][index_c(s)]);
1754
- }
1755
- return str;
740
+ function index_c(c: string) {
741
+ const l = "abcdefghijklmnopqrstuvwxyz";
742
+ return l.indexOf(c.toLowerCase());
743
+ }
744
+ const map: {
745
+ [font in "sans" | "frak" | "mono" | "bb" | "cal"]: {
746
+ num?: string;
747
+ up: string;
748
+ low: string;
749
+ };
750
+ } = {
751
+ sans: {
752
+ num: "𝟢𝟣𝟤𝟥𝟦𝟧𝟨𝟩𝟪𝟫",
753
+ low: "𝖺𝖻𝖼𝖽𝖾𝖿𝗀𝗁𝗂𝗃𝗄𝗅𝗆𝗇𝗈𝗉𝗊𝗋𝗌𝗍𝗎𝗏𝗐𝗑𝗒𝗓",
754
+ up: "𝖠𝖡𝖢𝖣𝖤𝖥𝖦𝖧𝖨𝖩𝖪𝖫𝖬𝖭𝖮𝖯𝖰𝖱𝖲𝖳𝖴𝖵𝖶𝖷𝖸𝖹",
755
+ },
756
+ frak: {
757
+ low: "𝔞𝔟𝔠𝔡𝔢𝔣𝔤𝔥𝔦𝔧𝔨𝔩𝔪𝔫𝔬𝔭𝔮𝔯𝔰𝔱𝔲𝔳𝔴𝔵𝔶𝔷",
758
+ up: "𝔄𝔅ℭ𝔇𝔈𝔉𝔊ℌℑ𝔍𝔎𝔏𝔐𝔑𝔒𝔓𝔔ℜ𝔖𝔗𝔘𝔙𝔚𝔛𝔜ℨ",
759
+ },
760
+ mono: {
761
+ num: "𝟶𝟷𝟸𝟹𝟺𝟻𝟼𝟽𝟾𝟿",
762
+ low: "𝚊𝚋𝚌𝚍𝚎𝚏𝚐𝚑𝚒𝚓𝚔𝚕𝚖𝚗𝚘𝚙𝚚𝚛𝚜𝚝𝚞𝚟𝚠𝚡𝚢𝚣",
763
+ up: "𝙰𝙱𝙲𝙳𝙴𝙵𝙶𝙷𝙸𝙹𝙺𝙻𝙼𝙽𝙾𝙿𝚀𝚁𝚂𝚃𝚄𝚅𝚆𝚇𝚈𝚉",
764
+ },
765
+ bb: {
766
+ num: "𝟘𝟙𝟚𝟛𝟜𝟝𝟞𝟟𝟠𝟡",
767
+ low: "𝕒𝕓𝕔𝕕𝕖𝕗𝕘𝕙𝕚𝕛𝕜𝕝𝕞𝕟𝕠𝕡𝕢𝕣𝕤𝕥𝕦𝕧𝕨𝕩𝕪𝕫",
768
+ up: "𝔸𝔹ℂ𝔻𝔼𝔽𝔾ℍ𝕀𝕁𝕂𝕃𝕄ℕ𝕆ℙℚℝ𝕊𝕋𝕌𝕍𝕎𝕏𝕐ℤ",
769
+ },
770
+ cal: {
771
+ low: "𝒶𝒷𝒸𝒹ℯ𝒻ℊ𝒽𝒾𝒿𝓀𝓁𝓂𝓃ℴ𝓅𝓆𝓇𝓈𝓉𝓊𝓋𝓌𝓍𝓎𝓏",
772
+ up: "𝒜ℬ𝒞𝒟ℰℱ𝒢ℋℐ𝒥𝒦ℒℳ𝒩𝒪𝒫𝒬ℛ𝒮𝒯𝒰𝒱𝒲𝒳𝒴𝒵",
773
+ },
774
+ };
775
+ if (type === "serif") return str;
776
+ else {
777
+ str = str.replace(/[0-9]/g, (s) => [...map[type].num][s]);
778
+ str = str.replace(/[a-z]/g, (s) => [...map[type].low][index_c(s)]);
779
+ str = str.replace(/[A-Z]/g, (s) => [...map[type].up][index_c(s)]);
780
+ }
781
+ return str;
782
+ }
783
+
784
+ function render(tree: tree, e?: fonts): VEl | VFragment {
785
+ const fragment = createFragment();
786
+
787
+ tree = ast2(tree);
788
+
789
+ // 多行
790
+ // 处理\ 换行
791
+ {
792
+ let xx = false;
793
+ for (const n in tree) {
794
+ const i = tree[n];
795
+ if (is_br(i)) {
796
+ xx = true;
797
+ break;
798
+ }
799
+ }
800
+ if (xx) {
801
+ const trees: tree[] = [[]];
802
+ for (let n = 0; n < tree.length; n++) {
803
+ const i = tree[n];
804
+ if (is_br(i)) {
805
+ trees.push([]);
806
+ } else {
807
+ trees.at(-1).push(i);
808
+ }
809
+ }
810
+ return x_table(trees, undefined, !isDisplay());
811
+ }
812
+ }
813
+
814
+ // 单行
815
+
816
+ tree = ast3(tree);
817
+
818
+ {
819
+ const t: tree = [];
820
+ for (const x of tree) {
821
+ // 移除&
822
+ if (!(x.type === "v" && x.value === "&" && !x.esc)) {
823
+ t.push(x);
824
+ }
825
+ }
826
+ tree = t;
827
+ }
828
+
829
+ // 移除group1
830
+ {
831
+ const t: tree = [];
832
+ for (const x of tree) {
833
+ if (x.type === "group1") {
834
+ t.push(...x.children);
835
+ } else {
836
+ t.push(x);
837
+ }
838
+ }
839
+ tree = t;
840
+ }
841
+
842
+ for (const i in tree) {
843
+ const n = Number(i);
844
+ const x = tree[n];
845
+
846
+ // 函数
847
+ if (x.type === "f") {
848
+ // 带有括号(参数)的函数
849
+ if (x.children && (x.kh === "()" || !x.kh)) {
850
+ const { attr, dic } = f_attr(x);
851
+
852
+ if (f[x.value]) {
853
+ const el = f[x.value](attr as tree[], dic, e);
854
+ fragment.append(el);
855
+ } else if (ss[x.value]) {
856
+ const el = createMath("mi", ss[x.value]);
857
+ fragment.append(el, render(in_kh(x.children), e));
858
+ } else if (ff[x.value]) {
859
+ const el = ff[x.value](attr as tree[], dic, e);
860
+ fragment.append(el);
861
+ }
862
+ } else {
863
+ if (ss[x.value]) {
864
+ let tag: keyof MathMLElementTagNameMap;
865
+ let space_w = null;
866
+ const space_width = {
867
+ " ": "0.36em",
868
+ "\u2002": "0.5em",
869
+ "\u2003": "1em",
870
+ "\u2004": "0.333em",
871
+ "\u2005": "0.25em",
872
+ "\u2006": "0.166em",
873
+ "\u205f": "0.222em",
874
+ "\u2007": "1ch",
875
+ "\u2008": "0.27em",
876
+ "\u2009": "0.17em",
877
+ "\u200a": "0.09em",
878
+ };
879
+ if (ss[x.value].match(/[a-zA-Z\u0391-\u03C9]/)) {
880
+ tag = "mi";
881
+ } else if (space_width[ss[x.value]]) {
882
+ tag = "mspace";
883
+ space_w = { width: space_width[ss[x.value]] };
884
+ } else {
885
+ tag = "mo";
886
+ }
887
+ const el = createMath(tag, ss[x.value], space_w);
888
+ fragment.append(el);
889
+ } else if (f[x.value]) {
890
+ const el = f[x.value](null, null, e);
891
+ fragment.append(el);
892
+ } else if (ff[x.value]) {
893
+ const el = ff[x.value]([x.children], null, e);
894
+ fragment.append(el);
895
+ }
896
+ }
897
+ }
898
+
899
+ if (x.type === "str") {
900
+ const el = createMath("ms", font(x.value, e));
901
+ fragment.append(el);
902
+ }
903
+
904
+ if (x.type === "v" || x.type === "sharp") {
905
+ let tag: keyof MathMLElementTagNameMap;
906
+ let value = x.value;
907
+ if (x.value.match(/[0-9.]+/)) {
908
+ tag = "mn";
909
+ } else if (x.value.match(/[a-zA-Z\u0391-\u03C9]/)) {
910
+ tag = "mi";
911
+ } else if (x.value.match(/^'+$/)) {
912
+ if (!x.esc) value = primeRun(x.value.length);
913
+ tag = "mo";
914
+ } else {
915
+ tag = "mo";
916
+ }
917
+ if (x.type === "sharp") {
918
+ if (x.value.startsWith("emoji.")) {
919
+ tag = "mi";
920
+ value = emojix[x.value.replace("emoji.", "")];
921
+ }
922
+ }
923
+ const el = createMath(tag, font(value, e));
924
+ fragment.append(el);
925
+ }
926
+
927
+ if (x.type === "group") {
928
+ // 未闭合组:typst 只剩开括号(math_delimited 走到行尾没等到闭括号),
929
+ // 不再包一层 mrow(typst 同样平铺,由外层需要单个元素时才包,
930
+ // 比如 mfrac 的分母),开括号也不参与拉伸。
931
+ if (x.kh && x.kh.length === 1) {
932
+ fragment.append(createMath("mo", x.kh, { stretchy: "false" }));
933
+ fragment.append(render(x.children, e));
934
+ } else {
935
+ fragment.append(f.lr([[x]], null, e));
936
+ }
937
+ }
938
+ }
939
+
940
+ return fragment;
1756
941
  }
1757
942
 
1758
- function render(tree: tree, e?: fonts) {
1759
- let fragment = document.createDocumentFragment();
1760
-
1761
- tree = ast2(tree);
1762
-
1763
- // 多行
1764
- // 处理\ 换行
1765
- {
1766
- let xx = false;
1767
- for (let n in tree) {
1768
- const i = tree[n];
1769
- if (is_br(i)) {
1770
- xx = true;
1771
- break;
1772
- }
1773
- }
1774
- if (xx) {
1775
- const trees: tree[] = [[]];
1776
- for (let n = 0; n < tree.length; n++) {
1777
- const i = tree[n];
1778
- if (is_br(i)) {
1779
- trees.push([]);
1780
- } else {
1781
- trees.at(-1).push(i);
1782
- }
1783
- }
1784
- return x_table(trees);
1785
- }
1786
- }
1787
-
1788
- // 单行
1789
-
1790
- tree = ast3(tree);
1791
-
1792
- {
1793
- let t: tree = [];
1794
- for (let x of tree) {
1795
- // 移除&
1796
- if (!(x.type == "v" && x.value == "&" && !x.esc)) {
1797
- t.push(x);
1798
- }
1799
- }
1800
- tree = t;
1801
- }
1802
-
1803
- // 移除group1
1804
- {
1805
- let t: tree = [];
1806
- for (let x of tree) {
1807
- if (x.type == "group1") {
1808
- t.push(...x.children);
1809
- } else {
1810
- t.push(x);
1811
- }
1812
- }
1813
- tree = t;
1814
- }
1815
-
1816
- console.log("ast3", structuredClone(tree));
1817
- for (let i in tree) {
1818
- let n = Number(i);
1819
- let x = tree[n];
1820
-
1821
- // 函数
1822
- if (x.type === "f") {
1823
- // 带有括号(参数)的函数
1824
- if (x.children && (x.kh === "()" || !x.kh)) {
1825
- let { attr, dic } = f_attr(x);
1826
-
1827
- if (f[x.value]) {
1828
- let el = f[x.value](attr as tree[], dic, e);
1829
- fragment.append(el);
1830
- } else if (ss[x.value]) {
1831
- let el = createMath("mi", ss[x.value]);
1832
- fragment.append(el, render(in_kh(x.children), e));
1833
- } else if (ff[x.value]) {
1834
- let el = ff[x.value](attr as tree[], dic, e);
1835
- fragment.append(el);
1836
- }
1837
- } else {
1838
- if (ss[x.value]) {
1839
- let tag: keyof MathMLElementTagNameMap;
1840
- let space_w = null;
1841
- const space_width = {
1842
- " ": "0.36em",
1843
- "\u2002": "0.5em",
1844
- "\u2003": "1em",
1845
- "\u2004": "0.333em",
1846
- "\u2005": "0.25em",
1847
- "\u2006": "0.166em",
1848
- "\u205f": "0.222em",
1849
- "\u2007": "1ch",
1850
- "\u2008": "0.27em",
1851
- "\u2009": "0.17em",
1852
- "\u200a": "0.09em",
1853
- };
1854
- if (ss[x.value].match(/[a-zA-Z\u0391-\u03C9]/)) {
1855
- tag = "mi";
1856
- } else if (space_width[ss[x.value]]) {
1857
- tag = "mspace";
1858
- space_w = { width: space_width[ss[x.value]] };
1859
- } else {
1860
- tag = "mo";
1861
- }
1862
- let el = createMath(tag, ss[x.value], space_w);
1863
- fragment.append(el);
1864
- } else if (f[x.value]) {
1865
- let el = f[x.value](null, null, e);
1866
- fragment.append(el);
1867
- } else if (ff[x.value]) {
1868
- let el = ff[x.value]([x.children], null, e);
1869
- fragment.append(el);
1870
- }
1871
- }
1872
- }
1873
-
1874
- if (x.type == "str") {
1875
- let el = createMath("ms", font(x.value, e));
1876
- fragment.append(el);
1877
- }
1878
-
1879
- if (x.type === "v" || x.type === "sharp") {
1880
- let tag: keyof MathMLElementTagNameMap;
1881
- let value = x.value;
1882
- if (x.value.match(/[0-9.]+/)) {
1883
- tag = "mn";
1884
- } else if (x.value.match(/[a-zA-Z\u0391-\u03C9]/)) {
1885
- tag = "mi";
1886
- } else if (x.value.match(/^'+$/)) {
1887
- if (!x.esc) value = x.value.replaceAll("'", ss["prime"]);
1888
- tag = "mo";
1889
- } else {
1890
- tag = "mo";
1891
- }
1892
- if (x.type === "sharp") {
1893
- if (x.value.startsWith("emoji.")) {
1894
- tag = "mi";
1895
- value = emojix[x.value.replace("emoji.", "")];
1896
- }
1897
- }
1898
- let el = createMath(tag, font(value, e));
1899
- fragment.append(el);
1900
- }
1901
-
1902
- if (x.type === "group") {
1903
- fragment.append(f.lr([[x]], null, e));
1904
- }
1905
- }
1906
-
1907
- return fragment;
943
+ function init(p: { emoji: boolean }) {
944
+ for (const i in p) {
945
+ if (init_c[i]) init_c[i] = p[i];
946
+ }
1908
947
  }
1909
948
 
1910
- var init_c: { emoji: (str: string) => string } = {
1911
- emoji: null,
1912
- };
949
+ // Build the MathML virtual DOM (no `document`).
950
+ function toMMLV(str: string, inline?: boolean): VEl {
951
+ setDisplay(!inline);
952
+ const obj = ast(str);
1913
953
 
1914
- function init(p: { emoji: boolean }) {
1915
- for (let i in p) {
1916
- if (init_c[i]) init_c[i] = p[i];
1917
- }
954
+ const mathEl = createMath("math");
955
+ if (!inline) mathEl.setAttribute("display", "block");
956
+ const f = render(obj);
957
+ mathEl.append(f);
958
+ return mathEl;
1918
959
  }
1919
960
 
1920
- function toMML(str: string, inline?: boolean) {
1921
- let obj = ast(str);
1922
- console.log("ast1", obj);
1923
-
1924
- let mathEl = createMath("math");
1925
- if (!inline) mathEl.setAttribute("display", "block");
1926
- let f = render(obj);
1927
- mathEl.append(f);
1928
- return mathEl;
961
+ // Materialize to real MathML DOM (the only path that touches `document`).
962
+ function toMML(str: string, inline?: boolean): MathMLElement {
963
+ return toDom(toMMLV(str, inline)) as MathMLElement;
1929
964
  }
1930
965
 
1931
- function toMMLHTML(str: string) {
1932
- return toMML(str).outerHTML;
966
+ // String MathML (DOM-free)。inline=true 输出行内(无 display 属性),默认块级。
967
+ function toMMLHTML(str: string, inline?: boolean) {
968
+ return toHtml(toMMLV(str, inline));
1933
969
  }
1934
970
 
1935
971
  const version = {
1936
- lan: "0.11.1",
1937
- symbol: "0.11.1",
972
+ lan: "0.11.1",
973
+ symbol: "0.15.1",
974
+ emoji: "0.15.1",
1938
975
  };
1939
976
 
1940
- export { init, toMML, toMMLHTML, version };
977
+ export { ast2, ast3, init, toMML, toMMLHTML, toMMLV, version };