@drghaliasri/butex 5.5.4 → 5.6.0

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/react.js CHANGED
@@ -146,6 +146,81 @@ function cloneEditorSession(session) {
146
146
  debugLog: session.debugLog.slice()
147
147
  };
148
148
  }
149
+ function collectNodeIds(chain, ids) {
150
+ for (const node of chain.nodes) {
151
+ ids.push(node.id);
152
+ if (node.superscript) {
153
+ collectNodeIds(node.superscript, ids);
154
+ }
155
+ if (node.subscript) {
156
+ collectNodeIds(node.subscript, ids);
157
+ }
158
+ if (node.innerExpr) {
159
+ collectNodeIds(node.innerExpr, ids);
160
+ }
161
+ if (node.numerator) {
162
+ collectNodeIds(node.numerator, ids);
163
+ }
164
+ if (node.denominator) {
165
+ collectNodeIds(node.denominator, ids);
166
+ }
167
+ if (node.rootIndex) {
168
+ collectNodeIds(node.rootIndex, ids);
169
+ }
170
+ if (node.radicand) {
171
+ collectNodeIds(node.radicand, ids);
172
+ }
173
+ if (node.overExpr) {
174
+ collectNodeIds(node.overExpr, ids);
175
+ }
176
+ if (node.underExpr) {
177
+ collectNodeIds(node.underExpr, ids);
178
+ }
179
+ if (node.baseExpr) {
180
+ collectNodeIds(node.baseExpr, ids);
181
+ }
182
+ if (node.matrixRows) {
183
+ for (const row of node.matrixRows) {
184
+ for (const cell of row) {
185
+ collectNodeIds(cell, ids);
186
+ }
187
+ }
188
+ }
189
+ }
190
+ }
191
+ function buildInitialSyncMap(englishTree, arabicTree) {
192
+ const englishIds = [];
193
+ const arabicIds = [];
194
+ collectNodeIds(englishTree, englishIds);
195
+ collectNodeIds(arabicTree, arabicIds);
196
+ const all = /* @__PURE__ */ new Set([...englishIds, ...arabicIds]);
197
+ const sync = {};
198
+ for (const nodeId of all) {
199
+ sync[nodeId] = { expr: "synced" };
200
+ }
201
+ return sync;
202
+ }
203
+ function createEditorSession(englishTree, arabicTree, activeSide = "arabic") {
204
+ return {
205
+ englishTree,
206
+ arabicTree,
207
+ activeSide,
208
+ splitLeafTyping: false,
209
+ forceSplitNextLeaf: false,
210
+ arabicReverseNumberTyping: true,
211
+ currentCharacterFont: "default",
212
+ currentDigitForm: "western",
213
+ caret: {
214
+ chainId: englishTree.id,
215
+ index: 0
216
+ },
217
+ selection: null,
218
+ selectedNodeId: null,
219
+ selectedStructureNodeId: null,
220
+ sync: buildInitialSyncMap(englishTree, arabicTree),
221
+ debugLog: []
222
+ };
223
+ }
149
224
 
150
225
  // src/editor/accentCommands.ts
