tex2typst 0.3.24 → 0.3.26

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -33,10 +33,9 @@ var TexToken = class _TexToken {
33
33
  }
34
34
  };
35
35
  var TexNode = class {
36
- constructor(type, head, args) {
36
+ constructor(type, head) {
37
37
  this.type = type;
38
38
  this.head = head ? head : TexToken.EMPTY;
39
- this.args = args;
40
39
  }
41
40
  // Note that this is only shallow equality.
42
41
  eq(other) {
@@ -91,16 +90,17 @@ var TexText = class extends TexNode {
91
90
  }
92
91
  };
93
92
  var TexGroup = class extends TexNode {
94
- constructor(args) {
95
- super("ordgroup", TexToken.EMPTY, args);
93
+ constructor(items) {
94
+ super("ordgroup", TexToken.EMPTY);
95
+ this.items = items;
96
96
  }
97
97
  serialize() {
98
- return this.args.map((n) => n.serialize()).flat();
98
+ return this.items.map((n) => n.serialize()).flat();
99
99
  }
100
100
  };
101
101
  var TexSupSub = class extends TexNode {
102
102
  constructor(data) {
103
- super("supsub", TexToken.EMPTY, []);
103
+ super("supsub", TexToken.EMPTY);
104
104
  this.base = data.base;
105
105
  this.sup = data.sup;
106
106
  this.sub = data.sub;
@@ -143,7 +143,8 @@ var TexSupSub = class extends TexNode {
143
143
  };
144
144
  var TexFuncCall = class extends TexNode {
145
145
  constructor(head, args, data = null) {
146
- super("funcCall", head, args);
146
+ super("funcCall", head);
147
+ this.args = args;
147
148
  this.data = data;
148
149
  }
149
150
  serialize() {
@@ -163,8 +164,9 @@ var TexFuncCall = class extends TexNode {
163
164
  }
164
165
  };
165
166
  var TexLeftRight = class extends TexNode {
166
- constructor(args, data) {
167
- super("leftright", TexToken.EMPTY, args);
167
+ constructor(data) {
168
+ super("leftright", TexToken.EMPTY);
169
+ this.body = data.body;
168
170
  this.left = data.left;
169
171
  this.right = data.right;
170
172
  }
@@ -172,17 +174,18 @@ var TexLeftRight = class extends TexNode {
172
174
  let tokens = [];
173
175
  tokens.push(new TexToken(2 /* COMMAND */, "\\left"));
174
176
  tokens.push(new TexToken(1 /* ELEMENT */, this.left ? this.left.value : "."));
175
- tokens = tokens.concat(this.args.map((n) => n.serialize()).flat());
177
+ tokens = tokens.concat(this.body.serialize());
176
178
  tokens.push(new TexToken(2 /* COMMAND */, "\\right"));
177
179
  tokens.push(new TexToken(1 /* ELEMENT */, this.right ? this.right.value : "."));
178
180
  return tokens;
179
181
  }
180
182
  };
181
183
  var TexBeginEnd = class extends TexNode {
182
- constructor(head, args, data) {
184
+ constructor(head, matrix, data = null) {
183
185
  assert(head.type === 3 /* LITERAL */);
184
- super("beginend", head, args);
185
- this.matrix = data;
186
+ super("beginend", head);
187
+ this.matrix = matrix;
188
+ this.data = data;
186
189
  }
187
190
  serialize() {
188
191
  let tokens = [];
@@ -230,6 +233,7 @@ function writeTexTokenBuffer(buffer, token) {
230
233
  no_need_space ||= buffer === "";
231
234
  no_need_space ||= /[\(\[{]\s*(-|\+)$/.test(buffer) || buffer === "-" || buffer === "+";
232
235
  no_need_space ||= buffer.endsWith("&") && str === "=";
236
+ no_need_space ||= /\d$/.test(buffer) && /^[a-zA-Z]$/.test(str);
233
237
  }
234
238
  if (!no_need_space) {
235
239
  buffer += " ";
@@ -549,7 +553,8 @@ var TEX_BINARY_COMMANDS = [
549
553
  "dfrac",
550
554
  "tbinom",
551
555
  "overset",
552
- "underset"
556
+ "underset",
557
+ "textcolor"
553
558
  ];
554
559
  function unescape(str) {
555
560
  const chars = ["{", "}", "\\", "$", "&", "#", "_", "%"];
@@ -559,7 +564,7 @@ function unescape(str) {
559
564
  return str;
560
565
  }
561
566
  var rules_map = /* @__PURE__ */ new Map([
562
- // math `\begin{array}{cc}`
567
+ // match `\begin{array}{cc}`
563
568
  [
564
569
  String.raw`\\begin{(array|subarry)}{(.+?)}`,
565
570
  (s) => {
@@ -576,7 +581,7 @@ var rules_map = /* @__PURE__ */ new Map([
576
581
  }
577
582
  ],
578
583
  [
579
- String.raw`\\(text|operatorname|begin|end|hspace|array){(.+?)}`,
584
+ String.raw`\\(text|operatorname|textcolor|begin|end|hspace|array){(.+?)}`,
580
585
  (s) => {
581
586
  const match = s.reMatchArray();
582
587
  return [
@@ -646,12 +651,16 @@ function tokenize_tex(input) {
646
651
  var IGNORED_COMMANDS = [
647
652
  "bigl",
648
653
  "bigr",
654
+ "bigm",
649
655
  "biggl",
650
656
  "biggr",
657
+ "biggm",
651
658
  "Bigl",
652
659
  "Bigr",
660
+ "Bigm",
653
661
  "Biggl",
654
- "Biggr"
662
+ "Biggr",
663
+ "Biggm"
655
664
  ];
656
665
  var EMPTY_NODE = TexToken.EMPTY.toNode();
657
666
  function get_command_param_num(command) {
@@ -809,16 +818,14 @@ var LatexParser = class {
809
818
  res.sub = sub;
810
819
  }
811
820
  if (num_prime > 0) {
812
- res.sup = new TexGroup([]);
821
+ const items = [];
813
822
  for (let i = 0; i < num_prime; i++) {
814
- res.sup.args.push(new TexToken(1 /* ELEMENT */, "'").toNode());
823
+ items.push(new TexToken(1 /* ELEMENT */, "'").toNode());
815
824
  }
816
825
  if (sup) {
817
- res.sup.args.push(sup);
818
- }
819
- if (res.sup.args.length === 1) {
820
- res.sup = res.sup.args[0];
826
+ items.push(sup);
821
827
  }
828
+ res.sup = items.length === 1 ? items[0] : new TexGroup(items);
822
829
  } else if (sup) {
823
830
  res.sup = sup;
824
831
  }
@@ -981,10 +988,9 @@ var LatexParser = class {
981
988
  }
982
989
  pos++;
983
990
  const [body, _] = this.parseGroup(tokens, exprInsideStart, exprInsideEnd);
984
- const args = [body];
985
991
  const left = leftDelimiter.value === "." ? null : leftDelimiter;
986
992
  const right = rightDelimiter.value === "." ? null : rightDelimiter;
987
- const res = new TexLeftRight(args, { left, right });
993
+ const res = new TexLeftRight({ body, left, right });
988
994
  return [res, pos];
989
995
  }
990
996
  parseBeginEndExpr(tokens, start) {
@@ -995,12 +1001,10 @@ var LatexParser = class {
995
1001
  assert(tokens[pos + 2].eq(RIGHT_CURLY_BRACKET));
996
1002
  const envName = tokens[pos + 1].value;
997
1003
  pos += 3;
998
- const args = [];
1004
+ let data = null;
999
1005
  if (["array", "subarray"].includes(envName)) {
1000
1006
  pos += eat_whitespaces(tokens, pos).length;
1001
- const [arg, newPos] = this.parseNextArg(tokens, pos);
1002
- args.push(arg);
1003
- pos = newPos;
1007
+ [data, pos] = this.parseNextArg(tokens, pos);
1004
1008
  }
1005
1009
  pos += eat_whitespaces(tokens, pos).length;
1006
1010
  const exprInsideStart = pos;
@@ -1022,7 +1026,7 @@ var LatexParser = class {
1022
1026
  exprInside.pop();
1023
1027
  }
1024
1028
  const body = this.parseAligned(exprInside);
1025
- const res = new TexBeginEnd(new TexToken(3 /* LITERAL */, envName), args, body);
1029
+ const res = new TexBeginEnd(new TexToken(3 /* LITERAL */, envName), body, data);
1026
1030
  return [res, pos];
1027
1031
  }
1028
1032
  parseAligned(tokens) {
@@ -1052,7 +1056,7 @@ var LatexParser = class {
1052
1056
  group = new TexGroup([]);
1053
1057
  row.push(group);
1054
1058
  } else {
1055
- group.args.push(res);
1059
+ group.items.push(res);
1056
1060
  }
1057
1061
  }
1058
1062
  return allRows;
@@ -1120,12 +1124,14 @@ var TypstToken = class _TypstToken {
1120
1124
  static {
1121
1125
  this.NONE = new _TypstToken(0 /* NONE */, "#none");
1122
1126
  }
1127
+ static {
1128
+ this.EMPTY = new _TypstToken(2 /* ELEMENT */, "");
1129
+ }
1123
1130
  };
1124
1131
  var TypstNode = class {
1125
- constructor(type, head, args) {
1132
+ constructor(type, head) {
1126
1133
  this.type = type;
1127
1134
  this.head = head ? head : TypstToken.NONE;
1128
- this.args = args;
1129
1135
  }
1130
1136
  setOptions(options) {
1131
1137
  this.options = options;
@@ -1150,16 +1156,17 @@ var TypstTerminal = class extends TypstNode {
1150
1156
  }
1151
1157
  };
1152
1158
  var TypstGroup = class extends TypstNode {
1153
- constructor(args) {
1154
- super("group", TypstToken.NONE, args);
1159
+ constructor(items) {
1160
+ super("group", TypstToken.NONE);
1161
+ this.items = items;
1155
1162
  }
1156
1163
  isOverHigh() {
1157
- return this.args.some((n) => n.isOverHigh());
1164
+ return this.items.some((n) => n.isOverHigh());
1158
1165
  }
1159
1166
  };
1160
1167
  var TypstSupsub = class extends TypstNode {
1161
1168
  constructor(data) {
1162
- super("supsub", TypstToken.NONE, []);
1169
+ super("supsub", TypstToken.NONE);
1163
1170
  this.base = data.base;
1164
1171
  this.sup = data.sup;
1165
1172
  this.sub = data.sub;
@@ -1170,7 +1177,8 @@ var TypstSupsub = class extends TypstNode {
1170
1177
  };
1171
1178
  var TypstFuncCall = class extends TypstNode {
1172
1179
  constructor(head, args) {
1173
- super("funcCall", head, args);
1180
+ super("funcCall", head);
1181
+ this.args = args;
1174
1182
  }
1175
1183
  isOverHigh() {
1176
1184
  if (this.head.value === "frac") {
@@ -1181,47 +1189,48 @@ var TypstFuncCall = class extends TypstNode {
1181
1189
  };
1182
1190
  var TypstFraction = class extends TypstNode {
1183
1191
  constructor(args) {
1184
- super("fraction", TypstToken.NONE, args);
1192
+ super("fraction", TypstToken.NONE);
1193
+ this.args = args;
1185
1194
  }
1186
1195
  isOverHigh() {
1187
1196
  return true;
1188
1197
  }
1189
1198
  };
1190
1199
  var TypstLeftright = class extends TypstNode {
1191
- constructor(head, args, data) {
1192
- super("leftright", head, args);
1200
+ // head is either null or 'lr'
1201
+ constructor(head, data) {
1202
+ super("leftright", head);
1203
+ this.body = data.body;
1193
1204
  this.left = data.left;
1194
1205
  this.right = data.right;
1195
1206
  }
1196
1207
  isOverHigh() {
1197
- return this.args.some((n) => n.isOverHigh());
1208
+ return this.body.isOverHigh();
1198
1209
  }
1199
1210
  };
1200
- var TypstAlign = class extends TypstNode {
1201
- constructor(data) {
1202
- super("align", TypstToken.NONE, []);
1211
+ var TypstMatrixLike = class extends TypstNode {
1212
+ // head is 'mat', 'cases' or null
1213
+ constructor(head, data) {
1214
+ super("matrixLike", head);
1203
1215
  this.matrix = data;
1204
1216
  }
1205
1217
  isOverHigh() {
1206
1218
  return true;
1207
1219
  }
1208
- };
1209
- var TypstMatrix = class extends TypstNode {
1210
- constructor(data) {
1211
- super("matrix", TypstToken.NONE, []);
1212
- this.matrix = data;
1220
+ static {
1221
+ this.MAT = new TypstToken(1 /* SYMBOL */, "mat");
1213
1222
  }
1214
- isOverHigh() {
1215
- return true;
1223
+ static {
1224
+ this.CASES = new TypstToken(1 /* SYMBOL */, "cases");
1216
1225
  }
1217
1226
  };
1218
- var TypstCases = class extends TypstNode {
1219
- constructor(data) {
1220
- super("cases", TypstToken.NONE, []);
1221
- this.matrix = data;
1227
+ var TypstMarkupFunc = class extends TypstNode {
1228
+ constructor(head, fragments) {
1229
+ super("markupFunc", head);
1230
+ this.fragments = fragments;
1222
1231
  }
1223
1232
  isOverHigh() {
1224
- return true;
1233
+ return this.fragments.some((n) => n.isOverHigh());
1225
1234
  }
1226
1235
  };
1227
1236
 
@@ -1300,7 +1309,7 @@ var TypstWriter = class {
1300
1309
  this.inftyToOo = options.inftyToOo;
1301
1310
  this.optimize = options.optimize;
1302
1311
  }
1303
- writeBuffer(token) {
1312
+ writeBuffer(previousToken, token) {
1304
1313
  const str = token.toString();
1305
1314
  if (str === "") {
1306
1315
  return;
@@ -1317,7 +1326,11 @@ var TypstWriter = class {
1317
1326
  no_need_space ||= /^\s/.test(str);
1318
1327
  no_need_space ||= this.buffer.endsWith("&") && str === "=";
1319
1328
  no_need_space ||= this.buffer.endsWith("/") || str === "/";
1329
+ no_need_space ||= token.type === 3 /* LITERAL */;
1320
1330
  no_need_space ||= /[\s_^{\(]$/.test(this.buffer);
1331
+ if (previousToken !== null) {
1332
+ no_need_space ||= previousToken.type === 3 /* LITERAL */;
1333
+ }
1321
1334
  if (!no_need_space) {
1322
1335
  this.buffer += " ";
1323
1336
  }
@@ -1367,7 +1380,7 @@ var TypstWriter = class {
1367
1380
  }
1368
1381
  case "group": {
1369
1382
  const node = abstractNode;
1370
- for (const item of node.args) {
1383
+ for (const item of node.items) {
1371
1384
  this.serialize(item);
1372
1385
  }
1373
1386
  break;
@@ -1383,9 +1396,7 @@ var TypstWriter = class {
1383
1396
  if (left) {
1384
1397
  this.queue.push(left);
1385
1398
  }
1386
- for (const item of node.args) {
1387
- this.serialize(item);
1388
- }
1399
+ this.serialize(node.body);
1389
1400
  if (right) {
1390
1401
  this.queue.push(right);
1391
1402
  }
@@ -1451,74 +1462,71 @@ var TypstWriter = class {
1451
1462
  this.appendWithBracketsIfNeeded(denominator);
1452
1463
  break;
1453
1464
  }
1454
- case "align": {
1465
+ case "matrixLike": {
1455
1466
  const node = abstractNode;
1456
1467
  const matrix = node.matrix;
1457
- matrix.forEach((row, i) => {
1458
- row.forEach((cell, j) => {
1459
- if (j > 0) {
1460
- this.queue.push(new TypstToken(2 /* ELEMENT */, "&"));
1468
+ let cell_sep;
1469
+ let row_sep;
1470
+ if (node.head.eq(TypstMatrixLike.MAT)) {
1471
+ cell_sep = new TypstToken(2 /* ELEMENT */, ",");
1472
+ row_sep = new TypstToken(2 /* ELEMENT */, ";");
1473
+ } else if (node.head.eq(TypstMatrixLike.CASES)) {
1474
+ cell_sep = new TypstToken(2 /* ELEMENT */, "&");
1475
+ row_sep = new TypstToken(2 /* ELEMENT */, ",");
1476
+ } else if (node.head.eq(TypstToken.NONE)) {
1477
+ cell_sep = new TypstToken(2 /* ELEMENT */, "&");
1478
+ row_sep = new TypstToken(1 /* SYMBOL */, "\\");
1479
+ }
1480
+ if (!node.head.eq(TypstToken.NONE)) {
1481
+ this.queue.push(node.head);
1482
+ this.insideFunctionDepth++;
1483
+ this.queue.push(TYPST_LEFT_PARENTHESIS);
1484
+ if (node.options) {
1485
+ for (const [key, value] of Object.entries(node.options)) {
1486
+ this.queue.push(new TypstToken(3 /* LITERAL */, `${key}: ${value.toString()}, `));
1461
1487
  }
1462
- this.serialize(cell);
1463
- });
1464
- if (i < matrix.length - 1) {
1465
- this.queue.push(new TypstToken(1 /* SYMBOL */, "\\"));
1466
- }
1467
- });
1468
- break;
1469
- }
1470
- case "matrix": {
1471
- const node = abstractNode;
1472
- const matrix = node.matrix;
1473
- this.queue.push(new TypstToken(1 /* SYMBOL */, "mat"));
1474
- this.insideFunctionDepth++;
1475
- this.queue.push(TYPST_LEFT_PARENTHESIS);
1476
- if (node.options) {
1477
- for (const [key, value] of Object.entries(node.options)) {
1478
- this.queue.push(new TypstToken(3 /* LITERAL */, `${key}: ${value.toString()}, `));
1479
1488
  }
1480
1489
  }
1481
1490
  matrix.forEach((row, i) => {
1482
1491
  row.forEach((cell, j) => {
1483
1492
  this.serialize(cell);
1484
1493
  if (j < row.length - 1) {
1485
- this.queue.push(new TypstToken(2 /* ELEMENT */, ","));
1494
+ this.queue.push(cell_sep);
1486
1495
  } else {
1487
1496
  if (i < matrix.length - 1) {
1488
- this.queue.push(new TypstToken(2 /* ELEMENT */, ";"));
1497
+ this.queue.push(row_sep);
1489
1498
  }
1490
1499
  }
1491
1500
  });
1492
1501
  });
1493
- this.queue.push(TYPST_RIGHT_PARENTHESIS);
1494
- this.insideFunctionDepth--;
1502
+ if (!node.head.eq(TypstToken.NONE)) {
1503
+ this.queue.push(TYPST_RIGHT_PARENTHESIS);
1504
+ this.insideFunctionDepth--;
1505
+ }
1495
1506
  break;
1496
1507
  }
1497
- case "cases": {
1508
+ case "markupFunc": {
1498
1509
  const node = abstractNode;
1499
- const cases = node.matrix;
1500
- this.queue.push(new TypstToken(1 /* SYMBOL */, "cases"));
1501
- this.insideFunctionDepth++;
1510
+ this.queue.push(node.head);
1502
1511
  this.queue.push(TYPST_LEFT_PARENTHESIS);
1503
1512
  if (node.options) {
1504
- for (const [key, value] of Object.entries(node.options)) {
1505
- this.queue.push(new TypstToken(3 /* LITERAL */, `${key}: ${value.toString()}, `));
1513
+ const entries = Object.entries(node.options);
1514
+ for (let i = 0; i < entries.length; i++) {
1515
+ const [key, value] = entries[i];
1516
+ this.queue.push(new TypstToken(3 /* LITERAL */, `${key}: ${value.toString()}`));
1517
+ if (i < entries.length - 1) {
1518
+ this.queue.push(new TypstToken(2 /* ELEMENT */, ","));
1519
+ }
1506
1520
  }
1507
1521
  }
1508
- cases.forEach((row, i) => {
1509
- row.forEach((cell, j) => {
1510
- this.serialize(cell);
1511
- if (j < row.length - 1) {
1512
- this.queue.push(new TypstToken(2 /* ELEMENT */, "&"));
1513
- } else {
1514
- if (i < cases.length - 1) {
1515
- this.queue.push(new TypstToken(2 /* ELEMENT */, ","));
1516
- }
1517
- }
1518
- });
1519
- });
1520
1522
  this.queue.push(TYPST_RIGHT_PARENTHESIS);
1521
- this.insideFunctionDepth--;
1523
+ this.queue.push(new TypstToken(3 /* LITERAL */, "["));
1524
+ for (const frag of node.fragments) {
1525
+ this.queue.push(new TypstToken(3 /* LITERAL */, "$"));
1526
+ this.serialize(frag);
1527
+ this.queue.push(new TypstToken(3 /* LITERAL */, "$"));
1528
+ }
1529
+ this.queue.push(new TypstToken(3 /* LITERAL */, "]"));
1522
1530
  break;
1523
1531
  }
1524
1532
  default:
@@ -1526,13 +1534,14 @@ var TypstWriter = class {
1526
1534
  }
1527
1535
  }
1528
1536
  appendWithBracketsIfNeeded(node) {
1529
- let need_to_wrap = ["group", "supsub", "align", "fraction", "empty"].includes(node.type);
1537
+ let need_to_wrap = ["group", "supsub", "matrixLike", "fraction", "empty"].includes(node.type);
1530
1538
  if (node.type === "group") {
1531
- if (node.args.length === 0) {
1539
+ const group = node;
1540
+ if (group.items.length === 0) {
1532
1541
  need_to_wrap = true;
1533
1542
  } else {
1534
- const first = node.args[0];
1535
- const last = node.args[node.args.length - 1];
1543
+ const first = group.items[0];
1544
+ const last = group.items[group.items.length - 1];
1536
1545
  if (is_delimiter(first) && is_delimiter(last)) {
1537
1546
  need_to_wrap = false;
1538
1547
  }
@@ -1559,9 +1568,11 @@ var TypstWriter = class {
1559
1568
  }
1560
1569
  }
1561
1570
  this.queue = this.queue.filter((token) => !token.eq(dummy_token));
1562
- this.queue.forEach((token) => {
1563
- this.writeBuffer(token);
1564
- });
1571
+ for (let i = 0; i < this.queue.length; i++) {
1572
+ let token = this.queue[i];
1573
+ let previous_token = i === 0 ? null : this.queue[i - 1];
1574
+ this.writeBuffer(previous_token, token);
1575
+ }
1565
1576
  this.queue = [];
1566
1577
  }
1567
1578
  finalize() {
@@ -1849,6 +1860,7 @@ var symbolMap = /* @__PURE__ */ new Map([
1849
1860
  ["xi", "xi"],
1850
1861
  ["yen", "yen"],
1851
1862
  ["zeta", "zeta"],
1863
+ ["intop", "limits(integral)"],
1852
1864
  // extended
1853
1865
  ["mathscr", "scr"],
1854
1866
  ["LaTeX", "#LaTeX"],
@@ -2836,7 +2848,7 @@ function convert_tex_node_to_typst(abstractNode, options = {}) {
2836
2848
  case "ordgroup":
2837
2849
  const node = abstractNode;
2838
2850
  return new TypstGroup(
2839
- node.args.map((n) => convert_tex_node_to_typst(n, options))
2851
+ node.items.map((n) => convert_tex_node_to_typst(n, options))
2840
2852
  );
2841
2853
  case "supsub": {
2842
2854
  const node2 = abstractNode;
@@ -2862,8 +2874,7 @@ function convert_tex_node_to_typst(abstractNode, options = {}) {
2862
2874
  case "leftright": {
2863
2875
  const node2 = abstractNode;
2864
2876
  const { left, right } = node2;
2865
- const [_body] = node2.args;
2866
- const typ_body = convert_tex_node_to_typst(_body, options);
2877
+ const typ_body = convert_tex_node_to_typst(node2.body, options);
2867
2878
  if (options.optimize) {
2868
2879
  if (left !== null && right !== null) {
2869
2880
  const typ_left2 = tex_token_to_typst(left, options);
@@ -2900,8 +2911,7 @@ function convert_tex_node_to_typst(abstractNode, options = {}) {
2900
2911
  }
2901
2912
  return new TypstLeftright(
2902
2913
  new TypstToken(1 /* SYMBOL */, "lr"),
2903
- [typ_body],
2904
- { left: typ_left, right: typ_right }
2914
+ { body: typ_body, left: typ_left, right: typ_right }
2905
2915
  );
2906
2916
  }
2907
2917
  case "funcCall": {
@@ -2944,6 +2954,14 @@ function convert_tex_node_to_typst(abstractNode, options = {}) {
2944
2954
  }
2945
2955
  return new TypstFuncCall(new TypstToken(1 /* SYMBOL */, "op"), [new TypstToken(4 /* TEXT */, arg0.head.value).toNode()]);
2946
2956
  }
2957
+ if (node2.head.value === "\\textcolor") {
2958
+ const res = new TypstMarkupFunc(
2959
+ new TypstToken(1 /* SYMBOL */, `#text`),
2960
+ [convert_tex_node_to_typst(node2.args[1], options)]
2961
+ );
2962
+ res.setOptions({ fill: arg0 });
2963
+ return res;
2964
+ }
2947
2965
  if (node2.head.value === "\\substack") {
2948
2966
  return arg0;
2949
2967
  }
@@ -2973,38 +2991,40 @@ function convert_tex_node_to_typst(abstractNode, options = {}) {
2973
2991
  }
2974
2992
  case "beginend": {
2975
2993
  const node2 = abstractNode;
2976
- const data = node2.matrix.map((row) => row.map((n) => convert_tex_node_to_typst(n, options)));
2994
+ const matrix = node2.matrix.map((row) => row.map((n) => convert_tex_node_to_typst(n, options)));
2977
2995
  if (node2.head.value.startsWith("align")) {
2978
- return new TypstAlign(data);
2996
+ return new TypstMatrixLike(null, matrix);
2979
2997
  }
2980
2998
  if (node2.head.value === "cases") {
2981
- return new TypstCases(data);
2999
+ return new TypstMatrixLike(TypstMatrixLike.CASES, matrix);
2982
3000
  }
2983
3001
  if (node2.head.value === "subarray") {
2984
- const align_node = node2.args[0];
2985
- switch (align_node.head.value) {
2986
- case "r":
2987
- data.forEach((row) => row[0].args.push(new TypstToken(7 /* CONTROL */, "&").toNode()));
2988
- break;
2989
- case "l":
2990
- data.forEach((row) => row[0].args.unshift(new TypstToken(7 /* CONTROL */, "&").toNode()));
2991
- break;
2992
- default:
2993
- break;
3002
+ if (node2.data) {
3003
+ const align_node = node2.data;
3004
+ switch (align_node.head.value) {
3005
+ case "r":
3006
+ matrix.forEach((row) => row.push(TypstToken.EMPTY.toNode()));
3007
+ break;
3008
+ case "l":
3009
+ matrix.forEach((row) => row.unshift(TypstToken.EMPTY.toNode()));
3010
+ break;
3011
+ default:
3012
+ break;
3013
+ }
2994
3014
  }
2995
- return new TypstAlign(data);
3015
+ return new TypstMatrixLike(null, matrix);
2996
3016
  }
2997
3017
  if (node2.head.value === "array") {
2998
3018
  const np = { "delim": TYPST_NONE };
2999
- assert(node2.args.length > 0 && node2.args[0].head.type === 3 /* LITERAL */);
3000
- const np_new = convert_tex_array_align_literal(node2.args[0].head.value);
3019
+ assert(node2.data !== null && node2.head.type === 3 /* LITERAL */);
3020
+ const np_new = convert_tex_array_align_literal(node2.data.head.value);
3001
3021
  Object.assign(np, np_new);
3002
- const res = new TypstMatrix(data);
3022
+ const res = new TypstMatrixLike(TypstMatrixLike.MAT, matrix);
3003
3023
  res.setOptions(np);
3004
3024
  return res;
3005
3025
  }
3006
3026
  if (node2.head.value.endsWith("matrix")) {
3007
- const res = new TypstMatrix(data);
3027
+ const res = new TypstMatrixLike(TypstMatrixLike.MAT, matrix);
3008
3028
  let delim;
3009
3029
  switch (node2.head.value) {
3010
3030
  case "matrix":
@@ -3123,32 +3143,30 @@ function convert_typst_node_to_tex(abstractNode) {
3123
3143
  }
3124
3144
  case "group": {
3125
3145
  const node = abstractNode;
3126
- const args = node.args.map(convert_typst_node_to_tex);
3146
+ const args = node.items.map(convert_typst_node_to_tex);
3127
3147
  const alignment_char = new TexToken(7 /* CONTROL */, "&").toNode();
3128
3148
  const newline_char = new TexToken(7 /* CONTROL */, "\\\\").toNode();
3129
3149
  if (array_includes(args, alignment_char)) {
3130
3150
  const rows = array_split(args, newline_char);
3131
- const data = [];
3151
+ const matrix = [];
3132
3152
  for (const row of rows) {
3133
3153
  const cells = array_split(row, alignment_char);
3134
- data.push(cells.map((cell) => new TexGroup(cell)));
3154
+ matrix.push(cells.map((cell) => new TexGroup(cell)));
3135
3155
  }
3136
- return new TexBeginEnd(new TexToken(3 /* LITERAL */, "aligned"), [], data);
3156
+ return new TexBeginEnd(new TexToken(3 /* LITERAL */, "aligned"), matrix);
3137
3157
  }
3138
3158
  return new TexGroup(args);
3139
3159
  }
3140
3160
  case "leftright": {
3141
3161
  const node = abstractNode;
3142
- const args = node.args.map(convert_typst_node_to_tex);
3162
+ const body = convert_typst_node_to_tex(node.body);
3143
3163
  let left = node.left ? typst_token_to_tex(node.left) : new TexToken(1 /* ELEMENT */, ".");
3144
3164
  let right = node.right ? typst_token_to_tex(node.right) : new TexToken(1 /* ELEMENT */, ".");
3145
3165
  if (node.isOverHigh()) {
3146
3166
  left.value = "\\left" + left.value;
3147
3167
  right.value = "\\right" + right.value;
3148
3168
  }
3149
- args.unshift(left.toNode());
3150
- args.push(right.toNode());
3151
- return new TexGroup(args);
3169
+ return new TexGroup([left.toNode(), body, right.toNode()]);
3152
3170
  }
3153
3171
  case "funcCall": {
3154
3172
  const node = abstractNode;
@@ -3158,14 +3176,15 @@ function convert_typst_node_to_tex(abstractNode) {
3158
3176
  // `\left\| a + \frac{1}{3} \right\|` <- `norm(a + 1/3)`
3159
3177
  case "norm": {
3160
3178
  const arg0 = node.args[0];
3161
- const args = [convert_typst_node_to_tex(arg0)];
3179
+ const body = convert_typst_node_to_tex(arg0);
3162
3180
  if (node.isOverHigh()) {
3163
- return new TexLeftRight(args, {
3181
+ return new TexLeftRight({
3182
+ body,
3164
3183
  left: new TexToken(2 /* COMMAND */, "\\|"),
3165
3184
  right: new TexToken(2 /* COMMAND */, "\\|")
3166
3185
  });
3167
3186
  } else {
3168
- return new TexGroup(args);
3187
+ return body;
3169
3188
  }
3170
3189
  }
3171
3190
  // special hook for floor, ceil
@@ -3178,16 +3197,17 @@ function convert_typst_node_to_tex(abstractNode) {
3178
3197
  const left = "\\l" + node.head.value;
3179
3198
  const right = "\\r" + node.head.value;
3180
3199
  const arg0 = node.args[0];
3181
- const typ_arg0 = convert_typst_node_to_tex(arg0);
3200
+ const body = convert_typst_node_to_tex(arg0);
3182
3201
  const left_node = new TexToken(2 /* COMMAND */, left);
3183
3202
  const right_node = new TexToken(2 /* COMMAND */, right);
3184
3203
  if (node.isOverHigh()) {
3185
- return new TexLeftRight([typ_arg0], {
3204
+ return new TexLeftRight({
3205
+ body,
3186
3206
  left: left_node,
3187
3207
  right: right_node
3188
3208
  });
3189
3209
  } else {
3190
- return new TexGroup([left_node.toNode(), typ_arg0, right_node.toNode()]);
3210
+ return new TexGroup([left_node.toNode(), body, right_node.toNode()]);
3191
3211
  }
3192
3212
  }
3193
3213
  // special hook for root
@@ -3208,8 +3228,8 @@ function convert_typst_node_to_tex(abstractNode) {
3208
3228
  // special hook for vec
3209
3229
  // "vec(a, b, c)" -> "\begin{pmatrix}a\\ b\\ c\end{pmatrix}"
3210
3230
  case "vec": {
3211
- const tex_data = node.args.map(convert_typst_node_to_tex).map((n) => [n]);
3212
- return new TexBeginEnd(new TexToken(3 /* LITERAL */, "pmatrix"), [], tex_data);
3231
+ const tex_matrix = node.args.map(convert_typst_node_to_tex).map((n) => [n]);
3232
+ return new TexBeginEnd(new TexToken(3 /* LITERAL */, "pmatrix"), tex_matrix);
3213
3233
  }
3214
3234
  // special hook for op
3215
3235
  case "op": {
@@ -3234,13 +3254,31 @@ function convert_typst_node_to_tex(abstractNode) {
3234
3254
  }
3235
3255
  }
3236
3256
  }
3257
+ case "markupFunc": {
3258
+ const node = abstractNode;
3259
+ switch (node.head.value) {
3260
+ case "#text": {
3261
+ if (node.options && node.options["fill"]) {
3262
+ const color = node.options["fill"];
3263
+ return new TexFuncCall(
3264
+ new TexToken(2 /* COMMAND */, "\\textcolor"),
3265
+ [convert_typst_node_to_tex(color), convert_typst_node_to_tex(node.fragments[0])]
3266
+ );
3267
+ }
3268
+ }
3269
+ case "#heading":
3270
+ default:
3271
+ throw new Error(`Unimplemented markup function: ${node.head.value}`);
3272
+ }
3273
+ }
3237
3274
  case "supsub": {
3238
3275
  const node = abstractNode;
3239
3276
  const { base, sup, sub } = node;
3240
3277
  const sup_tex = sup ? convert_typst_node_to_tex(sup) : null;
3241
3278
  const sub_tex = sub ? convert_typst_node_to_tex(sub) : null;
3242
3279
  if (base.head.eq(new TypstToken(1 /* SYMBOL */, "limits"))) {
3243
- const body_in_limits = convert_typst_node_to_tex(base.args[0]);
3280
+ const limits = base;
3281
+ const body_in_limits = convert_typst_node_to_tex(limits.args[0]);
3244
3282
  if (sup_tex !== null && sub_tex === null) {
3245
3283
  return new TexFuncCall(new TexToken(2 /* COMMAND */, "\\overset"), [sup_tex, body_in_limits]);
3246
3284
  } else if (sup_tex === null && sub_tex !== null) {
@@ -3258,50 +3296,51 @@ function convert_typst_node_to_tex(abstractNode) {
3258
3296
  });
3259
3297
  return res;
3260
3298
  }
3261
- case "matrix": {
3299
+ case "matrixLike": {
3262
3300
  const node = abstractNode;
3263
- const tex_data = node.matrix.map((row) => row.map(convert_typst_node_to_tex));
3264
- let env_type = "pmatrix";
3265
- if (node.options) {
3266
- if ("delim" in node.options) {
3267
- const delim = node.options.delim;
3268
- switch (delim.head.value) {
3269
- case "#none":
3270
- env_type = "matrix";
3271
- break;
3272
- case "[":
3273
- case "]":
3274
- env_type = "bmatrix";
3275
- break;
3276
- case "(":
3277
- case ")":
3278
- env_type = "pmatrix";
3279
- break;
3280
- case "{":
3281
- case "}":
3282
- env_type = "Bmatrix";
3283
- break;
3284
- case "|":
3285
- env_type = "vmatrix";
3286
- break;
3287
- case "bar":
3288
- case "bar.v":
3289
- env_type = "vmatrix";
3290
- break;
3291
- case "bar.v.double":
3292
- env_type = "Vmatrix";
3293
- break;
3294
- default:
3295
- throw new Error(`Unexpected delimiter ${delim.head}`);
3301
+ const tex_matrix = node.matrix.map((row) => row.map(convert_typst_node_to_tex));
3302
+ if (node.head.eq(TypstMatrixLike.MAT)) {
3303
+ let env_type = "pmatrix";
3304
+ if (node.options) {
3305
+ if ("delim" in node.options) {
3306
+ const delim = node.options.delim;
3307
+ switch (delim.head.value) {
3308
+ case "#none":
3309
+ env_type = "matrix";
3310
+ break;
3311
+ case "[":
3312
+ case "]":
3313
+ env_type = "bmatrix";
3314
+ break;
3315
+ case "(":
3316
+ case ")":
3317
+ env_type = "pmatrix";
3318
+ break;
3319
+ case "{":
3320
+ case "}":
3321
+ env_type = "Bmatrix";
3322
+ break;
3323
+ case "|":
3324
+ env_type = "vmatrix";
3325
+ break;
3326
+ case "bar":
3327
+ case "bar.v":
3328
+ env_type = "vmatrix";
3329
+ break;
3330
+ case "bar.v.double":
3331
+ env_type = "Vmatrix";
3332
+ break;
3333
+ default:
3334
+ throw new Error(`Unexpected delimiter ${delim.head}`);
3335
+ }
3296
3336
  }
3297
3337
  }
3338
+ return new TexBeginEnd(new TexToken(3 /* LITERAL */, env_type), tex_matrix);
3339
+ } else if (node.head.eq(TypstMatrixLike.CASES)) {
3340
+ return new TexBeginEnd(new TexToken(3 /* LITERAL */, "cases"), tex_matrix);
3341
+ } else {
3342
+ throw new Error(`Unexpected matrix type ${node.head}`);
3298
3343
  }
3299
- return new TexBeginEnd(new TexToken(3 /* LITERAL */, env_type), [], tex_data);
3300
- }
3301
- case "cases": {
3302
- const node = abstractNode;
3303
- const tex_data = node.matrix.map((row) => row.map(convert_typst_node_to_tex));
3304
- return new TexBeginEnd(new TexToken(3 /* LITERAL */, "cases"), [], tex_data);
3305
3344
  }
3306
3345
  case "fraction": {
3307
3346
  const node = abstractNode;
@@ -3376,10 +3415,10 @@ var rules_map2 = /* @__PURE__ */ new Map([
3376
3415
  new TypstToken(2 /* ELEMENT */, ")")
3377
3416
  ];
3378
3417
  }],
3379
- [String.raw`[a-zA-Z\.]+`, (s) => {
3418
+ [String.raw`#none`, (s) => new TypstToken(0 /* NONE */, s.text())],
3419
+ [String.raw`#?[a-zA-Z\.]+`, (s) => {
3380
3420
  return new TypstToken(s.text().length === 1 ? 2 /* ELEMENT */ : 1 /* SYMBOL */, s.text());
3381
3421
  }],
3382
- [String.raw`#none`, (s) => new TypstToken(0 /* NONE */, s.text())],
3383
3422
  [String.raw`.`, (s) => new TypstToken(2 /* ELEMENT */, s.text())]
3384
3423
  ]);
3385
3424
  var spec2 = {
@@ -3518,10 +3557,10 @@ function process_operators(nodes, parenthesis = false) {
3518
3557
  }
3519
3558
  let numerator = args.pop();
3520
3559
  if (denominator.type === "leftright") {
3521
- denominator = new TypstGroup(denominator.args);
3560
+ denominator = denominator.body;
3522
3561
  }
3523
3562
  if (numerator.type === "leftright") {
3524
- numerator = new TypstGroup(numerator.args);
3563
+ numerator = numerator.body;
3525
3564
  }
3526
3565
  args.push(new TypstFraction([numerator, denominator]));
3527
3566
  stack.pop();
@@ -3530,16 +3569,23 @@ function process_operators(nodes, parenthesis = false) {
3530
3569
  }
3531
3570
  }
3532
3571
  }
3572
+ const body = args.length === 1 ? args[0] : new TypstGroup(args);
3533
3573
  if (parenthesis) {
3534
- return new TypstLeftright(null, args, { left: LEFT_PARENTHESES, right: RIGHT_PARENTHESES });
3574
+ return new TypstLeftright(null, { body, left: LEFT_PARENTHESES, right: RIGHT_PARENTHESES });
3535
3575
  } else {
3536
- if (args.length === 1) {
3537
- return args[0];
3538
- } else {
3539
- return new TypstGroup(args);
3540
- }
3576
+ return body;
3541
3577
  }
3542
3578
  }
3579
+ function parse_named_params(groups) {
3580
+ const COLON = new TypstToken(2 /* ELEMENT */, ":").toNode();
3581
+ const np = {};
3582
+ for (const group of groups) {
3583
+ assert(group.items.length == 3);
3584
+ assert(group.items[1].eq(COLON));
3585
+ np[group.items[0].toString()] = new TypstTerminal(new TypstToken(3 /* LITERAL */, group.items[2].toString()));
3586
+ }
3587
+ return np;
3588
+ }
3543
3589
  var TypstParserError = class extends Error {
3544
3590
  constructor(message) {
3545
3591
  super(message);
@@ -3653,19 +3699,31 @@ var TypstParser = class {
3653
3699
  if (start + 1 < tokens.length && tokens[start + 1].eq(LEFT_PARENTHESES)) {
3654
3700
  if (firstToken.value === "mat") {
3655
3701
  const [matrix, named_params, newPos2] = this.parseMatrix(tokens, start + 1, SEMICOLON, COMMA);
3656
- const mat = new TypstMatrix(matrix);
3702
+ const mat = new TypstMatrixLike(firstToken, matrix);
3657
3703
  mat.setOptions(named_params);
3658
3704
  return [mat, newPos2];
3659
3705
  }
3660
3706
  if (firstToken.value === "cases") {
3661
3707
  const [cases, named_params, newPos2] = this.parseMatrix(tokens, start + 1, COMMA, CONTROL_AND);
3662
- const casesNode = new TypstCases(cases);
3708
+ const casesNode = new TypstMatrixLike(firstToken, cases);
3663
3709
  casesNode.setOptions(named_params);
3664
3710
  return [casesNode, newPos2];
3665
3711
  }
3666
3712
  if (firstToken.value === "lr") {
3667
3713
  return this.parseLrArguments(tokens, start + 1);
3668
3714
  }
3715
+ if (["#heading", "#text"].includes(firstToken.value)) {
3716
+ const [args2, newPos2] = this.parseArguments(tokens, start + 1);
3717
+ const named_params = parse_named_params(args2);
3718
+ assert(tokens[newPos2].eq(LEFT_BRACKET));
3719
+ const DOLLAR = new TypstToken(2 /* ELEMENT */, "$");
3720
+ const end = _find_closing_match(tokens, newPos2 + 1, [DOLLAR], [DOLLAR]);
3721
+ const [group, _] = this.parseGroup(tokens, newPos2 + 2, end);
3722
+ assert(tokens[end + 1].eq(RIGHT_BRACKET));
3723
+ const markup_func = new TypstMarkupFunc(firstToken, [group]);
3724
+ markup_func.setOptions(named_params);
3725
+ return [markup_func, end + 2];
3726
+ }
3669
3727
  const [args, newPos] = this.parseArguments(tokens, start + 1);
3670
3728
  const func_call = new TypstFuncCall(firstToken, args);
3671
3729
  return [func_call, newPos];
@@ -3687,13 +3745,13 @@ var TypstParser = class {
3687
3745
  const inner_end = find_closing_delim(tokens, inner_start);
3688
3746
  const inner_args = this.parseArgumentsWithSeparator(tokens, inner_start + 1, inner_end, COMMA);
3689
3747
  return [
3690
- new TypstLeftright(lr_token, inner_args, { left: tokens[inner_start], right: tokens[inner_end] }),
3748
+ new TypstLeftright(lr_token, { body: new TypstGroup(inner_args), left: tokens[inner_start], right: tokens[inner_end] }),
3691
3749
  end + 1
3692
3750
  ];
3693
3751
  } else {
3694
3752
  const [args, end] = this.parseArguments(tokens, start);
3695
3753
  return [
3696
- new TypstLeftright(lr_token, args, { left: null, right: null }),
3754
+ new TypstLeftright(lr_token, { body: new TypstGroup(args), left: null, right: null }),
3697
3755
  end
3698
3756
  ];
3699
3757
  }
@@ -3716,17 +3774,17 @@ var TypstParser = class {
3716
3774
  continue;
3717
3775
  }
3718
3776
  const g = arr[i];
3719
- const pos_colon = array_find(g.args, COLON);
3777
+ const pos_colon = array_find(g.items, COLON);
3720
3778
  if (pos_colon === -1 || pos_colon === 0) {
3721
3779
  continue;
3722
3780
  }
3723
3781
  to_delete.push(i);
3724
- const param_name = g.args[pos_colon - 1];
3782
+ const param_name = g.items[pos_colon - 1];
3725
3783
  if (param_name.eq(new TypstToken(1 /* SYMBOL */, "delim").toNode())) {
3726
- if (g.args.length !== 3) {
3784
+ if (g.items.length !== 3) {
3727
3785
  throw new TypstParserError("Invalid number of arguments for delim");
3728
3786
  }
3729
- np2["delim"] = g.args[pos_colon + 1];
3787
+ np2["delim"] = g.items[pos_colon + 1];
3730
3788
  } else {
3731
3789
  throw new TypstParserError("Not implemented for other named parameters");
3732
3790
  }