151
226
  var ACCENT_COMMANDS = {
@@ -284,6 +359,20 @@ function accentCommandSpec(id) {
284
359
  function accentCommandsList() {
285
360
  return Object.values(ACCENT_COMMANDS);
286
361
  }
362
+ function accentCommandIdFromTex(tex) {
363
+ const normalized = tex.startsWith("\\") ? tex : `\\${tex}`;
364
+ for (const spec of Object.values(ACCENT_COMMANDS)) {
365
+ if (spec.englishTex === normalized) {
366
+ return spec.id;
367
+ }
368
+ }
369
+ for (const spec of Object.values(ACCENT_COMMANDS)) {
370
+ if (spec.arabicTex === normalized) {
371
+ return spec.id;
372
+ }
373
+ }
374
+ return null;
375
+ }
287
376
 
288
377
  // src/editor/atomicCommands.ts
289
378
  var ATOMIC_COMMANDS = {
@@ -706,6 +795,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
706
795
  category: "operators1",
707
796
  Tex: "\\neq",
708
797
  mirror: true,
798
+ compileMirror: true,
709
799
  Label: "\u2260",
710
800
  title: "\u0644\u0627 \u064A\u0633\u0627\u0648\u064A",
711
801
  svg_path: null
@@ -745,6 +835,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
745
835
  Tex: "\\leq",
746
836
  mirror: true,
747
837
  displayMirror: false,
838
+ compileMirror: true,
748
839
  Label: "\u2264",
749
840
  title: "\u0623\u0635\u063A\u0631 \u0645\u0646 \u0623\u0648 \u064A\u0633\u0627\u0648\u064A",
750
841
  svg_path: null
@@ -755,6 +846,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
755
846
  Tex: "\\geq",
756
847
  mirror: true,
757
848
  displayMirror: false,
849
+ compileMirror: true,
758
850
  Label: "\u2265",
759
851
  title: "\u0623\u0643\u0628\u0631 \u0645\u0646 \u0623\u0648 \u064A\u0633\u0627\u0648\u064A",
760
852
  svg_path: null
@@ -765,6 +857,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
765
857
  Tex: "\\coloneqq",
766
858
  mirror: true,
767
859
  displayMirror: false,
860
+ compileMirror: true,
768
861
  Label: "\u2254",
769
862
  title: "\u064A\u0639\u0631\u0641 \u0628\u0623\u0646\u0647 \u064A\u0633\u0627\u0648\u064A",
770
863
  svg_path: null
@@ -775,6 +868,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
775
868
  Tex: "\\eqqcolon",
776
869
  mirror: true,
777
870
  displayMirror: false,
871
+ compileMirror: true,
778
872
  Label: "\u2255",
779
873
  title: "\u064A\u0633\u0627\u0648\u064A \u0628\u0627\u0644\u062A\u0639\u0631\u064A\u0641",
780
874
  svg_path: null
@@ -786,6 +880,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
786
880
  Tex: "\\propto",
787
881
  mirror: true,
788
882
  displayMirror: false,
883
+ compileMirror: true,
789
884
  Label: "\u221D",
790
885
  title: "\u064A\u062A\u0646\u0627\u0633\u0628 \u0645\u0639",
791
886
  svg_path: null
@@ -796,6 +891,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
796
891
  Tex: "\\in",
797
892
  mirror: true,
798
893
  displayMirror: false,
894
+ compileMirror: true,
799
895
  Label: "\u2208",
800
896
  title: "\u064A\u0646\u062A\u0645\u064A \u0625\u0644\u0649",
801
897
  svg_path: null
@@ -816,6 +912,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
816
912
  Tex: "\\subset",
817
913
  mirror: true,
818
914
  displayMirror: false,
915
+ compileMirror: true,
819
916
  Label: "\u2282",
820
917
  title: "\u0645\u062C\u0645\u0648\u0639\u0629 \u062C\u0632\u0626\u064A\u0629 \u0645\u0646",
821
918
  svg_path: null
@@ -826,6 +923,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
826
923
  Tex: "\\supset",
827
924
  mirror: true,
828
925
  displayMirror: false,
926
+ compileMirror: true,
829
927
  Label: "\u2283",
830
928
  title: "\u0645\u062C\u0645\u0648\u0639\u0629 \u0634\u0627\u0645\u0644\u0629 \u0644\u0640",
831
929
  svg_path: null
@@ -836,6 +934,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
836
934
  Tex: "\\subseteq",
837
935
  mirror: true,
838
936
  displayMirror: false,
937
+ compileMirror: true,
839
938
  Label: "\u2286",
840
939
  title: "\u0645\u062C\u0645\u0648\u0639\u0629 \u062C\u0632\u0626\u064A\u0629 \u0623\u0648 \u062A\u0633\u0627\u0648\u064A",
841
940
  svg_path: null
@@ -846,6 +945,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
846
945
  Tex: "\\supseteq",
847
946
  mirror: true,
848
947
  displayMirror: false,
948
+ compileMirror: true,
849
949
  Label: "\u2287",
850
950
  title: "\u0645\u062C\u0645\u0648\u0639\u0629 \u0634\u0627\u0645\u0644\u0629 \u0623\u0648 \u062A\u0633\u0627\u0648\u064A",
851
951
  svg_path: null
@@ -886,6 +986,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
886
986
  Tex: "\\nsubseteq",
887
987
  mirror: true,
888
988
  displayMirror: false,
989
+ compileMirror: true,
889
990
  Label: "\u2288",
890
991
  title: "\u0644\u064A\u0633\u062A \u0645\u062C\u0645\u0648\u0639\u0629 \u062C\u0632\u0626\u064A\u0629 \u0623\u0648 \u062A\u0633\u0627\u0648\u064A",
891
992
  svg_path: null
@@ -896,6 +997,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
896
997
  Tex: "\\nsupseteq",
897
998
  mirror: true,
898
999
  displayMirror: false,
1000
+ compileMirror: true,
899
1001
  Label: "\u2289",
900
1002
  title: "\u0644\u064A\u0633\u062A \u0645\u062C\u0645\u0648\u0639\u0629 \u0634\u0627\u0645\u0644\u0629 \u0623\u0648 \u062A\u0633\u0627\u0648\u064A",
901
1003
  svg_path: null
@@ -943,6 +1045,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
943
1045
  Tex: "\\ddots",
944
1046
  mirror: true,
945
1047
  displayMirror: false,
1048
+ compileMirror: true,
946
1049
  Label: "\u22F1",
947
1050
  title: "\u0646\u0642\u0627\u0637 \u0642\u0637\u0631\u064A\u0629",
948
1051
  svg_path: null
@@ -962,6 +1065,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
962
1065
  category: "operators3",
963
1066
  Tex: "\\implies",
964
1067
  mirror: true,
1068
+ compileMirror: true,
965
1069
  Label: "\u21D2",
966
1070
  title: "\u064A\u0633\u062A\u0644\u0632\u0645",
967
1071
  svg_path: null
@@ -971,6 +1075,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
971
1075
  category: "operators3",
972
1076
  Tex: "\\impliedby",
973
1077
  mirror: true,
1078
+ compileMirror: true,
974
1079
  Label: "\u21D0",
975
1080
  title: "\u0645\u0633\u062A\u0644\u0632\u0645 \u0645\u0646",
976
1081
  svg_path: null
@@ -989,6 +1094,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
989
1094
  category: "operators3",
990
1095
  Tex: "\\Longrightarrow",
991
1096
  mirror: true,
1097
+ compileMirror: true,
992
1098
  Label: "\u27F9",
993
1099
  title: "\u0633\u0647\u0645 \u0645\u0632\u062F\u0648\u062C \u0637\u0648\u064A\u0644 \u0625\u0644\u0649 \u0627\u0644\u064A\u0645\u064A\u0646",
994
1100
  svg_path: null
@@ -998,6 +1104,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
998
1104
  category: "operators3",
999
1105
  Tex: "\\Longleftarrow",
1000
1106
  mirror: true,
1107
+ compileMirror: true,
1001
1108
  Label: "\u27F8",
1002
1109
  title: "\u0633\u0647\u0645 \u0645\u0632\u062F\u0648\u062C \u0637\u0648\u064A\u0644 \u0625\u0644\u0649 \u0627\u0644\u064A\u0633\u0627\u0631",
1003
1110
  svg_path: null
@@ -1007,6 +1114,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
1007
1114
  category: "operators3",
1008
1115
  Tex: "\\to",
1009
1116
  mirror: true,
1117
+ compileMirror: true,
1010
1118
  Label: "\u2192",
1011
1119
  title: "\u064A\u0624\u0648\u0644 \u0625\u0644\u0649",
1012
1120
  svg_path: null
@@ -1016,6 +1124,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
1016
1124
  category: "operators3",
1017
1125
  Tex: "\\rightleftharpoons",
1018
1126
  mirror: true,
1127
+ compileMirror: true,
1019
1128
  Label: "\u21CC",
1020
1129
  title: "\u0627\u062A\u0632\u0627\u0646",
1021
1130
  svg_path: null
@@ -1025,6 +1134,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
1025
1134
  category: "operators3",
1026
1135
  Tex: "\\leftarrow",
1027
1136
  mirror: true,
1137
+ compileMirror: true,
1028
1138
  Label: "\u2190",
1029
1139
  title: "\u0633\u0647\u0645 \u0625\u0644\u0649 \u0627\u0644\u064A\u0633\u0627\u0631",
1030
1140
  svg_path: null
@@ -1034,6 +1144,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
1034
1144
  category: "operators3",
1035
1145
  Tex: "\\rightarrow",
1036
1146
  mirror: true,
1147
+ compileMirror: true,
1037
1148
  Label: "\u2192",
1038
1149
  title: "\u0633\u0647\u0645 \u0625\u0644\u0649 \u0627\u0644\u064A\u0645\u064A\u0646",
1039
1150
  svg_path: null
@@ -1043,6 +1154,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
1043
1154
  category: "operators3",
1044
1155
  Tex: "\\Leftarrow",
1045
1156
  mirror: true,
1157
+ compileMirror: true,
1046
1158
  Label: "\u21D0",
1047
1159
  title: "\u0633\u0647\u0645 \u0645\u0632\u062F\u0648\u062C \u0625\u0644\u0649 \u0627\u0644\u064A\u0633\u0627\u0631",
1048
1160
  svg_path: null
@@ -1052,6 +1164,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
1052
1164
  category: "operators3",
1053
1165
  Tex: "\\Rightarrow",
1054
1166
  mirror: true,
1167
+ compileMirror: true,
1055
1168
  Label: "\u21D2",
1056
1169
  title: "\u0633\u0647\u0645 \u0645\u0632\u062F\u0648\u062C \u0625\u0644\u0649 \u0627\u0644\u064A\u0645\u064A\u0646",
1057
1170
  svg_path: null
@@ -1061,6 +1174,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
1061
1174
  category: "operators3",
1062
1175
  Tex: "\\leftharpoonup",
1063
1176
  mirror: true,
1177
+ compileMirror: true,
1064
1178
  Label: "\u21BC",
1065
1179
  title: "\u0633\u0647\u0645 \u062E\u0637\u0627\u0641\u064A \u0639\u0644\u0648\u064A \u0625\u0644\u0649 \u0627\u0644\u064A\u0633\u0627\u0631",
1066
1180
  svg_path: null
@@ -1070,6 +1184,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
1070
1184
  category: "operators3",
1071
1185
  Tex: "\\rightharpoonup",
1072
1186
  mirror: true,
1187
+ compileMirror: true,
1073
1188
  Label: "\u21C0",
1074
1189
  title: "\u0633\u0647\u0645 \u062E\u0637\u0627\u0641\u064A \u0639\u0644\u0648\u064A \u0625\u0644\u0649 \u0627\u0644\u064A\u0645\u064A\u0646",
1075
1190
  svg_path: null
@@ -1079,6 +1194,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
1079
1194
  category: "operators3",
1080
1195
  Tex: "\\leftharpoondown",
1081
1196
  mirror: true,
1197
+ compileMirror: true,
1082
1198
  Label: "\u21BD",
1083
1199
  title: "\u0633\u0647\u0645 \u062E\u0637\u0627\u0641\u064A \u0633\u0641\u0644\u064A \u0625\u0644\u0649 \u0627\u0644\u064A\u0633\u0627\u0631",
1084
1200
  svg_path: null
@@ -1088,6 +1204,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
1088
1204
  category: "operators3",
1089
1205
  Tex: "\\rightharpoondown",
1090
1206
  mirror: true,
1207
+ compileMirror: true,
1091
1208
  Label: "\u21C1",
1092
1209
  title: "\u0633\u0647\u0645 \u062E\u0637\u0627\u0641\u064A \u0633\u0641\u0644\u064A \u0625\u0644\u0649 \u0627\u0644\u064A\u0645\u064A\u0646",
1093
1210
  svg_path: null
@@ -1098,6 +1215,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
1098
1215
  category: "integrals",
1099
1216
  Tex: "\\int",
1100
1217
  mirror: true,
1218
+ compileMirror: true,
1101
1219
  Label: "\u222B",
1102
1220
  title: "\u062A\u0643\u0627\u0645\u0644",
1103
1221
  svg_path: null
@@ -1108,6 +1226,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
1108
1226
  Tex: "\\iint",
1109
1227
  mirror: true,
1110
1228
  displayMirror: false,
1229
+ compileMirror: true,
1111
1230
  Label: "\u222C",
1112
1231
  title: "\u062A\u0643\u0627\u0645\u0644 \u0645\u0632\u062F\u0648\u062C",
1113
1232
  svg_path: null
@@ -1118,6 +1237,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
1118
1237
  Tex: "\\iiint",
1119
1238
  mirror: true,
1120
1239
  displayMirror: false,
1240
+ compileMirror: true,
1121
1241
  Label: "\u222D",
1122
1242
  title: "\u062A\u0643\u0627\u0645\u0644 \u062B\u0644\u0627\u062B\u064A",
1123
1243
  svg_path: null
@@ -1128,6 +1248,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
1128
1248
  Tex: "\\iiiint",
1129
1249
  mirror: true,
1130
1250
  displayMirror: false,
1251
+ compileMirror: true,
1131
1252
  Label: "\u2A0C",
1132
1253
  title: "\u062A\u0643\u0627\u0645\u0644 \u0631\u0628\u0627\u0639\u064A",
1133
1254
  svg_path: null
@@ -1138,6 +1259,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
1138
1259
  Tex: "\\oint",
1139
1260
  mirror: true,
1140
1261
  displayMirror: false,
1262
+ compileMirror: true,
1141
1263
  Label: "\u222E",
1142
1264
  title: "\u062A\u0643\u0627\u0645\u0644 \u0645\u063A\u0644\u0642",
1143
1265
  svg_path: null
@@ -1148,6 +1270,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
1148
1270
  Tex: "\\oiint",
1149
1271
  mirror: true,
1150
1272
  displayMirror: false,
1273
+ compileMirror: true,
1151
1274
  Label: "\u222F",
1152
1275
  title: "\u062A\u0643\u0627\u0645\u0644 \u0633\u0637\u062D\u064A \u0645\u063A\u0644\u0642",
1153
1276
  svg_path: null
@@ -1158,6 +1281,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
1158
1281
  Tex: "\\oiiint",
1159
1282
  mirror: true,
1160
1283
  displayMirror: false,
1284
+ compileMirror: true,
1161
1285
  Label: "\u2230",
1162
1286
  title: "\u062A\u0643\u0627\u0645\u0644 \u062D\u062C\u0645\u064A \u0645\u063A\u0644\u0642",
1163
1287
  svg_path: null
@@ -1167,8 +1291,9 @@ function shouldMirrorOperatorDisplay(command) {
1167
1291
  return command?.displayMirror ?? command?.mirror ?? false;
1168
1292
  }
1169
1293
  var MIRRORED_OPERATOR_TEX = Object.values(ATOMIC_OPERATOR_COMMANDS).filter((command) => command.mirror).map((command) => command.Tex).sort((a, b) => b.length - a.length);
1170
- function matchingMirroredOperator(tex, index) {
1171
- for (const operatorTex of MIRRORED_OPERATOR_TEX) {
1294
+ var COMPILE_MIRRORED_OPERATOR_TEX = Object.values(ATOMIC_OPERATOR_COMMANDS).filter((command) => "compileMirror" in command && command.compileMirror).map((command) => command.Tex).sort((a, b) => b.length - a.length);
1295
+ function matchingMirroredOperatorFrom(tex, index, operatorTexList) {
1296
+ for (const operatorTex of operatorTexList) {
1172
1297
  if (!tex.startsWith(operatorTex, index)) {
1173
1298
  continue;
1174
1299
  }
@@ -1199,19 +1324,26 @@ function findMatchingBrace(tex, openIndex) {
1199
1324
  }
1200
1325
  return -1;
1201
1326
  }
1202
- function mirrorBuTeXOperatorsInTex(tex) {
1327
+ function mirrorBuTeXOperatorsInTex(tex, options = {}) {
1328
+ const mirroredOperatorTex = options.target === "compile" ? COMPILE_MIRRORED_OPERATOR_TEX : MIRRORED_OPERATOR_TEX;
1203
1329
  let result = "";
1204
1330
  let index = 0;
1205
1331
  while (index < tex.length) {
1206
1332
  if (tex.startsWith("\\butexmirror{", index)) {
1207
1333
  const end = findMatchingBrace(tex, index + "\\butexmirror".length);
1208
1334
  if (end !== -1) {
1209
- result += tex.slice(index, end + 1);
1335
+ const bodyStart = index + "\\butexmirror{".length;
1336
+ const body = tex.slice(bodyStart, end);
1337
+ if (options.target === "compile" && MIRRORED_OPERATOR_TEX.includes(body) && !mirroredOperatorTex.includes(body)) {
1338
+ result += body;
1339
+ } else {
1340
+ result += tex.slice(index, end + 1);
1341
+ }
1210
1342
  index = end + 1;
1211
1343
  continue;
1212
1344
  }
1213
1345
  }
1214
- const operatorTex = matchingMirroredOperator(tex, index);
1346
+ const operatorTex = matchingMirroredOperatorFrom(tex, index, mirroredOperatorTex);
1215
1347
  if (operatorTex) {
1216
1348
  result += `\\butexmirror{${operatorTex}}`;
1217
1349
  index += operatorTex.length;
@@ -6884,6 +7016,11 @@ var WIDGET_CHROME_CSS = `
6884
7016
  font-family: ${BUTEX_DIWANI_OUTLINE_FONT_STACK};
6885
7017
  }
6886
7018
 
7019
+ .butex-widget .font-mode-btn--maghribi,
7020
+ .butex-widget .character-font-option--maghribi {
7021
+ font-family: ${BUTEX_MAGHRIBI_FONT_STACK};
7022
+ }
7023
+
6887
7024
  .butex-widget .character-font-options {
6888
7025
  position: absolute;
6889
7026
  z-index: 10;
@@ -7418,6 +7555,36 @@ var WIDGET_CHROME_CSS = `
7418
7555
  background: var(--butex-dev-inset-bg);
7419
7556
  color: var(--butex-dev-inset-fg);
7420
7557
  }
7558
+
7559
+ .butex-widget .debug-json-import {
7560
+ display: grid;
7561
+ gap: 8px;
7562
+ margin-bottom: 10px;
7563
+ }
7564
+
7565
+ .butex-widget .debug-json-import textarea {
7566
+ min-height: 150px;
7567
+ resize: vertical;
7568
+ border: 1px solid var(--butex-dev-inset-border);
7569
+ border-radius: 8px;
7570
+ padding: 8px 10px;
7571
+ direction: ltr;
7572
+ text-align: left;
7573
+ font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
7574
+ font-size: 11px;
7575
+ background: var(--butex-dev-inset-bg);
7576
+ color: var(--butex-dev-inset-fg);
7577
+ }
7578
+
7579
+ .butex-widget .debug-json-error {
7580
+ margin: 0;
7581
+ border: 1px solid rgba(220, 38, 38, 0.45);
7582
+ background: rgba(254, 226, 226, 0.9);
7583
+ color: #991b1b;
7584
+ border-radius: 8px;
7585
+ padding: 8px 10px;
7586
+ white-space: pre-wrap;
7587
+ }
7421
7588
  `.trim();
7422
7589
  var CHROME_STYLE_ID = "butex-widget-chrome-css";
7423
7590
  function injectWidgetChromeCss(doc) {
@@ -7436,6 +7603,1026 @@ function uiLocaleDirection(locale) {
7436
7603
  return locale === "ar" ? "rtl" : "ltr";
7437
7604
  }
7438
7605
 
7606
+ // src/ast/commands/index.ts
7607
+ function renderCommandCore(context) {
7608
+ const optional = context.optionalArgs.map((arg) => `[${context.renderChain(arg)}]`).join("");
7609
+ const mandatory = context.mandatoryArgs.map((arg) => `{${context.renderChain(arg)}}`).join("");
7610
+ return context.name + optional + mandatory;
7611
+ }
7612
+
7613
+ // src/ast/arabic_ast.ts
7614
+ var State = class {
7615
+ // Empty for now, like the Python State placeholder used by BaseNode.
7616
+ };
7617
+ var BaseAstNode = class {
7618
+ state;
7619
+ superscript;
7620
+ subscript;
7621
+ nodeType;
7622
+ constructor(state = null) {
7623
+ if (state === null) {
7624
+ state = new State();
7625
+ }
7626
+ this.state = state;
7627
+ this.superscript = null;
7628
+ this.subscript = null;
7629
+ this.nodeType = this.constructor.name;
7630
+ }
7631
+ latex() {
7632
+ throw new Error("NotImplementedError");
7633
+ }
7634
+ arabicLatex() {
7635
+ throw new Error("NotImplementedError");
7636
+ }
7637
+ toArabicLatex() {
7638
+ return this.arabicLatex();
7639
+ }
7640
+ /** Latin-style scripts: `^{...}` and `_{...}` for english_json rendering. */
7641
+ latexScripts() {
7642
+ let s = "";
7643
+ if (this.superscript !== null) {
7644
+ s += "^{" + this.superscript.latex() + "}";
7645
+ }
7646
+ if (this.subscript !== null) {
7647
+ s += "_{" + this.subscript.latex() + "}";
7648
+ }
7649
+ return s;
7650
+ }
7651
+ /**
7652
+ * Arabic scripts use \\prescript{sup}{sub}{content} (matches reference Python output).
7653
+ * Call with rendered strings for sup/sub chains (may be empty strings).
7654
+ */
7655
+ arabicLatexScripts(sup, sub, content) {
7656
+ if (sup !== "" || sub !== "") {
7657
+ return "\\prescript{" + sup + "}{" + sub + "}{" + content + "}";
7658
+ }
7659
+ return content;
7660
+ }
7661
+ };
7662
+ var ChainNode = class {
7663
+ chain;
7664
+ constructor(chain = []) {
7665
+ this.chain = chain;
7666
+ }
7667
+ latex() {
7668
+ return this.chain.map((node) => node.latex()).join(" ");
7669
+ }
7670
+ toEnglishLatex() {
7671
+ return this.chain.map((node) => node.latex()).join(" ");
7672
+ }
7673
+ /** RTL: reverse order relative to Latin chain (see test/ref_tests.txt). */
7674
+ arabicLatex() {
7675
+ const reversed = this.chain.slice().reverse();
7676
+ return reversed.map((node) => node.arabicLatex()).join(" ");
7677
+ }
7678
+ toArabicLatex() {
7679
+ return this.arabicLatex();
7680
+ }
7681
+ };
7682
+ var NumberNode = class extends BaseAstNode {
7683
+ expr;
7684
+ constructor(expr, state = null) {
7685
+ super(state);
7686
+ this.nodeType = "NumberObject";
7687
+ this.expr = expr;
7688
+ }
7689
+ latex() {
7690
+ return this.expr + this.latexScripts();
7691
+ }
7692
+ arabicLatex() {
7693
+ const sup = this.superscript !== null ? this.superscript.arabicLatex() : "";
7694
+ const sub = this.subscript !== null ? this.subscript.arabicLatex() : "";
7695
+ return this.arabicLatexScripts(sup, sub, this.expr);
7696
+ }
7697
+ };
7698
+ var CharNode = class extends BaseAstNode {
7699
+ expr;
7700
+ constructor(expr, state = null) {
7701
+ super(state);
7702
+ this.nodeType = "CharObject";
7703
+ this.expr = expr;
7704
+ }
7705
+ latex() {
7706
+ return this.expr + this.latexScripts();
7707
+ }
7708
+ arabicLatex() {
7709
+ const sup = this.superscript !== null ? this.superscript.arabicLatex() : "";
7710
+ const sub = this.subscript !== null ? this.subscript.arabicLatex() : "";
7711
+ return this.arabicLatexScripts(sup, sub, this.expr);
7712
+ }
7713
+ };
7714
+ var OperatorNode = class extends BaseAstNode {
7715
+ expr;
7716
+ constructor(expr, state = null) {
7717
+ super(state);
7718
+ this.nodeType = "OperatorObject";
7719
+ this.expr = expr;
7720
+ }
7721
+ latex() {
7722
+ return this.expr;
7723
+ }
7724
+ arabicLatex() {
7725
+ return this.expr;
7726
+ }
7727
+ };
7728
+ var DelimiterNode = class extends BaseAstNode {
7729
+ leftDelimExpr;
7730
+ innerExpr;
7731
+ rightDelimExpr;
7732
+ constructor(leftDelimExpr, innerExpr, rightDelimExpr, state = null) {
7733
+ super(state);
7734
+ this.nodeType = "DelimiterObject";
7735
+ this.leftDelimExpr = leftDelimExpr;
7736
+ this.innerExpr = innerExpr;
7737
+ this.rightDelimExpr = rightDelimExpr;
7738
+ }
7739
+ latex() {
7740
+ return this.leftDelimExpr + this.innerExpr.latex() + this.rightDelimExpr + this.latexScripts();
7741
+ }
7742
+ arabicLatex() {
7743
+ const sup = this.superscript !== null ? this.superscript.arabicLatex() : "";
7744
+ const sub = this.subscript !== null ? this.subscript.arabicLatex() : "";
7745
+ const content = this.leftDelimExpr + this.innerExpr.arabicLatex() + this.rightDelimExpr;
7746
+ return this.arabicLatexScripts(sup, sub, content);
7747
+ }
7748
+ };
7749
+ var CommandNode = class extends BaseAstNode {
7750
+ name;
7751
+ optionalArgs;
7752
+ mandatoryArgs;
7753
+ constructor(name, optionalArgs = [], mandatoryArgs = [], state = null) {
7754
+ super(state);
7755
+ this.nodeType = "CommandObject";
7756
+ this.name = name;
7757
+ this.optionalArgs = optionalArgs;
7758
+ this.mandatoryArgs = mandatoryArgs;
7759
+ }
7760
+ latex() {
7761
+ const content = renderCommandCore({
7762
+ name: this.name,
7763
+ optionalArgs: this.optionalArgs,
7764
+ mandatoryArgs: this.mandatoryArgs,
7765
+ renderChain: (chain) => chain.latex()
7766
+ });
7767
+ return content + this.latexScripts();
7768
+ }
7769
+ arabicLatex() {
7770
+ const sup = this.superscript !== null ? this.superscript.arabicLatex() : "";
7771
+ const sub = this.subscript !== null ? this.subscript.arabicLatex() : "";
7772
+ const arabicName = this.name === "\\sqrt" || this.name === "\\arabsqrt" ? "\\arsqrt" : this.name;
7773
+ const content = renderCommandCore({
7774
+ name: arabicName,
7775
+ optionalArgs: this.optionalArgs,
7776
+ mandatoryArgs: this.mandatoryArgs,
7777
+ renderChain: (chain) => chain.arabicLatex()
7778
+ });
7779
+ return this.arabicLatexScripts(sup, sub, content);
7780
+ }
7781
+ };
7782
+ var EnvNode = class extends BaseAstNode {
7783
+ opening;
7784
+ lines;
7785
+ closing;
7786
+ constructor(opening, lines = [], closing, state = null) {
7787
+ super(state);
7788
+ this.nodeType = "EnvObject";
7789
+ this.opening = opening;
7790
+ this.lines = lines;
7791
+ this.closing = closing;
7792
+ }
7793
+ latex() {
7794
+ if (this.lines.length === 0) {
7795
+ return this.opening + this.closing + this.latexScripts();
7796
+ }
7797
+ const formattedLines = this.lines.map((line) => ` ${line.latex()}`);
7798
+ const content = formattedLines.join(" \\\\\n");
7799
+ return `${this.opening}
7800
+ ${content}
7801
+ ${this.closing}` + this.latexScripts();
7802
+ }
7803
+ arabicLatex() {
7804
+ const sup = this.superscript !== null ? this.superscript.arabicLatex() : "";
7805
+ const sub = this.subscript !== null ? this.subscript.arabicLatex() : "";
7806
+ if (this.lines.length === 0) {
7807
+ return this.arabicLatexScripts(sup, sub, this.opening + this.closing);
7808
+ }
7809
+ const formattedLines = this.lines.map((line) => ` ${line.arabicLatex()}`);
7810
+ const content = formattedLines.join(" \\\\\n");
7811
+ const fullContent = `${this.opening}
7812
+ ${content}
7813
+ ${this.closing}`;
7814
+ return this.arabicLatexScripts(sup, sub, fullContent);
7815
+ }
7816
+ };
7817
+ function parseScripts(node, json, mode) {
7818
+ if (json.superscript) {
7819
+ node.superscript = parseChain(json.superscript, mode);
7820
+ }
7821
+ if (json.subscript) {
7822
+ node.subscript = parseChain(json.subscript, mode);
7823
+ }
7824
+ }
7825
+ function parseNode(json, mode) {
7826
+ if (json.node_type === "DelimiterObject") {
7827
+ const node2 = parseDelimiter(json, mode);
7828
+ parseScripts(node2, json, mode);
7829
+ return node2;
7830
+ }
7831
+ if (json.node_type === "OperatorObject") {
7832
+ if (typeof json.expr !== "string") {
7833
+ throw new Error("OperatorObject requires string expr");
7834
+ }
7835
+ const node2 = new OperatorNode(json.expr);
7836
+ parseScripts(node2, json, mode);
7837
+ return node2;
7838
+ }
7839
+ if (json.node_type === "CommandObject") {
7840
+ const node2 = parseCommand(json, mode);
7841
+ parseScripts(node2, json, mode);
7842
+ return node2;
7843
+ }
7844
+ if (json.node_type === "EnvObject") {
7845
+ const node2 = parseEnv(json, mode);
7846
+ parseScripts(node2, json, mode);
7847
+ return node2;
7848
+ }
7849
+ if (typeof json.expr !== "string") {
7850
+ throw new Error(`${json.node_type} requires string expr`);
7851
+ }
7852
+ let node;
7853
+ if (json.node_type === "NumberObject") {
7854
+ node = new NumberNode(json.expr);
7855
+ } else if (json.node_type === "CharObject") {
7856
+ node = new CharNode(json.expr);
7857
+ } else {
7858
+ throw new Error(`Unsupported node_type: ${json.node_type}`);
7859
+ }
7860
+ parseScripts(node, json, mode);
7861
+ return node;
7862
+ }
7863
+ function parseDelimiter(json, mode) {
7864
+ if (typeof json.left_delim_expr !== "string") {
7865
+ throw new Error("DelimiterObject requires string left_delim_expr");
7866
+ }
7867
+ if (!json.inner_expr || json.inner_expr.node_type !== "ChainClass") {
7868
+ throw new Error("DelimiterObject requires ChainClass inner_expr");
7869
+ }
7870
+ if (typeof json.right_delim_expr !== "string") {
7871
+ throw new Error("DelimiterObject requires string right_delim_expr");
7872
+ }
7873
+ const innerExpr = parseChain(json.inner_expr, mode);
7874
+ return new DelimiterNode(json.left_delim_expr, innerExpr, json.right_delim_expr);
7875
+ }
7876
+ function parseCommand(json, mode) {
7877
+ if (typeof json.name !== "string") {
7878
+ throw new Error("CommandObject requires string name");
7879
+ }
7880
+ const optionalJson = Array.isArray(json.optional_args) ? json.optional_args : [];
7881
+ const mandatoryJson = Array.isArray(json.mandatory_args) ? json.mandatory_args : [];
7882
+ const optionalArgs = optionalJson.map((arg) => parseChain(arg, mode));
7883
+ const mandatoryArgs = mandatoryJson.map((arg) => parseChain(arg, mode));
7884
+ return new CommandNode(json.name, optionalArgs, mandatoryArgs);
7885
+ }
7886
+ function parseEnv(json, mode) {
7887
+ if (typeof json.opening !== "string") {
7888
+ throw new Error("EnvObject requires string opening");
7889
+ }
7890
+ if (typeof json.closing !== "string") {
7891
+ throw new Error("EnvObject requires string closing");
7892
+ }
7893
+ if (!Array.isArray(json.lines)) {
7894
+ throw new Error("EnvObject requires lines array");
7895
+ }
7896
+ const lines = json.lines.map((line) => parseChain(line, mode));
7897
+ return new EnvNode(json.opening, lines, json.closing);
7898
+ }
7899
+ function parseChain(json, mode) {
7900
+ if (json.node_type !== "ChainClass") {
7901
+ throw new Error(`Expected ChainClass, got: ${json.node_type}`);
7902
+ }
7903
+ const nodes = json.chain.map((childJson) => parseNode(childJson, mode));
7904
+ return new ChainNode(nodes);
7905
+ }
7906
+ function fromEnglishJson(json) {
7907
+ if (json.node_type === "ChainClass") {
7908
+ return parseChain(json, "english");
7909
+ }
7910
+ return parseNode(json, "english");
7911
+ }
7912
+ function fromArabicJson(json) {
7913
+ if (json.node_type === "ChainClass") {
7914
+ return parseChain(json, "arabic");
7915
+ }
7916
+ return parseNode(json, "arabic");
7917
+ }
7918
+
7919
+ // src/document/mathObject.ts
7920
+ function isObject(value) {
7921
+ return typeof value === "object" && value !== null;
7922
+ }
7923
+ function isMathObjectJson(value) {
7924
+ return isObject(value) && value.node_type === "MathObject";
7925
+ }
7926
+ function assertChainNode(value) {
7927
+ if (!(value instanceof ChainNode)) {
7928
+ throw new Error("MathObject lines must parse to ChainNode");
7929
+ }
7930
+ return value;
7931
+ }
7932
+ function fromMathObjectJson(json, mode = "english") {
7933
+ if (!isMathObjectJson(json)) {
7934
+ throw new Error('MathObject requires node_type "MathObject"');
7935
+ }
7936
+ if (typeof json.math_mode !== "string") {
7937
+ throw new Error("MathObject requires string math_mode");
7938
+ }
7939
+ if (typeof json.closing !== "string") {
7940
+ throw new Error("MathObject requires string closing");
7941
+ }
7942
+ if (!Array.isArray(json.lines) || json.lines.length === 0) {
7943
+ throw new Error("MathObject requires non-empty lines array");
7944
+ }
7945
+ const parseChain2 = mode === "arabic" ? fromArabicJson : fromEnglishJson;
7946
+ const lines = json.lines.map((line) => assertChainNode(parseChain2(line)));
7947
+ return {
7948
+ nodeType: "MathObject",
7949
+ mathMode: json.math_mode,
7950
+ lines,
7951
+ closing: json.closing
7952
+ };
7953
+ }
7954
+ function scriptsToJson(node, path) {
7955
+ return {
7956
+ ...node.superscript ? { superscript: chainToJson(node.superscript, `${path}.superscript`) } : {},
7957
+ ...node.subscript ? { subscript: chainToJson(node.subscript, `${path}.subscript`) } : {}
7958
+ };
7959
+ }
7960
+ function nodeToJson(node, path) {
7961
+ const scripts = scriptsToJson(node, path);
7962
+ if (node instanceof CharNode || node instanceof NumberNode || node instanceof OperatorNode) {
7963
+ return { node_type: node.nodeType, expr: node.expr, ...scripts };
7964
+ }
7965
+ if (node instanceof DelimiterNode) {
7966
+ return {
7967
+ node_type: "DelimiterObject",
7968
+ left_delim_expr: node.leftDelimExpr,
7969
+ inner_expr: chainToJson(node.innerExpr, `${path}.inner_expr`),
7970
+ right_delim_expr: node.rightDelimExpr,
7971
+ ...scripts
7972
+ };
7973
+ }
7974
+ if (node instanceof CommandNode) {
7975
+ return {
7976
+ node_type: "CommandObject",
7977
+ name: node.name,
7978
+ optional_args: node.optionalArgs.map((arg, index) => chainToJson(arg, `${path}.optional_args[${String(index)}]`)),
7979
+ mandatory_args: node.mandatoryArgs.map((arg, index) => chainToJson(arg, `${path}.mandatory_args[${String(index)}]`)),
7980
+ ...scripts
7981
+ };
7982
+ }
7983
+ if (node instanceof EnvNode) {
7984
+ return {
7985
+ node_type: "EnvObject",
7986
+ opening: node.opening,
7987
+ lines: node.lines.map((line, index) => chainToJson(line, `${path}.lines[${String(index)}]`)),
7988
+ closing: node.closing,
7989
+ ...scripts
7990
+ };
7991
+ }
7992
+ throw new Error(`Unsupported runtime math node at ${path}: ${node.nodeType}`);
7993
+ }
7994
+ function chainToJson(chain, path) {
7995
+ if (!(chain instanceof ChainNode)) {
7996
+ throw new Error(`Expected live ChainNode at ${path}`);
7997
+ }
7998
+ return {
7999
+ node_type: "ChainClass",
8000
+ chain: chain.chain.map((node, index) => nodeToJson(node, `${path}.chain[${String(index)}]`))
8001
+ };
8002
+ }
8003
+ function toMathObjectJson(math) {
8004
+ if (math.nodeType !== "MathObject" || !Array.isArray(math.lines) || math.lines.length === 0) {
8005
+ throw new Error("Cannot serialize invalid runtime MathObject");
8006
+ }
8007
+ return {
8008
+ node_type: "MathObject",
8009
+ math_mode: math.mathMode,
8010
+ lines: math.lines.map((line, index) => chainToJson(line, `$.lines[${String(index)}]`)),
8011
+ closing: math.closing
8012
+ };
8013
+ }
8014
+
8015
+ // src/document/normalizeCharImport.ts
8016
+ var WRAPPER_SPECS = [
8017
+ ...NON_DEFAULT_CHARACTER_FONT_SPECS.flatMap((font) => [
8018
+ { prefix: font.macro, font: font.id },
8019
+ ...(font.importAliases ?? []).map((alias) => ({ prefix: alias, font: font.id }))
8020
+ ]),
8021
+ { prefix: "\\text" }
8022
+ ];
8023
+ function readBracedArg(source, openBraceIndex) {
8024
+ if (source[openBraceIndex] !== "{") {
8025
+ return null;
8026
+ }
8027
+ let depth = 0;
8028
+ for (let index = openBraceIndex; index < source.length; index += 1) {
8029
+ const char = source[index];
8030
+ if (char === "{") {
8031
+ depth += 1;
8032
+ } else if (char === "}") {
8033
+ depth -= 1;
8034
+ if (depth === 0) {
8035
+ return { inner: source.slice(openBraceIndex + 1, index), end: index + 1 };
8036
+ }
8037
+ }
8038
+ }
8039
+ return null;
8040
+ }
8041
+ function peelWrapper(source) {
8042
+ for (const wrapper of WRAPPER_SPECS) {
8043
+ if (!source.startsWith(wrapper.prefix)) {
8044
+ continue;
8045
+ }
8046
+ const braced = readBracedArg(source, wrapper.prefix.length);
8047
+ if (!braced || braced.end !== source.length) {
8048
+ continue;
8049
+ }
8050
+ return { inner: braced.inner, font: wrapper.font };
8051
+ }
8052
+ return null;
8053
+ }
8054
+ function normalizeCharImportExpr(expr) {
8055
+ const original = expr;
8056
+ let current = expr;
8057
+ let characterFont = "default";
8058
+ let changed = false;
8059
+ while (true) {
8060
+ const peeled = peelWrapper(current);
8061
+ if (!peeled) {
8062
+ break;
8063
+ }
8064
+ current = peeled.inner;
8065
+ if (peeled.font) {
8066
+ characterFont = peeled.font;
8067
+ }
8068
+ changed = true;
8069
+ }
8070
+ if (!changed) {
8071
+ return { expr: original, characterFont: "default" };
8072
+ }
8073
+ if (current.includes("\\")) {
8074
+ return { expr: original, characterFont: "default" };
8075
+ }
8076
+ return { expr: current, characterFont };
8077
+ }
8078
+
8079
+ // src/document/mathEditorAdapter.ts
8080
+ var COMMAND_FONT_MAP = Object.fromEntries([
8081
+ ["\\text", "infer"],
8082
+ ...NON_DEFAULT_CHARACTER_FONT_SPECS.flatMap((font) => [
8083
+ [font.macro, font.id],
8084
+ ...(font.importAliases ?? []).map((alias) => [alias, font.id])
8085
+ ])
8086
+ ]);
8087
+ var CHAR_FONT_EXPORT_TEX = Object.fromEntries(
8088
+ NON_DEFAULT_CHARACTER_FONT_SPECS.map((font) => [font.id, font.macro])
8089
+ );
8090
+ function charEditorNodeToAstNode(node) {
8091
+ const font = node.characterFont ?? "default";
8092
+ const charNode = new CharNode(node.expr);
8093
+ if (font === "default") {
8094
+ return charNode;
8095
+ }
8096
+ return new CommandNode(CHAR_FONT_EXPORT_TEX[font], [], [new ChainNode([charNode])]);
8097
+ }
8098
+ function commandIdForTex(tex) {
8099
+ for (const spec of Object.values(ATOMIC_COMMANDS)) {
8100
+ const renderTex = spec.arabicRenderTex;
8101
+ if (spec.englishTex === tex || spec.arabicTex === tex || renderTex === tex) {
8102
+ return spec.id;
8103
+ }
8104
+ }
8105
+ return null;
8106
+ }
8107
+ function operatorCommandIdForTex(tex) {
8108
+ for (const spec of Object.values(ATOMIC_OPERATOR_COMMANDS)) {
8109
+ if (spec.Tex === tex) {
8110
+ return spec.id;
8111
+ }
8112
+ }
8113
+ return null;
8114
+ }
8115
+ function cloneChainWithFreshIds(chain) {
8116
+ return structuredClone(chain);
8117
+ }
8118
+ function attachScripts(astNode, editorNode) {
8119
+ if (astNode.superscript) {
8120
+ const sup = chainToEditorChain(astNode.superscript);
8121
+ if (!sup.editable) {
8122
+ return sup;
8123
+ }
8124
+ editorNode.superscript = sup.chain;
8125
+ }
8126
+ if (astNode.subscript) {
8127
+ const sub = chainToEditorChain(astNode.subscript);
8128
+ if (!sub.editable) {
8129
+ return sub;
8130
+ }
8131
+ editorNode.subscript = sub.chain;
8132
+ }
8133
+ return null;
8134
+ }
8135
+ var MATRIX_ENV_NAMES = /* @__PURE__ */ new Set(["matrix", "pmatrix", "bmatrix", "Bmatrix", "vmatrix", "Vmatrix"]);
8136
+ function isGridEnvName(value) {
8137
+ return MATRIX_ENV_NAMES.has(value) || value === "array" || value === "aligned";
8138
+ }
8139
+ function isMatrixEnvName(value) {
8140
+ return MATRIX_ENV_NAMES.has(value);
8141
+ }
8142
+ function parseEnvOpening(opening) {
8143
+ const match = /^\\begin\{([^}]+)\}(?:\{([lcr]+)\})?$/.exec(opening);
8144
+ if (!match) {
8145
+ return null;
8146
+ }
8147
+ const envName = match[1] ?? "";
8148
+ if (!isGridEnvName(envName)) {
8149
+ return null;
8150
+ }
8151
+ if (envName === "array") {
8152
+ const spec = match[2] ?? "c";
8153
+ return {
8154
+ envName,
8155
+ alignments: spec.split("").map((alignment) => alignment)
8156
+ };
8157
+ }
8158
+ return { envName };
8159
+ }
8160
+ function splitEnvLine(line) {
8161
+ const cells = [];
8162
+ let current = [];
8163
+ for (const node of line.chain) {
8164
+ if (node instanceof OperatorNode && node.expr === "&") {
8165
+ cells.push(new ChainNode(current));
8166
+ current = [];
8167
+ } else {
8168
+ current.push(node);
8169
+ }
8170
+ }
8171
+ cells.push(new ChainNode(current));
8172
+ return cells;
8173
+ }
8174
+ function envToEditorNode(node) {
8175
+ const parsed = parseEnvOpening(node.opening);
8176
+ if (!parsed) {
8177
+ return { editable: false, reason: `\u0628\u064A\u0626\u0629 \u0631\u064A\u0627\u0636\u064A\u0629 \u063A\u064A\u0631 \u0645\u062F\u0639\u0648\u0645\u0629 \u062D\u0627\u0644\u064A\u0627: ${node.opening}` };
8178
+ }
8179
+ const expectedClosing = `\\end{${parsed.envName}}`;
8180
+ if (node.closing !== expectedClosing) {
8181
+ return { editable: false, reason: `\u0625\u063A\u0644\u0627\u0642 \u0628\u064A\u0626\u0629 \u0631\u064A\u0627\u0636\u064A\u0629 \u063A\u064A\u0631 \u0645\u062F\u0639\u0648\u0645 \u062D\u0627\u0644\u064A\u0627: ${node.closing}` };
8182
+ }
8183
+ const rowCells = node.lines.map(splitEnvLine);
8184
+ const rows = Math.max(1, rowCells.length);
8185
+ const columns = Math.max(1, ...rowCells.map((row) => row.length));
8186
+ const env = createGridEnvNode(parsed.envName, rows, columns, parsed.alignments);
8187
+ for (let rowIndex = 0; rowIndex < rows; rowIndex += 1) {
8188
+ const row = rowCells[rowIndex] ?? [];
8189
+ for (let columnIndex = 0; columnIndex < columns; columnIndex += 1) {
8190
+ const cell = chainToEditorChain(row[columnIndex] ?? new ChainNode());
8191
+ if (!cell.editable) {
8192
+ return cell;
8193
+ }
8194
+ env.matrixRows[rowIndex][columnIndex] = cell.chain;
8195
+ }
8196
+ }
8197
+ if (parsed.envName === "array") {
8198
+ env.columnAlignments = Array.from(
8199
+ { length: columns },
8200
+ (_, index) => parsed.alignments?.[index] ?? "c"
8201
+ );
8202
+ }
8203
+ if (isMatrixEnvName(parsed.envName)) {
8204
+ env.matrixStyle = parsed.envName;
8205
+ }
8206
+ const scripts = attachScripts(node, env);
8207
+ return scripts ?? { editable: true, node: env };
8208
+ }
8209
+ function charFromTextLikeCommand(node) {
8210
+ const commandFont = COMMAND_FONT_MAP[node.name];
8211
+ if (commandFont === void 0) {
8212
+ return null;
8213
+ }
8214
+ if (node.optionalArgs.length > 0 || node.mandatoryArgs.length !== 1) {
8215
+ return null;
8216
+ }
8217
+ const arg = node.mandatoryArgs[0];
8218
+ if (arg.chain.length !== 1) {
8219
+ return null;
8220
+ }
8221
+ const child = arg.chain[0];
8222
+ if (child instanceof CommandNode) {
8223
+ const nested = charFromTextLikeCommand(child);
8224
+ if (!nested?.editable) {
8225
+ return null;
8226
+ }
8227
+ if (commandFont !== "infer") {
8228
+ nested.node.characterFont = commandFont;
8229
+ }
8230
+ const scripts2 = attachScripts(node, nested.node);
8231
+ return scripts2 ?? nested;
8232
+ }
8233
+ if (!(child instanceof CharNode)) {
8234
+ return null;
8235
+ }
8236
+ const normalized = normalizeCharImportExpr(child.expr);
8237
+ const characterFont = commandFont === "infer" ? normalized.characterFont : commandFont;
8238
+ const editorNode = createEditorNode("char", normalized.expr);
8239
+ if (characterFont !== "default") {
8240
+ editorNode.characterFont = characterFont;
8241
+ }
8242
+ const scripts = attachScripts(node, editorNode);
8243
+ return scripts ?? { editable: true, node: editorNode };
8244
+ }
8245
+ function commandToEditorNode(node) {
8246
+ if (node.name === "\\frac" && node.mandatoryArgs.length >= 2) {
8247
+ const frac = createFracNode();
8248
+ const numerator = chainToEditorChain(node.mandatoryArgs[0]);
8249
+ const denominator = chainToEditorChain(node.mandatoryArgs[1]);
8250
+ if (!numerator.editable) {
8251
+ return numerator;
8252
+ }
8253
+ if (!denominator.editable) {
8254
+ return denominator;
8255
+ }
8256
+ frac.numerator = numerator.chain;
8257
+ frac.denominator = denominator.chain;
8258
+ const scripts = attachScripts(node, frac);
8259
+ if (scripts) {
8260
+ return scripts;
8261
+ }
8262
+ return { editable: true, node: frac };
8263
+ }
8264
+ if ((node.name === "\\sqrt" || node.name === "\\arabsqrt" || node.name === "\\arsqrt") && node.mandatoryArgs.length >= 1) {
8265
+ const sqrt = createSqrtNode();
8266
+ const radicand = chainToEditorChain(node.mandatoryArgs[0]);
8267
+ if (!radicand.editable) {
8268
+ return radicand;
8269
+ }
8270
+ sqrt.radicand = radicand.chain;
8271
+ if (node.optionalArgs[0]) {
8272
+ const rootIndex = chainToEditorChain(node.optionalArgs[0]);
8273
+ if (!rootIndex.editable) {
8274
+ return rootIndex;
8275
+ }
8276
+ sqrt.rootIndex = rootIndex.chain;
8277
+ }
8278
+ const scripts = attachScripts(node, sqrt);
8279
+ if (scripts) {
8280
+ return scripts;
8281
+ }
8282
+ return { editable: true, node: sqrt };
8283
+ }
8284
+ if (node.name === "\\overset" && node.mandatoryArgs.length >= 2) {
8285
+ const overset = createOversetNode();
8286
+ const overExpr = chainToEditorChain(node.mandatoryArgs[0]);
8287
+ const baseExpr = chainToEditorChain(node.mandatoryArgs[1]);
8288
+ if (!overExpr.editable) {
8289
+ return overExpr;
8290
+ }
8291
+ if (!baseExpr.editable) {
8292
+ return baseExpr;
8293
+ }
8294
+ overset.overExpr = overExpr.chain;
8295
+ overset.baseExpr = baseExpr.chain;
8296
+ const scripts = attachScripts(node, overset);
8297
+ if (scripts) {
8298
+ return scripts;
8299
+ }
8300
+ return { editable: true, node: overset };
8301
+ }
8302
+ if (node.name === "\\underset" && node.mandatoryArgs.length >= 2) {
8303
+ const underset = createUndersetNode();
8304
+ const underExpr = chainToEditorChain(node.mandatoryArgs[0]);
8305
+ const baseExpr = chainToEditorChain(node.mandatoryArgs[1]);
8306
+ if (!underExpr.editable) {
8307
+ return underExpr;
8308
+ }
8309
+ if (!baseExpr.editable) {
8310
+ return baseExpr;
8311
+ }
8312
+ underset.underExpr = underExpr.chain;
8313
+ underset.baseExpr = baseExpr.chain;
8314
+ const scripts = attachScripts(node, underset);
8315
+ if (scripts) {
8316
+ return scripts;
8317
+ }
8318
+ return { editable: true, node: underset };
8319
+ }
8320
+ const accentId = accentCommandIdFromTex(node.name);
8321
+ if (accentId && node.mandatoryArgs.length >= 1) {
8322
+ const accent = createAccentNode(accentId);
8323
+ const baseExpr = chainToEditorChain(node.mandatoryArgs[0]);
8324
+ if (!baseExpr.editable) {
8325
+ return baseExpr;
8326
+ }
8327
+ accent.baseExpr = baseExpr.chain;
8328
+ const scripts = attachScripts(node, accent);
8329
+ if (scripts) {
8330
+ return scripts;
8331
+ }
8332
+ return { editable: true, node: accent };
8333
+ }
8334
+ const atomicId = commandIdForTex(node.name);
8335
+ if (atomicId && node.optionalArgs.length === 0 && node.mandatoryArgs.length === 0) {
8336
+ const atomic = createEditorNode("atomicCommand", atomicId);
8337
+ const scripts = attachScripts(node, atomic);
8338
+ if (scripts) {
8339
+ return scripts;
8340
+ }
8341
+ return { editable: true, node: atomic };
8342
+ }
8343
+ const operatorId = operatorCommandIdForTex(node.name);
8344
+ if (operatorId && node.optionalArgs.length === 0 && node.mandatoryArgs.length === 0) {
8345
+ const atomicOperator = createEditorNode("atomicOperatorCommand", operatorId);
8346
+ const scripts = attachScripts(node, atomicOperator);
8347
+ if (scripts) {
8348
+ return scripts;
8349
+ }
8350
+ return { editable: true, node: atomicOperator };
8351
+ }
8352
+ const textLike = charFromTextLikeCommand(node);
8353
+ if (textLike) {
8354
+ return textLike;
8355
+ }
8356
+ return { editable: false, reason: `\u0623\u0645\u0631 \u0631\u064A\u0627\u0636\u064A \u063A\u064A\u0631 \u0645\u062F\u0639\u0648\u0645 \u062D\u0627\u0644\u064A\u0627: ${node.name}` };
8357
+ }
8358
+ function astNodeToEditorNode(node) {
8359
+ if (node instanceof CharNode) {
8360
+ const normalized = normalizeCharImportExpr(node.expr);
8361
+ const editorNode = createEditorNode("char", normalized.expr);
8362
+ if (normalized.characterFont !== "default") {
8363
+ editorNode.characterFont = normalized.characterFont;
8364
+ }
8365
+ const scripts = attachScripts(node, editorNode);
8366
+ return scripts ?? { editable: true, node: editorNode };
8367
+ }
8368
+ if (node instanceof NumberNode) {
8369
+ const editorNode = createEditorNode("number", node.expr);
8370
+ const scripts = attachScripts(node, editorNode);
8371
+ return scripts ?? { editable: true, node: editorNode };
8372
+ }
8373
+ if (node instanceof OperatorNode) {
8374
+ return { editable: true, node: createEditorNode("operator", node.expr) };
8375
+ }
8376
+ if (node instanceof DelimiterNode) {
8377
+ const editorNode = createDelimiterNode(node.leftDelimExpr, node.rightDelimExpr);
8378
+ const inner = chainToEditorChain(node.innerExpr);
8379
+ if (!inner.editable) {
8380
+ return inner;
8381
+ }
8382
+ editorNode.innerExpr = inner.chain;
8383
+ const scripts = attachScripts(node, editorNode);
8384
+ return scripts ?? { editable: true, node: editorNode };
8385
+ }
8386
+ if (node instanceof CommandNode) {
8387
+ return commandToEditorNode(node);
8388
+ }
8389
+ if (node instanceof EnvNode) {
8390
+ return envToEditorNode(node);
8391
+ }
8392
+ return { editable: false, reason: `\u0639\u0642\u062F\u0629 \u0631\u064A\u0627\u0636\u064A\u0629 \u063A\u064A\u0631 \u0645\u062F\u0639\u0648\u0645\u0629 \u062D\u0627\u0644\u064A\u0627: ${node.nodeType}` };
8393
+ }
8394
+ function chainToEditorChain(chain) {
8395
+ const nodes = [];
8396
+ for (const astNode of chain.chain) {
8397
+ const converted = astNodeToEditorNode(astNode);
8398
+ if (!converted.editable) {
8399
+ return converted;
8400
+ }
8401
+ nodes.push(converted.node);
8402
+ }
8403
+ return { editable: true, chain: createEditorChain(nodes) };
8404
+ }
8405
+ function mathObjectToEditorSession(math) {
8406
+ if (math.lines.length !== 1) {
8407
+ return { editable: false, reason: "\u062A\u062D\u0631\u064A\u0631 \u0627\u0644\u0645\u0639\u0627\u062F\u0644\u0627\u062A \u0645\u062A\u0639\u062F\u062F\u0629 \u0627\u0644\u0623\u0633\u0637\u0631 \u062F\u0627\u062E\u0644 \u0627\u0644\u0645\u0633\u062A\u0646\u062F \u063A\u064A\u0631 \u0645\u062F\u0639\u0648\u0645 \u0628\u0639\u062F" };
8408
+ }
8409
+ const english = chainToEditorChain(math.lines[0]);
8410
+ if (!english.editable) {
8411
+ return english;
8412
+ }
8413
+ const arabic = cloneChainWithFreshIds(english.chain);
8414
+ const session = createEditorSession(english.chain, arabic, "english");
8415
+ session.sync = buildInitialSyncMap(session.englishTree, session.arabicTree);
8416
+ return { editable: true, session: cloneEditorSession(session) };
8417
+ }
8418
+ function attachAstScripts(editorNode, astNode) {
8419
+ if (editorNode.superscript) {
8420
+ const sup = editorChainToAstChain(editorNode.superscript);
8421
+ if (!sup.ok) {
8422
+ return sup;
8423
+ }
8424
+ astNode.superscript = sup.chain;
8425
+ }
8426
+ if (editorNode.subscript) {
8427
+ const sub = editorChainToAstChain(editorNode.subscript);
8428
+ if (!sub.ok) {
8429
+ return sub;
8430
+ }
8431
+ astNode.subscript = sub.chain;
8432
+ }
8433
+ return null;
8434
+ }
8435
+ function editorEnvOpening(node) {
8436
+ const envName = node.envName ?? node.matrixStyle;
8437
+ if (!envName) {
8438
+ return null;
8439
+ }
8440
+ if (envName === "array") {
8441
+ const spec = (node.columnAlignments ?? []).slice(0, node.matrixRows?.[0]?.length ?? 0).join("") || "c";
8442
+ return { opening: `\\begin{array}{${spec}}`, closing: "\\end{array}" };
8443
+ }
8444
+ return { opening: `\\begin{${envName}}`, closing: `\\end{${envName}}` };
8445
+ }
8446
+ function editorEnvToAstNode(node) {
8447
+ const wrapper = editorEnvOpening(node);
8448
+ if (!wrapper || !node.matrixRows) {
8449
+ return { ok: false, reason: "\u0628\u064A\u0626\u0629 \u0631\u064A\u0627\u0636\u064A\u0629 \u063A\u064A\u0631 \u0645\u062F\u0639\u0648\u0645\u0629 \u062D\u0627\u0644\u064A\u0627" };
8450
+ }
8451
+ const lines = [];
8452
+ for (const row of node.matrixRows) {
8453
+ const lineNodes = [];
8454
+ for (let columnIndex = 0; columnIndex < row.length; columnIndex += 1) {
8455
+ if (columnIndex > 0) {
8456
+ lineNodes.push(new OperatorNode("&"));
8457
+ }
8458
+ const cell = editorChainToAstChain(row[columnIndex]);
8459
+ if (!cell.ok) {
8460
+ return cell;
8461
+ }
8462
+ lineNodes.push(...cell.chain.chain);
8463
+ }
8464
+ lines.push(new ChainNode(lineNodes));
8465
+ }
8466
+ const env = new EnvNode(wrapper.opening, lines, wrapper.closing);
8467
+ const scripts = attachAstScripts(node, env);
8468
+ return scripts ?? { ok: true, node: env };
8469
+ }
8470
+ function accentChainForExport(accent) {
8471
+ if (!accent) {
8472
+ return { ok: true, chain: new ChainNode([]) };
8473
+ }
8474
+ return editorChainToAstChain(accent);
8475
+ }
8476
+ function nodeFromAccentBase(command, accent, base) {
8477
+ if (accent.chain.length > 0) {
8478
+ return new CommandNode(command, [], [accent, base]);
8479
+ }
8480
+ if (base.chain.length === 1) {
8481
+ return base.chain[0];
8482
+ }
8483
+ return new DelimiterNode("", base, "");
8484
+ }
8485
+ function editorNodeToAstNode(node) {
8486
+ let astNode;
8487
+ if (node.kind === "char") {
8488
+ astNode = charEditorNodeToAstNode(node);
8489
+ } else if (node.kind === "number") {
8490
+ astNode = new NumberNode(node.expr);
8491
+ } else if (node.kind === "operator") {
8492
+ astNode = new OperatorNode(node.expr);
8493
+ } else if (node.kind === "delimiter" && node.innerExpr) {
8494
+ const inner = editorChainToAstChain(node.innerExpr);
8495
+ if (!inner.ok) {
8496
+ return inner;
8497
+ }
8498
+ astNode = new DelimiterNode(node.leftDelimExpr, inner.chain, node.rightDelimExpr);
8499
+ } else if (node.kind === "frac" && node.numerator && node.denominator) {
8500
+ const numerator = editorChainToAstChain(node.numerator);
8501
+ const denominator = editorChainToAstChain(node.denominator);
8502
+ if (!numerator.ok) {
8503
+ return numerator;
8504
+ }
8505
+ if (!denominator.ok) {
8506
+ return denominator;
8507
+ }
8508
+ astNode = new CommandNode("\\frac", [], [numerator.chain, denominator.chain]);
8509
+ } else if (node.kind === "sqrt" && node.radicand) {
8510
+ const radicand = editorChainToAstChain(node.radicand);
8511
+ if (!radicand.ok) {
8512
+ return radicand;
8513
+ }
8514
+ const optionalArgs = [];
8515
+ if (node.rootIndex && node.rootIndex.nodes.length > 0) {
8516
+ const rootIndex = editorChainToAstChain(node.rootIndex);
8517
+ if (!rootIndex.ok) {
8518
+ return rootIndex;
8519
+ }
8520
+ optionalArgs.push(rootIndex.chain);
8521
+ }
8522
+ astNode = new CommandNode("\\sqrt", optionalArgs, [radicand.chain]);
8523
+ } else if (node.kind === "overset" && node.baseExpr) {
8524
+ const accent = accentChainForExport(node.overExpr);
8525
+ if (!accent.ok) {
8526
+ return accent;
8527
+ }
8528
+ const baseExpr = editorChainToAstChain(node.baseExpr);
8529
+ if (!baseExpr.ok) {
8530
+ return baseExpr;
8531
+ }
8532
+ astNode = nodeFromAccentBase("\\overset", accent.chain, baseExpr.chain);
8533
+ } else if (node.kind === "underset" && node.baseExpr) {
8534
+ const accent = accentChainForExport(node.underExpr);
8535
+ if (!accent.ok) {
8536
+ return accent;
8537
+ }
8538
+ const baseExpr = editorChainToAstChain(node.baseExpr);
8539
+ if (!baseExpr.ok) {
8540
+ return baseExpr;
8541
+ }
8542
+ astNode = nodeFromAccentBase("\\underset", accent.chain, baseExpr.chain);
8543
+ } else if (node.kind === "accent" && node.baseExpr) {
8544
+ const tex = accentCommandSpec(node.expr)?.englishTex;
8545
+ if (!tex) {
8546
+ return { ok: false, reason: `\u0644\u0643\u0646\u0629 \u0631\u064A\u0627\u0636\u064A\u0629 \u063A\u064A\u0631 \u0645\u062F\u0639\u0648\u0645\u0629 \u062D\u0627\u0644\u064A\u0627: ${node.expr}` };
8547
+ }
8548
+ const baseExpr = editorChainToAstChain(node.baseExpr);
8549
+ if (!baseExpr.ok) {
8550
+ return baseExpr;
8551
+ }
8552
+ astNode = new CommandNode(tex, [], [baseExpr.chain]);
8553
+ } else if (node.kind === "atomicCommand") {
8554
+ const tex = atomicCommandSpec(node.expr)?.englishTex;
8555
+ if (!tex) {
8556
+ return { ok: false, reason: `\u0623\u0645\u0631 \u0631\u064A\u0627\u0636\u064A \u063A\u064A\u0631 \u0645\u062F\u0639\u0648\u0645 \u062D\u0627\u0644\u064A\u0627: ${node.expr}` };
8557
+ }
8558
+ astNode = new CommandNode(tex);
8559
+ } else if (node.kind === "atomicOperatorCommand") {
8560
+ const tex = atomicOperatorCommandSpec(node.expr)?.Tex;
8561
+ if (!tex) {
8562
+ return { ok: false, reason: `\u0631\u0645\u0632 \u0631\u064A\u0627\u0636\u064A \u063A\u064A\u0631 \u0645\u062F\u0639\u0648\u0645 \u062D\u0627\u0644\u064A\u0627: ${node.expr}` };
8563
+ }
8564
+ astNode = new CommandNode(tex);
8565
+ } else if (node.kind === "env") {
8566
+ return editorEnvToAstNode(node);
8567
+ } else {
8568
+ return { ok: false, reason: `\u0639\u0642\u062F\u0629 \u0631\u064A\u0627\u0636\u064A\u0629 \u063A\u064A\u0631 \u0645\u062F\u0639\u0648\u0645\u0629 \u062D\u0627\u0644\u064A\u0627: ${node.kind}` };
8569
+ }
8570
+ const scripts = attachAstScripts(node, astNode);
8571
+ return scripts ?? { ok: true, node: astNode };
8572
+ }
8573
+ function editorChainToAstChain(chain) {
8574
+ const nodes = [];
8575
+ for (const editorNode of chain.nodes) {
8576
+ const converted = editorNodeToAstNode(editorNode);
8577
+ if (!converted.ok) {
8578
+ return converted;
8579
+ }
8580
+ nodes.push(converted.node);
8581
+ }
8582
+ return { ok: true, chain: new ChainNode(nodes) };
8583
+ }
8584
+ function mathNodeFromEditorSession(session, mathMode = "$", closing = "$") {
8585
+ const converted = editorChainToAstChain(session.englishTree);
8586
+ if (!converted.ok) {
8587
+ return converted;
8588
+ }
8589
+ return {
8590
+ ok: true,
8591
+ math: {
8592
+ nodeType: "MathObject",
8593
+ mathMode,
8594
+ lines: [converted.chain],
8595
+ closing
8596
+ }
8597
+ };
8598
+ }
8599
+
8600
+ // src/debugJson.ts
8601
+ function lineColumnForPosition(text, position) {
8602
+ const before = text.slice(0, Math.max(0, position));
8603
+ const lines = before.split("\n");
8604
+ return {
8605
+ line: lines.length,
8606
+ column: (lines[lines.length - 1]?.length ?? 0) + 1
8607
+ };
8608
+ }
8609
+ function parseJsonDebugInput(text) {
8610
+ try {
8611
+ return { ok: true, value: JSON.parse(text) };
8612
+ } catch (error) {
8613
+ const message = error instanceof Error ? error.message : String(error);
8614
+ const positionMatch = message.match(/position\s+(\d+)/i);
8615
+ if (positionMatch) {
8616
+ const position = Number(positionMatch[1]);
8617
+ if (Number.isFinite(position)) {
8618
+ const location = lineColumnForPosition(text, position);
8619
+ return { ok: false, error: `Invalid JSON at line ${String(location.line)}, column ${String(location.column)}: ${message}` };
8620
+ }
8621
+ }
8622
+ return { ok: false, error: `Invalid JSON: ${message}` };
8623
+ }
8624
+ }
8625
+
7439
8626
  // src/react/ButexEditor.tsx
7440
8627
  var import_jsx_runtime = require("react/jsx-runtime");
7441
8628
  var DIGIT_FORM_OPTIONS = [
@@ -7551,6 +8738,7 @@ ${latex || messages.empty}`;
7551
8738
  var ButexEditor = (0, import_react.forwardRef)(
7552
8739
  function ButexEditor2({ className, debug = false, defaultSide, uiLocale = "ar", initialSession, onSessionChange }, ref) {
7553
8740
  const messages = equationEditorMessages(uiLocale);
8741
+ const debugEnabled = debug || typeof window !== "undefined" && new URLSearchParams(window.location.search).get("debug") === "1";
7554
8742
  const wrapperRef = (0, import_react.useRef)(null);
7555
8743
  const surfaceRef = (0, import_react.useRef)(null);
7556
8744
  const inputRef = (0, import_react.useRef)(null);
@@ -7568,7 +8756,7 @@ var ButexEditor = (0, import_react.forwardRef)(
7568
8756
  const digitFormBtnRef = (0, import_react.useRef)(null);
7569
8757
  const runtimeRef = (0, import_react.useRef)(null);
7570
8758
  const debugRef = (0, import_react.useRef)(debug);
7571
- debugRef.current = debug;
8759
+ debugRef.current = debugEnabled;
7572
8760
  const uiLocaleRef = (0, import_react.useRef)(uiLocale);
7573
8761
  uiLocaleRef.current = uiLocale;
7574
8762
  const onSessionChangeRef = (0, import_react.useRef)(onSessionChange);
@@ -7576,6 +8764,9 @@ var ButexEditor = (0, import_react.forwardRef)(
7576
8764
  const debugBodyHiddenRef = (0, import_react.useRef)(false);
7577
8765
  const [debugBodyHidden, setDebugBodyHidden] = (0, import_react.useState)(false);
7578
8766
  debugBodyHiddenRef.current = debugBodyHidden;
8767
+ const [debugImportOpen, setDebugImportOpen] = (0, import_react.useState)(false);
8768
+ const [debugImportText, setDebugImportText] = (0, import_react.useState)("");
8769
+ const [debugImportError, setDebugImportError] = (0, import_react.useState)("");
7579
8770
  const [digitMenuOpen, setDigitMenuOpen] = (0, import_react.useState)(false);
7580
8771
  const [characterFontMenuOpen, setCharacterFontMenuOpen] = (0, import_react.useState)(false);
7581
8772
  const [delimiterMenuOpen, setDelimiterMenuOpen] = (0, import_react.useState)(false);
@@ -7656,6 +8847,54 @@ ${arabic || currentMessages.empty}`;
7656
8847
  }, []);
7657
8848
  const updatePreviewRef = (0, import_react.useRef)(updatePreview);
7658
8849
  updatePreviewRef.current = updatePreview;
8850
+ async function copyDebugText(text) {
8851
+ try {
8852
+ await navigator.clipboard.writeText(text);
8853
+ } catch {
8854
+ }
8855
+ }
8856
+ function currentMathObjectJsonText() {
8857
+ const session = runtimeRef.current?.getSession();
8858
+ if (!session) {
8859
+ return "";
8860
+ }
8861
+ const math = mathNodeFromEditorSession(session);
8862
+ if (!math.ok) {
8863
+ return "";
8864
+ }
8865
+ return JSON.stringify(toMathObjectJson(math.math), null, 2);
8866
+ }
8867
+ function openDebugMathObjectImport() {
8868
+ setDebugImportText(currentMathObjectJsonText());
8869
+ setDebugImportError("");
8870
+ setDebugImportOpen(true);
8871
+ }
8872
+ function applyDebugMathObjectImport() {
8873
+ const parsed = parseJsonDebugInput(debugImportText);
8874
+ if (!parsed.ok) {
8875
+ setDebugImportError(parsed.error);
8876
+ return;
8877
+ }
8878
+ try {
8879
+ const math = fromMathObjectJson(parsed.value, activeEditorSide);
8880
+ const result = mathObjectToEditorSession(math);
8881
+ if (!result.editable) {
8882
+ setDebugImportError(result.reason);
8883
+ return;
8884
+ }
8885
+ const next = { ...result.session, activeSide: activeEditorSide };
8886
+ runtimeRef.current?.setSession(next);
8887
+ setSelectedDigitForm(next.currentDigitForm ?? "western");
8888
+ setSelectedCharacterFont(next.currentCharacterFont ?? "default");
8889
+ setActiveEditorSide(next.activeSide);
8890
+ onSessionChangeRef.current?.(next);
8891
+ setDebugImportError("");
8892
+ setDebugImportOpen(false);
8893
+ void updatePreviewRef.current(next);
8894
+ } catch (importError) {
8895
+ setDebugImportError(importError instanceof Error ? importError.message : String(importError));
8896
+ }
8897
+ }
7659
8898
  (0, import_react.useEffect)(() => {
7660
8899
  injectWidgetChromeCss();
7661
8900
  injectBuTeXEditorStyles(typeof document !== "undefined" ? document : void 0);
@@ -8816,7 +10055,7 @@ ${arabic || currentMessages.empty}`;
8816
10055
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { ref: renderErrorRef, className: "error", hidden: true })
8817
10056
  ] }),
8818
10057
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("section", { className: "panel panel-preview", "aria-label": messages.renderPreview, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { ref: mathOutputRef, className: "preview-box" }) }),
8819
- debug ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", { className: "panel", children: [
10058
+ debugEnabled ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", { className: "panel", children: [
8820
10059
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dev-strip", children: [
8821
10060
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dev-badge", children: messages.development }),
8822
10061
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { ref: latexLinesRef, className: "ascii" }),
@@ -8824,6 +10063,8 @@ ${arabic || currentMessages.empty}`;
8824
10063
  ] }),
8825
10064
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { marginTop: 12 }, children: [
8826
10065
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "debug-head", children: [
10066
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", onClick: () => void copyDebugText(currentMathObjectJsonText()), children: "Copy MathObject JSON" }),
10067
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", "aria-expanded": debugImportOpen, onClick: openDebugMathObjectImport, children: "Import MathObject JSON" }),
8827
10068
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
8828
10069
  "button",
8829
10070
  {
@@ -8859,6 +10100,35 @@ ${arabic || currentMessages.empty}`;
8859
10100
  }
8860
10101
  )
8861
10102
  ] }),
10103
+ debugImportOpen ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "debug-json-import", children: [
10104
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
10105
+ "textarea",
10106
+ {
10107
+ value: debugImportText,
10108
+ "aria-label": "Import MathObject JSON",
10109
+ spellCheck: false,
10110
+ onChange: (event) => {
10111
+ setDebugImportText(event.currentTarget.value);
10112
+ setDebugImportError("");
10113
+ }
10114
+ }
10115
+ ),
10116
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "debug-head", children: [
10117
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", onClick: applyDebugMathObjectImport, children: "Apply" }),
10118
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
10119
+ "button",
10120
+ {
10121
+ type: "button",
10122
+ onClick: () => {
10123
+ setDebugImportOpen(false);
10124
+ setDebugImportError("");
10125
+ },
10126
+ children: "Cancel"
10127
+ }
10128
+ )
10129
+ ] }),
10130
+ debugImportError ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "debug-json-error", children: debugImportError }) : null
10131
+ ] }) : null,
8862
10132
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { ref: debugLogRef, className: "debug-log" })
8863
10133
  ] })
8864
10134
  ] }) : null