@drghaliasri/butex 5.5.5 → 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.mjs CHANGED
@@ -127,6 +127,81 @@ function cloneEditorSession(session) {
127
127
  debugLog: session.debugLog.slice()
128
128
  };
129
129
  }
130
+ function collectNodeIds(chain, ids) {
131
+ for (const node of chain.nodes) {
132
+ ids.push(node.id);
133
+ if (node.superscript) {
134
+ collectNodeIds(node.superscript, ids);
135
+ }
136
+ if (node.subscript) {
137
+ collectNodeIds(node.subscript, ids);
138
+ }
139
+ if (node.innerExpr) {
140
+ collectNodeIds(node.innerExpr, ids);
141
+ }
142
+ if (node.numerator) {
143
+ collectNodeIds(node.numerator, ids);
144
+ }
145
+ if (node.denominator) {
146
+ collectNodeIds(node.denominator, ids);
147
+ }
148
+ if (node.rootIndex) {
149
+ collectNodeIds(node.rootIndex, ids);
150
+ }
151
+ if (node.radicand) {
152
+ collectNodeIds(node.radicand, ids);
153
+ }
154
+ if (node.overExpr) {
155
+ collectNodeIds(node.overExpr, ids);
156
+ }
157
+ if (node.underExpr) {
158
+ collectNodeIds(node.underExpr, ids);
159
+ }
160
+ if (node.baseExpr) {
161
+ collectNodeIds(node.baseExpr, ids);
162
+ }
163
+ if (node.matrixRows) {
164
+ for (const row of node.matrixRows) {
165
+ for (const cell of row) {
166
+ collectNodeIds(cell, ids);
167
+ }
168
+ }
169
+ }
170
+ }
171
+ }
172
+ function buildInitialSyncMap(englishTree, arabicTree) {
173
+ const englishIds = [];
174
+ const arabicIds = [];
175
+ collectNodeIds(englishTree, englishIds);
176
+ collectNodeIds(arabicTree, arabicIds);
177
+ const all = /* @__PURE__ */ new Set([...englishIds, ...arabicIds]);
178
+ const sync = {};
179
+ for (const nodeId of all) {
180
+ sync[nodeId] = { expr: "synced" };
181
+ }
182
+ return sync;
183
+ }
184
+ function createEditorSession(englishTree, arabicTree, activeSide = "arabic") {
185
+ return {
186
+ englishTree,
187
+ arabicTree,
188
+ activeSide,
189
+ splitLeafTyping: false,
190
+ forceSplitNextLeaf: false,
191
+ arabicReverseNumberTyping: true,
192
+ currentCharacterFont: "default",
193
+ currentDigitForm: "western",
194
+ caret: {
195
+ chainId: englishTree.id,
196
+ index: 0
197
+ },
198
+ selection: null,
199
+ selectedNodeId: null,
200
+ selectedStructureNodeId: null,
201
+ sync: buildInitialSyncMap(englishTree, arabicTree),
202
+ debugLog: []
203
+ };
204
+ }
130
205
 
131
206
  // src/editor/accentCommands.ts
132
207
  var ACCENT_COMMANDS = {
@@ -265,6 +340,20 @@ function accentCommandSpec(id) {
265
340
  function accentCommandsList() {
266
341
  return Object.values(ACCENT_COMMANDS);
267
342
  }
343
+ function accentCommandIdFromTex(tex) {
344
+ const normalized = tex.startsWith("\\") ? tex : `\\${tex}`;
345
+ for (const spec of Object.values(ACCENT_COMMANDS)) {
346
+ if (spec.englishTex === normalized) {
347
+ return spec.id;
348
+ }
349
+ }
350
+ for (const spec of Object.values(ACCENT_COMMANDS)) {
351
+ if (spec.arabicTex === normalized) {
352
+ return spec.id;
353
+ }
354
+ }
355
+ return null;
356
+ }
268
357
 
269
358
  // src/editor/atomicCommands.ts
270
359
  var ATOMIC_COMMANDS = {
@@ -687,6 +776,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
687
776
  category: "operators1",
688
777
  Tex: "\\neq",
689
778
  mirror: true,
779
+ compileMirror: true,
690
780
  Label: "\u2260",
691
781
  title: "\u0644\u0627 \u064A\u0633\u0627\u0648\u064A",
692
782
  svg_path: null
@@ -726,6 +816,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
726
816
  Tex: "\\leq",
727
817
  mirror: true,
728
818
  displayMirror: false,
819
+ compileMirror: true,
729
820
  Label: "\u2264",
730
821
  title: "\u0623\u0635\u063A\u0631 \u0645\u0646 \u0623\u0648 \u064A\u0633\u0627\u0648\u064A",
731
822
  svg_path: null
@@ -736,6 +827,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
736
827
  Tex: "\\geq",
737
828
  mirror: true,
738
829
  displayMirror: false,
830
+ compileMirror: true,
739
831
  Label: "\u2265",
740
832
  title: "\u0623\u0643\u0628\u0631 \u0645\u0646 \u0623\u0648 \u064A\u0633\u0627\u0648\u064A",
741
833
  svg_path: null
@@ -746,6 +838,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
746
838
  Tex: "\\coloneqq",
747
839
  mirror: true,
748
840
  displayMirror: false,
841
+ compileMirror: true,
749
842
  Label: "\u2254",
750
843
  title: "\u064A\u0639\u0631\u0641 \u0628\u0623\u0646\u0647 \u064A\u0633\u0627\u0648\u064A",
751
844
  svg_path: null
@@ -756,6 +849,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
756
849
  Tex: "\\eqqcolon",
757
850
  mirror: true,
758
851
  displayMirror: false,
852
+ compileMirror: true,
759
853
  Label: "\u2255",
760
854
  title: "\u064A\u0633\u0627\u0648\u064A \u0628\u0627\u0644\u062A\u0639\u0631\u064A\u0641",
761
855
  svg_path: null
@@ -767,6 +861,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
767
861
  Tex: "\\propto",
768
862
  mirror: true,
769
863
  displayMirror: false,
864
+ compileMirror: true,
770
865
  Label: "\u221D",
771
866
  title: "\u064A\u062A\u0646\u0627\u0633\u0628 \u0645\u0639",
772
867
  svg_path: null
@@ -777,6 +872,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
777
872
  Tex: "\\in",
778
873
  mirror: true,
779
874
  displayMirror: false,
875
+ compileMirror: true,
780
876
  Label: "\u2208",
781
877
  title: "\u064A\u0646\u062A\u0645\u064A \u0625\u0644\u0649",
782
878
  svg_path: null
@@ -797,6 +893,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
797
893
  Tex: "\\subset",
798
894
  mirror: true,
799
895
  displayMirror: false,
896
+ compileMirror: true,
800
897
  Label: "\u2282",
801
898
  title: "\u0645\u062C\u0645\u0648\u0639\u0629 \u062C\u0632\u0626\u064A\u0629 \u0645\u0646",
802
899
  svg_path: null
@@ -807,6 +904,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
807
904
  Tex: "\\supset",
808
905
  mirror: true,
809
906
  displayMirror: false,
907
+ compileMirror: true,
810
908
  Label: "\u2283",
811
909
  title: "\u0645\u062C\u0645\u0648\u0639\u0629 \u0634\u0627\u0645\u0644\u0629 \u0644\u0640",
812
910
  svg_path: null
@@ -817,6 +915,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
817
915
  Tex: "\\subseteq",
818
916
  mirror: true,
819
917
  displayMirror: false,
918
+ compileMirror: true,
820
919
  Label: "\u2286",
821
920
  title: "\u0645\u062C\u0645\u0648\u0639\u0629 \u062C\u0632\u0626\u064A\u0629 \u0623\u0648 \u062A\u0633\u0627\u0648\u064A",
822
921
  svg_path: null
@@ -827,6 +926,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
827
926
  Tex: "\\supseteq",
828
927
  mirror: true,
829
928
  displayMirror: false,
929
+ compileMirror: true,
830
930
  Label: "\u2287",
831
931
  title: "\u0645\u062C\u0645\u0648\u0639\u0629 \u0634\u0627\u0645\u0644\u0629 \u0623\u0648 \u062A\u0633\u0627\u0648\u064A",
832
932
  svg_path: null
@@ -867,6 +967,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
867
967
  Tex: "\\nsubseteq",
868
968
  mirror: true,
869
969
  displayMirror: false,
970
+ compileMirror: true,
870
971
  Label: "\u2288",
871
972
  title: "\u0644\u064A\u0633\u062A \u0645\u062C\u0645\u0648\u0639\u0629 \u062C\u0632\u0626\u064A\u0629 \u0623\u0648 \u062A\u0633\u0627\u0648\u064A",
872
973
  svg_path: null
@@ -877,6 +978,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
877
978
  Tex: "\\nsupseteq",
878
979
  mirror: true,
879
980
  displayMirror: false,
981
+ compileMirror: true,
880
982
  Label: "\u2289",
881
983
  title: "\u0644\u064A\u0633\u062A \u0645\u062C\u0645\u0648\u0639\u0629 \u0634\u0627\u0645\u0644\u0629 \u0623\u0648 \u062A\u0633\u0627\u0648\u064A",
882
984
  svg_path: null
@@ -924,6 +1026,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
924
1026
  Tex: "\\ddots",
925
1027
  mirror: true,
926
1028
  displayMirror: false,
1029
+ compileMirror: true,
927
1030
  Label: "\u22F1",
928
1031
  title: "\u0646\u0642\u0627\u0637 \u0642\u0637\u0631\u064A\u0629",
929
1032
  svg_path: null
@@ -943,6 +1046,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
943
1046
  category: "operators3",
944
1047
  Tex: "\\implies",
945
1048
  mirror: true,
1049
+ compileMirror: true,
946
1050
  Label: "\u21D2",
947
1051
  title: "\u064A\u0633\u062A\u0644\u0632\u0645",
948
1052
  svg_path: null
@@ -952,6 +1056,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
952
1056
  category: "operators3",
953
1057
  Tex: "\\impliedby",
954
1058
  mirror: true,
1059
+ compileMirror: true,
955
1060
  Label: "\u21D0",
956
1061
  title: "\u0645\u0633\u062A\u0644\u0632\u0645 \u0645\u0646",
957
1062
  svg_path: null
@@ -970,6 +1075,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
970
1075
  category: "operators3",
971
1076
  Tex: "\\Longrightarrow",
972
1077
  mirror: true,
1078
+ compileMirror: true,
973
1079
  Label: "\u27F9",
974
1080
  title: "\u0633\u0647\u0645 \u0645\u0632\u062F\u0648\u062C \u0637\u0648\u064A\u0644 \u0625\u0644\u0649 \u0627\u0644\u064A\u0645\u064A\u0646",
975
1081
  svg_path: null
@@ -979,6 +1085,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
979
1085
  category: "operators3",
980
1086
  Tex: "\\Longleftarrow",
981
1087
  mirror: true,
1088
+ compileMirror: true,
982
1089
  Label: "\u27F8",
983
1090
  title: "\u0633\u0647\u0645 \u0645\u0632\u062F\u0648\u062C \u0637\u0648\u064A\u0644 \u0625\u0644\u0649 \u0627\u0644\u064A\u0633\u0627\u0631",
984
1091
  svg_path: null
@@ -988,6 +1095,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
988
1095
  category: "operators3",
989
1096
  Tex: "\\to",
990
1097
  mirror: true,
1098
+ compileMirror: true,
991
1099
  Label: "\u2192",
992
1100
  title: "\u064A\u0624\u0648\u0644 \u0625\u0644\u0649",
993
1101
  svg_path: null
@@ -997,6 +1105,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
997
1105
  category: "operators3",
998
1106
  Tex: "\\rightleftharpoons",
999
1107
  mirror: true,
1108
+ compileMirror: true,
1000
1109
  Label: "\u21CC",
1001
1110
  title: "\u0627\u062A\u0632\u0627\u0646",
1002
1111
  svg_path: null
@@ -1006,6 +1115,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
1006
1115
  category: "operators3",
1007
1116
  Tex: "\\leftarrow",
1008
1117
  mirror: true,
1118
+ compileMirror: true,
1009
1119
  Label: "\u2190",
1010
1120
  title: "\u0633\u0647\u0645 \u0625\u0644\u0649 \u0627\u0644\u064A\u0633\u0627\u0631",
1011
1121
  svg_path: null
@@ -1015,6 +1125,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
1015
1125
  category: "operators3",
1016
1126
  Tex: "\\rightarrow",
1017
1127
  mirror: true,
1128
+ compileMirror: true,
1018
1129
  Label: "\u2192",
1019
1130
  title: "\u0633\u0647\u0645 \u0625\u0644\u0649 \u0627\u0644\u064A\u0645\u064A\u0646",
1020
1131
  svg_path: null
@@ -1024,6 +1135,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
1024
1135
  category: "operators3",
1025
1136
  Tex: "\\Leftarrow",
1026
1137
  mirror: true,
1138
+ compileMirror: true,
1027
1139
  Label: "\u21D0",
1028
1140
  title: "\u0633\u0647\u0645 \u0645\u0632\u062F\u0648\u062C \u0625\u0644\u0649 \u0627\u0644\u064A\u0633\u0627\u0631",
1029
1141
  svg_path: null
@@ -1033,6 +1145,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
1033
1145
  category: "operators3",
1034
1146
  Tex: "\\Rightarrow",
1035
1147
  mirror: true,
1148
+ compileMirror: true,
1036
1149
  Label: "\u21D2",
1037
1150
  title: "\u0633\u0647\u0645 \u0645\u0632\u062F\u0648\u062C \u0625\u0644\u0649 \u0627\u0644\u064A\u0645\u064A\u0646",
1038
1151
  svg_path: null
@@ -1042,6 +1155,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
1042
1155
  category: "operators3",
1043
1156
  Tex: "\\leftharpoonup",
1044
1157
  mirror: true,
1158
+ compileMirror: true,
1045
1159
  Label: "\u21BC",
1046
1160
  title: "\u0633\u0647\u0645 \u062E\u0637\u0627\u0641\u064A \u0639\u0644\u0648\u064A \u0625\u0644\u0649 \u0627\u0644\u064A\u0633\u0627\u0631",
1047
1161
  svg_path: null
@@ -1051,6 +1165,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
1051
1165
  category: "operators3",
1052
1166
  Tex: "\\rightharpoonup",
1053
1167
  mirror: true,
1168
+ compileMirror: true,
1054
1169
  Label: "\u21C0",
1055
1170
  title: "\u0633\u0647\u0645 \u062E\u0637\u0627\u0641\u064A \u0639\u0644\u0648\u064A \u0625\u0644\u0649 \u0627\u0644\u064A\u0645\u064A\u0646",
1056
1171
  svg_path: null
@@ -1060,6 +1175,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
1060
1175
  category: "operators3",
1061
1176
  Tex: "\\leftharpoondown",
1062
1177
  mirror: true,
1178
+ compileMirror: true,
1063
1179
  Label: "\u21BD",
1064
1180
  title: "\u0633\u0647\u0645 \u062E\u0637\u0627\u0641\u064A \u0633\u0641\u0644\u064A \u0625\u0644\u0649 \u0627\u0644\u064A\u0633\u0627\u0631",
1065
1181
  svg_path: null
@@ -1069,6 +1185,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
1069
1185
  category: "operators3",
1070
1186
  Tex: "\\rightharpoondown",
1071
1187
  mirror: true,
1188
+ compileMirror: true,
1072
1189
  Label: "\u21C1",
1073
1190
  title: "\u0633\u0647\u0645 \u062E\u0637\u0627\u0641\u064A \u0633\u0641\u0644\u064A \u0625\u0644\u0649 \u0627\u0644\u064A\u0645\u064A\u0646",
1074
1191
  svg_path: null
@@ -1079,6 +1196,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
1079
1196
  category: "integrals",
1080
1197
  Tex: "\\int",
1081
1198
  mirror: true,
1199
+ compileMirror: true,
1082
1200
  Label: "\u222B",
1083
1201
  title: "\u062A\u0643\u0627\u0645\u0644",
1084
1202
  svg_path: null
@@ -1089,6 +1207,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
1089
1207
  Tex: "\\iint",
1090
1208
  mirror: true,
1091
1209
  displayMirror: false,
1210
+ compileMirror: true,
1092
1211
  Label: "\u222C",
1093
1212
  title: "\u062A\u0643\u0627\u0645\u0644 \u0645\u0632\u062F\u0648\u062C",
1094
1213
  svg_path: null
@@ -1099,6 +1218,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
1099
1218
  Tex: "\\iiint",
1100
1219
  mirror: true,
1101
1220
  displayMirror: false,
1221
+ compileMirror: true,
1102
1222
  Label: "\u222D",
1103
1223
  title: "\u062A\u0643\u0627\u0645\u0644 \u062B\u0644\u0627\u062B\u064A",
1104
1224
  svg_path: null
@@ -1109,6 +1229,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
1109
1229
  Tex: "\\iiiint",
1110
1230
  mirror: true,
1111
1231
  displayMirror: false,
1232
+ compileMirror: true,
1112
1233
  Label: "\u2A0C",
1113
1234
  title: "\u062A\u0643\u0627\u0645\u0644 \u0631\u0628\u0627\u0639\u064A",
1114
1235
  svg_path: null
@@ -1119,6 +1240,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
1119
1240
  Tex: "\\oint",
1120
1241
  mirror: true,
1121
1242
  displayMirror: false,
1243
+ compileMirror: true,
1122
1244
  Label: "\u222E",
1123
1245
  title: "\u062A\u0643\u0627\u0645\u0644 \u0645\u063A\u0644\u0642",
1124
1246
  svg_path: null
@@ -1129,6 +1251,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
1129
1251
  Tex: "\\oiint",
1130
1252
  mirror: true,
1131
1253
  displayMirror: false,
1254
+ compileMirror: true,
1132
1255
  Label: "\u222F",
1133
1256
  title: "\u062A\u0643\u0627\u0645\u0644 \u0633\u0637\u062D\u064A \u0645\u063A\u0644\u0642",
1134
1257
  svg_path: null
@@ -1139,6 +1262,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
1139
1262
  Tex: "\\oiiint",
1140
1263
  mirror: true,
1141
1264
  displayMirror: false,
1265
+ compileMirror: true,
1142
1266
  Label: "\u2230",
1143
1267
  title: "\u062A\u0643\u0627\u0645\u0644 \u062D\u062C\u0645\u064A \u0645\u063A\u0644\u0642",
1144
1268
  svg_path: null
@@ -1148,8 +1272,9 @@ function shouldMirrorOperatorDisplay(command) {
1148
1272
  return command?.displayMirror ?? command?.mirror ?? false;
1149
1273
  }
1150
1274
  var MIRRORED_OPERATOR_TEX = Object.values(ATOMIC_OPERATOR_COMMANDS).filter((command) => command.mirror).map((command) => command.Tex).sort((a, b) => b.length - a.length);
1151
- function matchingMirroredOperator(tex, index) {
1152
- for (const operatorTex of MIRRORED_OPERATOR_TEX) {
1275
+ 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);
1276
+ function matchingMirroredOperatorFrom(tex, index, operatorTexList) {
1277
+ for (const operatorTex of operatorTexList) {
1153
1278
  if (!tex.startsWith(operatorTex, index)) {
1154
1279
  continue;
1155
1280
  }
@@ -1180,19 +1305,26 @@ function findMatchingBrace(tex, openIndex) {
1180
1305
  }
1181
1306
  return -1;
1182
1307
  }
1183
- function mirrorBuTeXOperatorsInTex(tex) {
1308
+ function mirrorBuTeXOperatorsInTex(tex, options = {}) {
1309
+ const mirroredOperatorTex = options.target === "compile" ? COMPILE_MIRRORED_OPERATOR_TEX : MIRRORED_OPERATOR_TEX;
1184
1310
  let result = "";
1185
1311
  let index = 0;
1186
1312
  while (index < tex.length) {
1187
1313
  if (tex.startsWith("\\butexmirror{", index)) {
1188
1314
  const end = findMatchingBrace(tex, index + "\\butexmirror".length);
1189
1315
  if (end !== -1) {
1190
- result += tex.slice(index, end + 1);
1316
+ const bodyStart = index + "\\butexmirror{".length;
1317
+ const body = tex.slice(bodyStart, end);
1318
+ if (options.target === "compile" && MIRRORED_OPERATOR_TEX.includes(body) && !mirroredOperatorTex.includes(body)) {
1319
+ result += body;
1320
+ } else {
1321
+ result += tex.slice(index, end + 1);
1322
+ }
1191
1323
  index = end + 1;
1192
1324
  continue;
1193
1325
  }
1194
1326
  }
1195
- const operatorTex = matchingMirroredOperator(tex, index);
1327
+ const operatorTex = matchingMirroredOperatorFrom(tex, index, mirroredOperatorTex);
1196
1328
  if (operatorTex) {
1197
1329
  result += `\\butexmirror{${operatorTex}}`;
1198
1330
  index += operatorTex.length;
@@ -6865,6 +6997,11 @@ var WIDGET_CHROME_CSS = `
6865
6997
  font-family: ${BUTEX_DIWANI_OUTLINE_FONT_STACK};
6866
6998
  }
6867
6999
 
7000
+ .butex-widget .font-mode-btn--maghribi,
7001
+ .butex-widget .character-font-option--maghribi {
7002
+ font-family: ${BUTEX_MAGHRIBI_FONT_STACK};
7003
+ }
7004
+
6868
7005
  .butex-widget .character-font-options {
6869
7006
  position: absolute;
6870
7007
  z-index: 10;
@@ -7399,6 +7536,36 @@ var WIDGET_CHROME_CSS = `
7399
7536
  background: var(--butex-dev-inset-bg);
7400
7537
  color: var(--butex-dev-inset-fg);
7401
7538
  }
7539
+
7540
+ .butex-widget .debug-json-import {
7541
+ display: grid;
7542
+ gap: 8px;
7543
+ margin-bottom: 10px;
7544
+ }
7545
+
7546
+ .butex-widget .debug-json-import textarea {
7547
+ min-height: 150px;
7548
+ resize: vertical;
7549
+ border: 1px solid var(--butex-dev-inset-border);
7550
+ border-radius: 8px;
7551
+ padding: 8px 10px;
7552
+ direction: ltr;
7553
+ text-align: left;
7554
+ font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
7555
+ font-size: 11px;
7556
+ background: var(--butex-dev-inset-bg);
7557
+ color: var(--butex-dev-inset-fg);
7558
+ }
7559
+
7560
+ .butex-widget .debug-json-error {
7561
+ margin: 0;
7562
+ border: 1px solid rgba(220, 38, 38, 0.45);
7563
+ background: rgba(254, 226, 226, 0.9);
7564
+ color: #991b1b;
7565
+ border-radius: 8px;
7566
+ padding: 8px 10px;
7567
+ white-space: pre-wrap;
7568
+ }
7402
7569
  `.trim();
7403
7570
  var CHROME_STYLE_ID = "butex-widget-chrome-css";
7404
7571
  function injectWidgetChromeCss(doc) {
@@ -7417,6 +7584,1026 @@ function uiLocaleDirection(locale) {
7417
7584
  return locale === "ar" ? "rtl" : "ltr";
7418
7585
  }
7419
7586
 
7587
+ // src/ast/commands/index.ts
7588
+ function renderCommandCore(context) {
7589
+ const optional = context.optionalArgs.map((arg) => `[${context.renderChain(arg)}]`).join("");
7590
+ const mandatory = context.mandatoryArgs.map((arg) => `{${context.renderChain(arg)}}`).join("");
7591
+ return context.name + optional + mandatory;
7592
+ }
7593
+
7594
+ // src/ast/arabic_ast.ts
7595
+ var State = class {
7596
+ // Empty for now, like the Python State placeholder used by BaseNode.
7597
+ };
7598
+ var BaseAstNode = class {
7599
+ state;
7600
+ superscript;
7601
+ subscript;
7602
+ nodeType;
7603
+ constructor(state = null) {
7604
+ if (state === null) {
7605
+ state = new State();
7606
+ }
7607
+ this.state = state;
7608
+ this.superscript = null;
7609
+ this.subscript = null;
7610
+ this.nodeType = this.constructor.name;
7611
+ }
7612
+ latex() {
7613
+ throw new Error("NotImplementedError");
7614
+ }
7615
+ arabicLatex() {
7616
+ throw new Error("NotImplementedError");
7617
+ }
7618
+ toArabicLatex() {
7619
+ return this.arabicLatex();
7620
+ }
7621
+ /** Latin-style scripts: `^{...}` and `_{...}` for english_json rendering. */
7622
+ latexScripts() {
7623
+ let s = "";
7624
+ if (this.superscript !== null) {
7625
+ s += "^{" + this.superscript.latex() + "}";
7626
+ }
7627
+ if (this.subscript !== null) {
7628
+ s += "_{" + this.subscript.latex() + "}";
7629
+ }
7630
+ return s;
7631
+ }
7632
+ /**
7633
+ * Arabic scripts use \\prescript{sup}{sub}{content} (matches reference Python output).
7634
+ * Call with rendered strings for sup/sub chains (may be empty strings).
7635
+ */
7636
+ arabicLatexScripts(sup, sub, content) {
7637
+ if (sup !== "" || sub !== "") {
7638
+ return "\\prescript{" + sup + "}{" + sub + "}{" + content + "}";
7639
+ }
7640
+ return content;
7641
+ }
7642
+ };
7643
+ var ChainNode = class {
7644
+ chain;
7645
+ constructor(chain = []) {
7646
+ this.chain = chain;
7647
+ }
7648
+ latex() {
7649
+ return this.chain.map((node) => node.latex()).join(" ");
7650
+ }
7651
+ toEnglishLatex() {
7652
+ return this.chain.map((node) => node.latex()).join(" ");
7653
+ }
7654
+ /** RTL: reverse order relative to Latin chain (see test/ref_tests.txt). */
7655
+ arabicLatex() {
7656
+ const reversed = this.chain.slice().reverse();
7657
+ return reversed.map((node) => node.arabicLatex()).join(" ");
7658
+ }
7659
+ toArabicLatex() {
7660
+ return this.arabicLatex();
7661
+ }
7662
+ };
7663
+ var NumberNode = class extends BaseAstNode {
7664
+ expr;
7665
+ constructor(expr, state = null) {
7666
+ super(state);
7667
+ this.nodeType = "NumberObject";
7668
+ this.expr = expr;
7669
+ }
7670
+ latex() {
7671
+ return this.expr + this.latexScripts();
7672
+ }
7673
+ arabicLatex() {
7674
+ const sup = this.superscript !== null ? this.superscript.arabicLatex() : "";
7675
+ const sub = this.subscript !== null ? this.subscript.arabicLatex() : "";
7676
+ return this.arabicLatexScripts(sup, sub, this.expr);
7677
+ }
7678
+ };
7679
+ var CharNode = class extends BaseAstNode {
7680
+ expr;
7681
+ constructor(expr, state = null) {
7682
+ super(state);
7683
+ this.nodeType = "CharObject";
7684
+ this.expr = expr;
7685
+ }
7686
+ latex() {
7687
+ return this.expr + this.latexScripts();
7688
+ }
7689
+ arabicLatex() {
7690
+ const sup = this.superscript !== null ? this.superscript.arabicLatex() : "";
7691
+ const sub = this.subscript !== null ? this.subscript.arabicLatex() : "";
7692
+ return this.arabicLatexScripts(sup, sub, this.expr);
7693
+ }
7694
+ };
7695
+ var OperatorNode = class extends BaseAstNode {
7696
+ expr;
7697
+ constructor(expr, state = null) {
7698
+ super(state);
7699
+ this.nodeType = "OperatorObject";
7700
+ this.expr = expr;
7701
+ }
7702
+ latex() {
7703
+ return this.expr;
7704
+ }
7705
+ arabicLatex() {
7706
+ return this.expr;
7707
+ }
7708
+ };
7709
+ var DelimiterNode = class extends BaseAstNode {
7710
+ leftDelimExpr;
7711
+ innerExpr;
7712
+ rightDelimExpr;
7713
+ constructor(leftDelimExpr, innerExpr, rightDelimExpr, state = null) {
7714
+ super(state);
7715
+ this.nodeType = "DelimiterObject";
7716
+ this.leftDelimExpr = leftDelimExpr;
7717
+ this.innerExpr = innerExpr;
7718
+ this.rightDelimExpr = rightDelimExpr;
7719
+ }
7720
+ latex() {
7721
+ return this.leftDelimExpr + this.innerExpr.latex() + this.rightDelimExpr + this.latexScripts();
7722
+ }
7723
+ arabicLatex() {
7724
+ const sup = this.superscript !== null ? this.superscript.arabicLatex() : "";
7725
+ const sub = this.subscript !== null ? this.subscript.arabicLatex() : "";
7726
+ const content = this.leftDelimExpr + this.innerExpr.arabicLatex() + this.rightDelimExpr;
7727
+ return this.arabicLatexScripts(sup, sub, content);
7728
+ }
7729
+ };
7730
+ var CommandNode = class extends BaseAstNode {
7731
+ name;
7732
+ optionalArgs;
7733
+ mandatoryArgs;
7734
+ constructor(name, optionalArgs = [], mandatoryArgs = [], state = null) {
7735
+ super(state);
7736
+ this.nodeType = "CommandObject";
7737
+ this.name = name;
7738
+ this.optionalArgs = optionalArgs;
7739
+ this.mandatoryArgs = mandatoryArgs;
7740
+ }
7741
+ latex() {
7742
+ const content = renderCommandCore({
7743
+ name: this.name,
7744
+ optionalArgs: this.optionalArgs,
7745
+ mandatoryArgs: this.mandatoryArgs,
7746
+ renderChain: (chain) => chain.latex()
7747
+ });
7748
+ return content + this.latexScripts();
7749
+ }
7750
+ arabicLatex() {
7751
+ const sup = this.superscript !== null ? this.superscript.arabicLatex() : "";
7752
+ const sub = this.subscript !== null ? this.subscript.arabicLatex() : "";
7753
+ const arabicName = this.name === "\\sqrt" || this.name === "\\arabsqrt" ? "\\arsqrt" : this.name;
7754
+ const content = renderCommandCore({
7755
+ name: arabicName,
7756
+ optionalArgs: this.optionalArgs,
7757
+ mandatoryArgs: this.mandatoryArgs,
7758
+ renderChain: (chain) => chain.arabicLatex()
7759
+ });
7760
+ return this.arabicLatexScripts(sup, sub, content);
7761
+ }
7762
+ };
7763
+ var EnvNode = class extends BaseAstNode {
7764
+ opening;
7765
+ lines;
7766
+ closing;
7767
+ constructor(opening, lines = [], closing, state = null) {
7768
+ super(state);
7769
+ this.nodeType = "EnvObject";
7770
+ this.opening = opening;
7771
+ this.lines = lines;
7772
+ this.closing = closing;
7773
+ }
7774
+ latex() {
7775
+ if (this.lines.length === 0) {
7776
+ return this.opening + this.closing + this.latexScripts();
7777
+ }
7778
+ const formattedLines = this.lines.map((line) => ` ${line.latex()}`);
7779
+ const content = formattedLines.join(" \\\\\n");
7780
+ return `${this.opening}
7781
+ ${content}
7782
+ ${this.closing}` + this.latexScripts();
7783
+ }
7784
+ arabicLatex() {
7785
+ const sup = this.superscript !== null ? this.superscript.arabicLatex() : "";
7786
+ const sub = this.subscript !== null ? this.subscript.arabicLatex() : "";
7787
+ if (this.lines.length === 0) {
7788
+ return this.arabicLatexScripts(sup, sub, this.opening + this.closing);
7789
+ }
7790
+ const formattedLines = this.lines.map((line) => ` ${line.arabicLatex()}`);
7791
+ const content = formattedLines.join(" \\\\\n");
7792
+ const fullContent = `${this.opening}
7793
+ ${content}
7794
+ ${this.closing}`;
7795
+ return this.arabicLatexScripts(sup, sub, fullContent);
7796
+ }
7797
+ };
7798
+ function parseScripts(node, json, mode) {
7799
+ if (json.superscript) {
7800
+ node.superscript = parseChain(json.superscript, mode);
7801
+ }
7802
+ if (json.subscript) {
7803
+ node.subscript = parseChain(json.subscript, mode);
7804
+ }
7805
+ }
7806
+ function parseNode(json, mode) {
7807
+ if (json.node_type === "DelimiterObject") {
7808
+ const node2 = parseDelimiter(json, mode);
7809
+ parseScripts(node2, json, mode);
7810
+ return node2;
7811
+ }
7812
+ if (json.node_type === "OperatorObject") {
7813
+ if (typeof json.expr !== "string") {
7814
+ throw new Error("OperatorObject requires string expr");
7815
+ }
7816
+ const node2 = new OperatorNode(json.expr);
7817
+ parseScripts(node2, json, mode);
7818
+ return node2;
7819
+ }
7820
+ if (json.node_type === "CommandObject") {
7821
+ const node2 = parseCommand(json, mode);
7822
+ parseScripts(node2, json, mode);
7823
+ return node2;
7824
+ }
7825
+ if (json.node_type === "EnvObject") {
7826
+ const node2 = parseEnv(json, mode);
7827
+ parseScripts(node2, json, mode);
7828
+ return node2;
7829
+ }
7830
+ if (typeof json.expr !== "string") {
7831
+ throw new Error(`${json.node_type} requires string expr`);
7832
+ }
7833
+ let node;
7834
+ if (json.node_type === "NumberObject") {
7835
+ node = new NumberNode(json.expr);
7836
+ } else if (json.node_type === "CharObject") {
7837
+ node = new CharNode(json.expr);
7838
+ } else {
7839
+ throw new Error(`Unsupported node_type: ${json.node_type}`);
7840
+ }
7841
+ parseScripts(node, json, mode);
7842
+ return node;
7843
+ }
7844
+ function parseDelimiter(json, mode) {
7845
+ if (typeof json.left_delim_expr !== "string") {
7846
+ throw new Error("DelimiterObject requires string left_delim_expr");
7847
+ }
7848
+ if (!json.inner_expr || json.inner_expr.node_type !== "ChainClass") {
7849
+ throw new Error("DelimiterObject requires ChainClass inner_expr");
7850
+ }
7851
+ if (typeof json.right_delim_expr !== "string") {
7852
+ throw new Error("DelimiterObject requires string right_delim_expr");
7853
+ }
7854
+ const innerExpr = parseChain(json.inner_expr, mode);
7855
+ return new DelimiterNode(json.left_delim_expr, innerExpr, json.right_delim_expr);
7856
+ }
7857
+ function parseCommand(json, mode) {
7858
+ if (typeof json.name !== "string") {
7859
+ throw new Error("CommandObject requires string name");
7860
+ }
7861
+ const optionalJson = Array.isArray(json.optional_args) ? json.optional_args : [];
7862
+ const mandatoryJson = Array.isArray(json.mandatory_args) ? json.mandatory_args : [];
7863
+ const optionalArgs = optionalJson.map((arg) => parseChain(arg, mode));
7864
+ const mandatoryArgs = mandatoryJson.map((arg) => parseChain(arg, mode));
7865
+ return new CommandNode(json.name, optionalArgs, mandatoryArgs);
7866
+ }
7867
+ function parseEnv(json, mode) {
7868
+ if (typeof json.opening !== "string") {
7869
+ throw new Error("EnvObject requires string opening");
7870
+ }
7871
+ if (typeof json.closing !== "string") {
7872
+ throw new Error("EnvObject requires string closing");
7873
+ }
7874
+ if (!Array.isArray(json.lines)) {
7875
+ throw new Error("EnvObject requires lines array");
7876
+ }
7877
+ const lines = json.lines.map((line) => parseChain(line, mode));
7878
+ return new EnvNode(json.opening, lines, json.closing);
7879
+ }
7880
+ function parseChain(json, mode) {
7881
+ if (json.node_type !== "ChainClass") {
7882
+ throw new Error(`Expected ChainClass, got: ${json.node_type}`);
7883
+ }
7884
+ const nodes = json.chain.map((childJson) => parseNode(childJson, mode));
7885
+ return new ChainNode(nodes);
7886
+ }
7887
+ function fromEnglishJson(json) {
7888
+ if (json.node_type === "ChainClass") {
7889
+ return parseChain(json, "english");
7890
+ }
7891
+ return parseNode(json, "english");
7892
+ }
7893
+ function fromArabicJson(json) {
7894
+ if (json.node_type === "ChainClass") {
7895
+ return parseChain(json, "arabic");
7896
+ }
7897
+ return parseNode(json, "arabic");
7898
+ }
7899
+
7900
+ // src/document/mathObject.ts
7901
+ function isObject(value) {
7902
+ return typeof value === "object" && value !== null;
7903
+ }
7904
+ function isMathObjectJson(value) {
7905
+ return isObject(value) && value.node_type === "MathObject";
7906
+ }
7907
+ function assertChainNode(value) {
7908
+ if (!(value instanceof ChainNode)) {
7909
+ throw new Error("MathObject lines must parse to ChainNode");
7910
+ }
7911
+ return value;
7912
+ }
7913
+ function fromMathObjectJson(json, mode = "english") {
7914
+ if (!isMathObjectJson(json)) {
7915
+ throw new Error('MathObject requires node_type "MathObject"');
7916
+ }
7917
+ if (typeof json.math_mode !== "string") {
7918
+ throw new Error("MathObject requires string math_mode");
7919
+ }
7920
+ if (typeof json.closing !== "string") {
7921
+ throw new Error("MathObject requires string closing");
7922
+ }
7923
+ if (!Array.isArray(json.lines) || json.lines.length === 0) {
7924
+ throw new Error("MathObject requires non-empty lines array");
7925
+ }
7926
+ const parseChain2 = mode === "arabic" ? fromArabicJson : fromEnglishJson;
7927
+ const lines = json.lines.map((line) => assertChainNode(parseChain2(line)));
7928
+ return {
7929
+ nodeType: "MathObject",
7930
+ mathMode: json.math_mode,
7931
+ lines,
7932
+ closing: json.closing
7933
+ };
7934
+ }
7935
+ function scriptsToJson(node, path) {
7936
+ return {
7937
+ ...node.superscript ? { superscript: chainToJson(node.superscript, `${path}.superscript`) } : {},
7938
+ ...node.subscript ? { subscript: chainToJson(node.subscript, `${path}.subscript`) } : {}
7939
+ };
7940
+ }
7941
+ function nodeToJson(node, path) {
7942
+ const scripts = scriptsToJson(node, path);
7943
+ if (node instanceof CharNode || node instanceof NumberNode || node instanceof OperatorNode) {
7944
+ return { node_type: node.nodeType, expr: node.expr, ...scripts };
7945
+ }
7946
+ if (node instanceof DelimiterNode) {
7947
+ return {
7948
+ node_type: "DelimiterObject",
7949
+ left_delim_expr: node.leftDelimExpr,
7950
+ inner_expr: chainToJson(node.innerExpr, `${path}.inner_expr`),
7951
+ right_delim_expr: node.rightDelimExpr,
7952
+ ...scripts
7953
+ };
7954
+ }
7955
+ if (node instanceof CommandNode) {
7956
+ return {
7957
+ node_type: "CommandObject",
7958
+ name: node.name,
7959
+ optional_args: node.optionalArgs.map((arg, index) => chainToJson(arg, `${path}.optional_args[${String(index)}]`)),
7960
+ mandatory_args: node.mandatoryArgs.map((arg, index) => chainToJson(arg, `${path}.mandatory_args[${String(index)}]`)),
7961
+ ...scripts
7962
+ };
7963
+ }
7964
+ if (node instanceof EnvNode) {
7965
+ return {
7966
+ node_type: "EnvObject",
7967
+ opening: node.opening,
7968
+ lines: node.lines.map((line, index) => chainToJson(line, `${path}.lines[${String(index)}]`)),
7969
+ closing: node.closing,
7970
+ ...scripts
7971
+ };
7972
+ }
7973
+ throw new Error(`Unsupported runtime math node at ${path}: ${node.nodeType}`);
7974
+ }
7975
+ function chainToJson(chain, path) {
7976
+ if (!(chain instanceof ChainNode)) {
7977
+ throw new Error(`Expected live ChainNode at ${path}`);
7978
+ }
7979
+ return {
7980
+ node_type: "ChainClass",
7981
+ chain: chain.chain.map((node, index) => nodeToJson(node, `${path}.chain[${String(index)}]`))
7982
+ };
7983
+ }
7984
+ function toMathObjectJson(math) {
7985
+ if (math.nodeType !== "MathObject" || !Array.isArray(math.lines) || math.lines.length === 0) {
7986
+ throw new Error("Cannot serialize invalid runtime MathObject");
7987
+ }
7988
+ return {
7989
+ node_type: "MathObject",
7990
+ math_mode: math.mathMode,
7991
+ lines: math.lines.map((line, index) => chainToJson(line, `$.lines[${String(index)}]`)),
7992
+ closing: math.closing
7993
+ };
7994
+ }
7995
+
7996
+ // src/document/normalizeCharImport.ts
7997
+ var WRAPPER_SPECS = [
7998
+ ...NON_DEFAULT_CHARACTER_FONT_SPECS.flatMap((font) => [
7999
+ { prefix: font.macro, font: font.id },
8000
+ ...(font.importAliases ?? []).map((alias) => ({ prefix: alias, font: font.id }))
8001
+ ]),
8002
+ { prefix: "\\text" }
8003
+ ];
8004
+ function readBracedArg(source, openBraceIndex) {
8005
+ if (source[openBraceIndex] !== "{") {
8006
+ return null;
8007
+ }
8008
+ let depth = 0;
8009
+ for (let index = openBraceIndex; index < source.length; index += 1) {
8010
+ const char = source[index];
8011
+ if (char === "{") {
8012
+ depth += 1;
8013
+ } else if (char === "}") {
8014
+ depth -= 1;
8015
+ if (depth === 0) {
8016
+ return { inner: source.slice(openBraceIndex + 1, index), end: index + 1 };
8017
+ }
8018
+ }
8019
+ }
8020
+ return null;
8021
+ }
8022
+ function peelWrapper(source) {
8023
+ for (const wrapper of WRAPPER_SPECS) {
8024
+ if (!source.startsWith(wrapper.prefix)) {
8025
+ continue;
8026
+ }
8027
+ const braced = readBracedArg(source, wrapper.prefix.length);
8028
+ if (!braced || braced.end !== source.length) {
8029
+ continue;
8030
+ }
8031
+ return { inner: braced.inner, font: wrapper.font };
8032
+ }
8033
+ return null;
8034
+ }
8035
+ function normalizeCharImportExpr(expr) {
8036
+ const original = expr;
8037
+ let current = expr;
8038
+ let characterFont = "default";
8039
+ let changed = false;
8040
+ while (true) {
8041
+ const peeled = peelWrapper(current);
8042
+ if (!peeled) {
8043
+ break;
8044
+ }
8045
+ current = peeled.inner;
8046
+ if (peeled.font) {
8047
+ characterFont = peeled.font;
8048
+ }
8049
+ changed = true;
8050
+ }
8051
+ if (!changed) {
8052
+ return { expr: original, characterFont: "default" };
8053
+ }
8054
+ if (current.includes("\\")) {
8055
+ return { expr: original, characterFont: "default" };
8056
+ }
8057
+ return { expr: current, characterFont };
8058
+ }
8059
+
8060
+ // src/document/mathEditorAdapter.ts
8061
+ var COMMAND_FONT_MAP = Object.fromEntries([
8062
+ ["\\text", "infer"],
8063
+ ...NON_DEFAULT_CHARACTER_FONT_SPECS.flatMap((font) => [
8064
+ [font.macro, font.id],
8065
+ ...(font.importAliases ?? []).map((alias) => [alias, font.id])
8066
+ ])
8067
+ ]);
8068
+ var CHAR_FONT_EXPORT_TEX = Object.fromEntries(
8069
+ NON_DEFAULT_CHARACTER_FONT_SPECS.map((font) => [font.id, font.macro])
8070
+ );
8071
+ function charEditorNodeToAstNode(node) {
8072
+ const font = node.characterFont ?? "default";
8073
+ const charNode = new CharNode(node.expr);
8074
+ if (font === "default") {
8075
+ return charNode;
8076
+ }
8077
+ return new CommandNode(CHAR_FONT_EXPORT_TEX[font], [], [new ChainNode([charNode])]);
8078
+ }
8079
+ function commandIdForTex(tex) {
8080
+ for (const spec of Object.values(ATOMIC_COMMANDS)) {
8081
+ const renderTex = spec.arabicRenderTex;
8082
+ if (spec.englishTex === tex || spec.arabicTex === tex || renderTex === tex) {
8083
+ return spec.id;
8084
+ }
8085
+ }
8086
+ return null;
8087
+ }
8088
+ function operatorCommandIdForTex(tex) {
8089
+ for (const spec of Object.values(ATOMIC_OPERATOR_COMMANDS)) {
8090
+ if (spec.Tex === tex) {
8091
+ return spec.id;
8092
+ }
8093
+ }
8094
+ return null;
8095
+ }
8096
+ function cloneChainWithFreshIds(chain) {
8097
+ return structuredClone(chain);
8098
+ }
8099
+ function attachScripts(astNode, editorNode) {
8100
+ if (astNode.superscript) {
8101
+ const sup = chainToEditorChain(astNode.superscript);
8102
+ if (!sup.editable) {
8103
+ return sup;
8104
+ }
8105
+ editorNode.superscript = sup.chain;
8106
+ }
8107
+ if (astNode.subscript) {
8108
+ const sub = chainToEditorChain(astNode.subscript);
8109
+ if (!sub.editable) {
8110
+ return sub;
8111
+ }
8112
+ editorNode.subscript = sub.chain;
8113
+ }
8114
+ return null;
8115
+ }
8116
+ var MATRIX_ENV_NAMES = /* @__PURE__ */ new Set(["matrix", "pmatrix", "bmatrix", "Bmatrix", "vmatrix", "Vmatrix"]);
8117
+ function isGridEnvName(value) {
8118
+ return MATRIX_ENV_NAMES.has(value) || value === "array" || value === "aligned";
8119
+ }
8120
+ function isMatrixEnvName(value) {
8121
+ return MATRIX_ENV_NAMES.has(value);
8122
+ }
8123
+ function parseEnvOpening(opening) {
8124
+ const match = /^\\begin\{([^}]+)\}(?:\{([lcr]+)\})?$/.exec(opening);
8125
+ if (!match) {
8126
+ return null;
8127
+ }
8128
+ const envName = match[1] ?? "";
8129
+ if (!isGridEnvName(envName)) {
8130
+ return null;
8131
+ }
8132
+ if (envName === "array") {
8133
+ const spec = match[2] ?? "c";
8134
+ return {
8135
+ envName,
8136
+ alignments: spec.split("").map((alignment) => alignment)
8137
+ };
8138
+ }
8139
+ return { envName };
8140
+ }
8141
+ function splitEnvLine(line) {
8142
+ const cells = [];
8143
+ let current = [];
8144
+ for (const node of line.chain) {
8145
+ if (node instanceof OperatorNode && node.expr === "&") {
8146
+ cells.push(new ChainNode(current));
8147
+ current = [];
8148
+ } else {
8149
+ current.push(node);
8150
+ }
8151
+ }
8152
+ cells.push(new ChainNode(current));
8153
+ return cells;
8154
+ }
8155
+ function envToEditorNode(node) {
8156
+ const parsed = parseEnvOpening(node.opening);
8157
+ if (!parsed) {
8158
+ 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}` };
8159
+ }
8160
+ const expectedClosing = `\\end{${parsed.envName}}`;
8161
+ if (node.closing !== expectedClosing) {
8162
+ 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}` };
8163
+ }
8164
+ const rowCells = node.lines.map(splitEnvLine);
8165
+ const rows = Math.max(1, rowCells.length);
8166
+ const columns = Math.max(1, ...rowCells.map((row) => row.length));
8167
+ const env = createGridEnvNode(parsed.envName, rows, columns, parsed.alignments);
8168
+ for (let rowIndex = 0; rowIndex < rows; rowIndex += 1) {
8169
+ const row = rowCells[rowIndex] ?? [];
8170
+ for (let columnIndex = 0; columnIndex < columns; columnIndex += 1) {
8171
+ const cell = chainToEditorChain(row[columnIndex] ?? new ChainNode());
8172
+ if (!cell.editable) {
8173
+ return cell;
8174
+ }
8175
+ env.matrixRows[rowIndex][columnIndex] = cell.chain;
8176
+ }
8177
+ }
8178
+ if (parsed.envName === "array") {
8179
+ env.columnAlignments = Array.from(
8180
+ { length: columns },
8181
+ (_, index) => parsed.alignments?.[index] ?? "c"
8182
+ );
8183
+ }
8184
+ if (isMatrixEnvName(parsed.envName)) {
8185
+ env.matrixStyle = parsed.envName;
8186
+ }
8187
+ const scripts = attachScripts(node, env);
8188
+ return scripts ?? { editable: true, node: env };
8189
+ }
8190
+ function charFromTextLikeCommand(node) {
8191
+ const commandFont = COMMAND_FONT_MAP[node.name];
8192
+ if (commandFont === void 0) {
8193
+ return null;
8194
+ }
8195
+ if (node.optionalArgs.length > 0 || node.mandatoryArgs.length !== 1) {
8196
+ return null;
8197
+ }
8198
+ const arg = node.mandatoryArgs[0];
8199
+ if (arg.chain.length !== 1) {
8200
+ return null;
8201
+ }
8202
+ const child = arg.chain[0];
8203
+ if (child instanceof CommandNode) {
8204
+ const nested = charFromTextLikeCommand(child);
8205
+ if (!nested?.editable) {
8206
+ return null;
8207
+ }
8208
+ if (commandFont !== "infer") {
8209
+ nested.node.characterFont = commandFont;
8210
+ }
8211
+ const scripts2 = attachScripts(node, nested.node);
8212
+ return scripts2 ?? nested;
8213
+ }
8214
+ if (!(child instanceof CharNode)) {
8215
+ return null;
8216
+ }
8217
+ const normalized = normalizeCharImportExpr(child.expr);
8218
+ const characterFont = commandFont === "infer" ? normalized.characterFont : commandFont;
8219
+ const editorNode = createEditorNode("char", normalized.expr);
8220
+ if (characterFont !== "default") {
8221
+ editorNode.characterFont = characterFont;
8222
+ }
8223
+ const scripts = attachScripts(node, editorNode);
8224
+ return scripts ?? { editable: true, node: editorNode };
8225
+ }
8226
+ function commandToEditorNode(node) {
8227
+ if (node.name === "\\frac" && node.mandatoryArgs.length >= 2) {
8228
+ const frac = createFracNode();
8229
+ const numerator = chainToEditorChain(node.mandatoryArgs[0]);
8230
+ const denominator = chainToEditorChain(node.mandatoryArgs[1]);
8231
+ if (!numerator.editable) {
8232
+ return numerator;
8233
+ }
8234
+ if (!denominator.editable) {
8235
+ return denominator;
8236
+ }
8237
+ frac.numerator = numerator.chain;
8238
+ frac.denominator = denominator.chain;
8239
+ const scripts = attachScripts(node, frac);
8240
+ if (scripts) {
8241
+ return scripts;
8242
+ }
8243
+ return { editable: true, node: frac };
8244
+ }
8245
+ if ((node.name === "\\sqrt" || node.name === "\\arabsqrt" || node.name === "\\arsqrt") && node.mandatoryArgs.length >= 1) {
8246
+ const sqrt = createSqrtNode();
8247
+ const radicand = chainToEditorChain(node.mandatoryArgs[0]);
8248
+ if (!radicand.editable) {
8249
+ return radicand;
8250
+ }
8251
+ sqrt.radicand = radicand.chain;
8252
+ if (node.optionalArgs[0]) {
8253
+ const rootIndex = chainToEditorChain(node.optionalArgs[0]);
8254
+ if (!rootIndex.editable) {
8255
+ return rootIndex;
8256
+ }
8257
+ sqrt.rootIndex = rootIndex.chain;
8258
+ }
8259
+ const scripts = attachScripts(node, sqrt);
8260
+ if (scripts) {
8261
+ return scripts;
8262
+ }
8263
+ return { editable: true, node: sqrt };
8264
+ }
8265
+ if (node.name === "\\overset" && node.mandatoryArgs.length >= 2) {
8266
+ const overset = createOversetNode();
8267
+ const overExpr = chainToEditorChain(node.mandatoryArgs[0]);
8268
+ const baseExpr = chainToEditorChain(node.mandatoryArgs[1]);
8269
+ if (!overExpr.editable) {
8270
+ return overExpr;
8271
+ }
8272
+ if (!baseExpr.editable) {
8273
+ return baseExpr;
8274
+ }
8275
+ overset.overExpr = overExpr.chain;
8276
+ overset.baseExpr = baseExpr.chain;
8277
+ const scripts = attachScripts(node, overset);
8278
+ if (scripts) {
8279
+ return scripts;
8280
+ }
8281
+ return { editable: true, node: overset };
8282
+ }
8283
+ if (node.name === "\\underset" && node.mandatoryArgs.length >= 2) {
8284
+ const underset = createUndersetNode();
8285
+ const underExpr = chainToEditorChain(node.mandatoryArgs[0]);
8286
+ const baseExpr = chainToEditorChain(node.mandatoryArgs[1]);
8287
+ if (!underExpr.editable) {
8288
+ return underExpr;
8289
+ }
8290
+ if (!baseExpr.editable) {
8291
+ return baseExpr;
8292
+ }
8293
+ underset.underExpr = underExpr.chain;
8294
+ underset.baseExpr = baseExpr.chain;
8295
+ const scripts = attachScripts(node, underset);
8296
+ if (scripts) {
8297
+ return scripts;
8298
+ }
8299
+ return { editable: true, node: underset };
8300
+ }
8301
+ const accentId = accentCommandIdFromTex(node.name);
8302
+ if (accentId && node.mandatoryArgs.length >= 1) {
8303
+ const accent = createAccentNode(accentId);
8304
+ const baseExpr = chainToEditorChain(node.mandatoryArgs[0]);
8305
+ if (!baseExpr.editable) {
8306
+ return baseExpr;
8307
+ }
8308
+ accent.baseExpr = baseExpr.chain;
8309
+ const scripts = attachScripts(node, accent);
8310
+ if (scripts) {
8311
+ return scripts;
8312
+ }
8313
+ return { editable: true, node: accent };
8314
+ }
8315
+ const atomicId = commandIdForTex(node.name);
8316
+ if (atomicId && node.optionalArgs.length === 0 && node.mandatoryArgs.length === 0) {
8317
+ const atomic = createEditorNode("atomicCommand", atomicId);
8318
+ const scripts = attachScripts(node, atomic);
8319
+ if (scripts) {
8320
+ return scripts;
8321
+ }
8322
+ return { editable: true, node: atomic };
8323
+ }
8324
+ const operatorId = operatorCommandIdForTex(node.name);
8325
+ if (operatorId && node.optionalArgs.length === 0 && node.mandatoryArgs.length === 0) {
8326
+ const atomicOperator = createEditorNode("atomicOperatorCommand", operatorId);
8327
+ const scripts = attachScripts(node, atomicOperator);
8328
+ if (scripts) {
8329
+ return scripts;
8330
+ }
8331
+ return { editable: true, node: atomicOperator };
8332
+ }
8333
+ const textLike = charFromTextLikeCommand(node);
8334
+ if (textLike) {
8335
+ return textLike;
8336
+ }
8337
+ 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}` };
8338
+ }
8339
+ function astNodeToEditorNode(node) {
8340
+ if (node instanceof CharNode) {
8341
+ const normalized = normalizeCharImportExpr(node.expr);
8342
+ const editorNode = createEditorNode("char", normalized.expr);
8343
+ if (normalized.characterFont !== "default") {
8344
+ editorNode.characterFont = normalized.characterFont;
8345
+ }
8346
+ const scripts = attachScripts(node, editorNode);
8347
+ return scripts ?? { editable: true, node: editorNode };
8348
+ }
8349
+ if (node instanceof NumberNode) {
8350
+ const editorNode = createEditorNode("number", node.expr);
8351
+ const scripts = attachScripts(node, editorNode);
8352
+ return scripts ?? { editable: true, node: editorNode };
8353
+ }
8354
+ if (node instanceof OperatorNode) {
8355
+ return { editable: true, node: createEditorNode("operator", node.expr) };
8356
+ }
8357
+ if (node instanceof DelimiterNode) {
8358
+ const editorNode = createDelimiterNode(node.leftDelimExpr, node.rightDelimExpr);
8359
+ const inner = chainToEditorChain(node.innerExpr);
8360
+ if (!inner.editable) {
8361
+ return inner;
8362
+ }
8363
+ editorNode.innerExpr = inner.chain;
8364
+ const scripts = attachScripts(node, editorNode);
8365
+ return scripts ?? { editable: true, node: editorNode };
8366
+ }
8367
+ if (node instanceof CommandNode) {
8368
+ return commandToEditorNode(node);
8369
+ }
8370
+ if (node instanceof EnvNode) {
8371
+ return envToEditorNode(node);
8372
+ }
8373
+ 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}` };
8374
+ }
8375
+ function chainToEditorChain(chain) {
8376
+ const nodes = [];
8377
+ for (const astNode of chain.chain) {
8378
+ const converted = astNodeToEditorNode(astNode);
8379
+ if (!converted.editable) {
8380
+ return converted;
8381
+ }
8382
+ nodes.push(converted.node);
8383
+ }
8384
+ return { editable: true, chain: createEditorChain(nodes) };
8385
+ }
8386
+ function mathObjectToEditorSession(math) {
8387
+ if (math.lines.length !== 1) {
8388
+ 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" };
8389
+ }
8390
+ const english = chainToEditorChain(math.lines[0]);
8391
+ if (!english.editable) {
8392
+ return english;
8393
+ }
8394
+ const arabic = cloneChainWithFreshIds(english.chain);
8395
+ const session = createEditorSession(english.chain, arabic, "english");
8396
+ session.sync = buildInitialSyncMap(session.englishTree, session.arabicTree);
8397
+ return { editable: true, session: cloneEditorSession(session) };
8398
+ }
8399
+ function attachAstScripts(editorNode, astNode) {
8400
+ if (editorNode.superscript) {
8401
+ const sup = editorChainToAstChain(editorNode.superscript);
8402
+ if (!sup.ok) {
8403
+ return sup;
8404
+ }
8405
+ astNode.superscript = sup.chain;
8406
+ }
8407
+ if (editorNode.subscript) {
8408
+ const sub = editorChainToAstChain(editorNode.subscript);
8409
+ if (!sub.ok) {
8410
+ return sub;
8411
+ }
8412
+ astNode.subscript = sub.chain;
8413
+ }
8414
+ return null;
8415
+ }
8416
+ function editorEnvOpening(node) {
8417
+ const envName = node.envName ?? node.matrixStyle;
8418
+ if (!envName) {
8419
+ return null;
8420
+ }
8421
+ if (envName === "array") {
8422
+ const spec = (node.columnAlignments ?? []).slice(0, node.matrixRows?.[0]?.length ?? 0).join("") || "c";
8423
+ return { opening: `\\begin{array}{${spec}}`, closing: "\\end{array}" };
8424
+ }
8425
+ return { opening: `\\begin{${envName}}`, closing: `\\end{${envName}}` };
8426
+ }
8427
+ function editorEnvToAstNode(node) {
8428
+ const wrapper = editorEnvOpening(node);
8429
+ if (!wrapper || !node.matrixRows) {
8430
+ 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" };
8431
+ }
8432
+ const lines = [];
8433
+ for (const row of node.matrixRows) {
8434
+ const lineNodes = [];
8435
+ for (let columnIndex = 0; columnIndex < row.length; columnIndex += 1) {
8436
+ if (columnIndex > 0) {
8437
+ lineNodes.push(new OperatorNode("&"));
8438
+ }
8439
+ const cell = editorChainToAstChain(row[columnIndex]);
8440
+ if (!cell.ok) {
8441
+ return cell;
8442
+ }
8443
+ lineNodes.push(...cell.chain.chain);
8444
+ }
8445
+ lines.push(new ChainNode(lineNodes));
8446
+ }
8447
+ const env = new EnvNode(wrapper.opening, lines, wrapper.closing);
8448
+ const scripts = attachAstScripts(node, env);
8449
+ return scripts ?? { ok: true, node: env };
8450
+ }
8451
+ function accentChainForExport(accent) {
8452
+ if (!accent) {
8453
+ return { ok: true, chain: new ChainNode([]) };
8454
+ }
8455
+ return editorChainToAstChain(accent);
8456
+ }
8457
+ function nodeFromAccentBase(command, accent, base) {
8458
+ if (accent.chain.length > 0) {
8459
+ return new CommandNode(command, [], [accent, base]);
8460
+ }
8461
+ if (base.chain.length === 1) {
8462
+ return base.chain[0];
8463
+ }
8464
+ return new DelimiterNode("", base, "");
8465
+ }
8466
+ function editorNodeToAstNode(node) {
8467
+ let astNode;
8468
+ if (node.kind === "char") {
8469
+ astNode = charEditorNodeToAstNode(node);
8470
+ } else if (node.kind === "number") {
8471
+ astNode = new NumberNode(node.expr);
8472
+ } else if (node.kind === "operator") {
8473
+ astNode = new OperatorNode(node.expr);
8474
+ } else if (node.kind === "delimiter" && node.innerExpr) {
8475
+ const inner = editorChainToAstChain(node.innerExpr);
8476
+ if (!inner.ok) {
8477
+ return inner;
8478
+ }
8479
+ astNode = new DelimiterNode(node.leftDelimExpr, inner.chain, node.rightDelimExpr);
8480
+ } else if (node.kind === "frac" && node.numerator && node.denominator) {
8481
+ const numerator = editorChainToAstChain(node.numerator);
8482
+ const denominator = editorChainToAstChain(node.denominator);
8483
+ if (!numerator.ok) {
8484
+ return numerator;
8485
+ }
8486
+ if (!denominator.ok) {
8487
+ return denominator;
8488
+ }
8489
+ astNode = new CommandNode("\\frac", [], [numerator.chain, denominator.chain]);
8490
+ } else if (node.kind === "sqrt" && node.radicand) {
8491
+ const radicand = editorChainToAstChain(node.radicand);
8492
+ if (!radicand.ok) {
8493
+ return radicand;
8494
+ }
8495
+ const optionalArgs = [];
8496
+ if (node.rootIndex && node.rootIndex.nodes.length > 0) {
8497
+ const rootIndex = editorChainToAstChain(node.rootIndex);
8498
+ if (!rootIndex.ok) {
8499
+ return rootIndex;
8500
+ }
8501
+ optionalArgs.push(rootIndex.chain);
8502
+ }
8503
+ astNode = new CommandNode("\\sqrt", optionalArgs, [radicand.chain]);
8504
+ } else if (node.kind === "overset" && node.baseExpr) {
8505
+ const accent = accentChainForExport(node.overExpr);
8506
+ if (!accent.ok) {
8507
+ return accent;
8508
+ }
8509
+ const baseExpr = editorChainToAstChain(node.baseExpr);
8510
+ if (!baseExpr.ok) {
8511
+ return baseExpr;
8512
+ }
8513
+ astNode = nodeFromAccentBase("\\overset", accent.chain, baseExpr.chain);
8514
+ } else if (node.kind === "underset" && node.baseExpr) {
8515
+ const accent = accentChainForExport(node.underExpr);
8516
+ if (!accent.ok) {
8517
+ return accent;
8518
+ }
8519
+ const baseExpr = editorChainToAstChain(node.baseExpr);
8520
+ if (!baseExpr.ok) {
8521
+ return baseExpr;
8522
+ }
8523
+ astNode = nodeFromAccentBase("\\underset", accent.chain, baseExpr.chain);
8524
+ } else if (node.kind === "accent" && node.baseExpr) {
8525
+ const tex = accentCommandSpec(node.expr)?.englishTex;
8526
+ if (!tex) {
8527
+ 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}` };
8528
+ }
8529
+ const baseExpr = editorChainToAstChain(node.baseExpr);
8530
+ if (!baseExpr.ok) {
8531
+ return baseExpr;
8532
+ }
8533
+ astNode = new CommandNode(tex, [], [baseExpr.chain]);
8534
+ } else if (node.kind === "atomicCommand") {
8535
+ const tex = atomicCommandSpec(node.expr)?.englishTex;
8536
+ if (!tex) {
8537
+ 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}` };
8538
+ }
8539
+ astNode = new CommandNode(tex);
8540
+ } else if (node.kind === "atomicOperatorCommand") {
8541
+ const tex = atomicOperatorCommandSpec(node.expr)?.Tex;
8542
+ if (!tex) {
8543
+ 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}` };
8544
+ }
8545
+ astNode = new CommandNode(tex);
8546
+ } else if (node.kind === "env") {
8547
+ return editorEnvToAstNode(node);
8548
+ } else {
8549
+ 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}` };
8550
+ }
8551
+ const scripts = attachAstScripts(node, astNode);
8552
+ return scripts ?? { ok: true, node: astNode };
8553
+ }
8554
+ function editorChainToAstChain(chain) {
8555
+ const nodes = [];
8556
+ for (const editorNode of chain.nodes) {
8557
+ const converted = editorNodeToAstNode(editorNode);
8558
+ if (!converted.ok) {
8559
+ return converted;
8560
+ }
8561
+ nodes.push(converted.node);
8562
+ }
8563
+ return { ok: true, chain: new ChainNode(nodes) };
8564
+ }
8565
+ function mathNodeFromEditorSession(session, mathMode = "$", closing = "$") {
8566
+ const converted = editorChainToAstChain(session.englishTree);
8567
+ if (!converted.ok) {
8568
+ return converted;
8569
+ }
8570
+ return {
8571
+ ok: true,
8572
+ math: {
8573
+ nodeType: "MathObject",
8574
+ mathMode,
8575
+ lines: [converted.chain],
8576
+ closing
8577
+ }
8578
+ };
8579
+ }
8580
+
8581
+ // src/debugJson.ts
8582
+ function lineColumnForPosition(text, position) {
8583
+ const before = text.slice(0, Math.max(0, position));
8584
+ const lines = before.split("\n");
8585
+ return {
8586
+ line: lines.length,
8587
+ column: (lines[lines.length - 1]?.length ?? 0) + 1
8588
+ };
8589
+ }
8590
+ function parseJsonDebugInput(text) {
8591
+ try {
8592
+ return { ok: true, value: JSON.parse(text) };
8593
+ } catch (error) {
8594
+ const message = error instanceof Error ? error.message : String(error);
8595
+ const positionMatch = message.match(/position\s+(\d+)/i);
8596
+ if (positionMatch) {
8597
+ const position = Number(positionMatch[1]);
8598
+ if (Number.isFinite(position)) {
8599
+ const location = lineColumnForPosition(text, position);
8600
+ return { ok: false, error: `Invalid JSON at line ${String(location.line)}, column ${String(location.column)}: ${message}` };
8601
+ }
8602
+ }
8603
+ return { ok: false, error: `Invalid JSON: ${message}` };
8604
+ }
8605
+ }
8606
+
7420
8607
  // src/react/ButexEditor.tsx
7421
8608
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
7422
8609
  var DIGIT_FORM_OPTIONS = [
@@ -7532,6 +8719,7 @@ ${latex || messages.empty}`;
7532
8719
  var ButexEditor = forwardRef(
7533
8720
  function ButexEditor2({ className, debug = false, defaultSide, uiLocale = "ar", initialSession, onSessionChange }, ref) {
7534
8721
  const messages = equationEditorMessages(uiLocale);
8722
+ const debugEnabled = debug || typeof window !== "undefined" && new URLSearchParams(window.location.search).get("debug") === "1";
7535
8723
  const wrapperRef = useRef(null);
7536
8724
  const surfaceRef = useRef(null);
7537
8725
  const inputRef = useRef(null);
@@ -7549,7 +8737,7 @@ var ButexEditor = forwardRef(
7549
8737
  const digitFormBtnRef = useRef(null);
7550
8738
  const runtimeRef = useRef(null);
7551
8739
  const debugRef = useRef(debug);
7552
- debugRef.current = debug;
8740
+ debugRef.current = debugEnabled;
7553
8741
  const uiLocaleRef = useRef(uiLocale);
7554
8742
  uiLocaleRef.current = uiLocale;
7555
8743
  const onSessionChangeRef = useRef(onSessionChange);
@@ -7557,6 +8745,9 @@ var ButexEditor = forwardRef(
7557
8745
  const debugBodyHiddenRef = useRef(false);
7558
8746
  const [debugBodyHidden, setDebugBodyHidden] = useState(false);
7559
8747
  debugBodyHiddenRef.current = debugBodyHidden;
8748
+ const [debugImportOpen, setDebugImportOpen] = useState(false);
8749
+ const [debugImportText, setDebugImportText] = useState("");
8750
+ const [debugImportError, setDebugImportError] = useState("");
7560
8751
  const [digitMenuOpen, setDigitMenuOpen] = useState(false);
7561
8752
  const [characterFontMenuOpen, setCharacterFontMenuOpen] = useState(false);
7562
8753
  const [delimiterMenuOpen, setDelimiterMenuOpen] = useState(false);
@@ -7637,6 +8828,54 @@ ${arabic || currentMessages.empty}`;
7637
8828
  }, []);
7638
8829
  const updatePreviewRef = useRef(updatePreview);
7639
8830
  updatePreviewRef.current = updatePreview;
8831
+ async function copyDebugText(text) {
8832
+ try {
8833
+ await navigator.clipboard.writeText(text);
8834
+ } catch {
8835
+ }
8836
+ }
8837
+ function currentMathObjectJsonText() {
8838
+ const session = runtimeRef.current?.getSession();
8839
+ if (!session) {
8840
+ return "";
8841
+ }
8842
+ const math = mathNodeFromEditorSession(session);
8843
+ if (!math.ok) {
8844
+ return "";
8845
+ }
8846
+ return JSON.stringify(toMathObjectJson(math.math), null, 2);
8847
+ }
8848
+ function openDebugMathObjectImport() {
8849
+ setDebugImportText(currentMathObjectJsonText());
8850
+ setDebugImportError("");
8851
+ setDebugImportOpen(true);
8852
+ }
8853
+ function applyDebugMathObjectImport() {
8854
+ const parsed = parseJsonDebugInput(debugImportText);
8855
+ if (!parsed.ok) {
8856
+ setDebugImportError(parsed.error);
8857
+ return;
8858
+ }
8859
+ try {
8860
+ const math = fromMathObjectJson(parsed.value, activeEditorSide);
8861
+ const result = mathObjectToEditorSession(math);
8862
+ if (!result.editable) {
8863
+ setDebugImportError(result.reason);
8864
+ return;
8865
+ }
8866
+ const next = { ...result.session, activeSide: activeEditorSide };
8867
+ runtimeRef.current?.setSession(next);
8868
+ setSelectedDigitForm(next.currentDigitForm ?? "western");
8869
+ setSelectedCharacterFont(next.currentCharacterFont ?? "default");
8870
+ setActiveEditorSide(next.activeSide);
8871
+ onSessionChangeRef.current?.(next);
8872
+ setDebugImportError("");
8873
+ setDebugImportOpen(false);
8874
+ void updatePreviewRef.current(next);
8875
+ } catch (importError) {
8876
+ setDebugImportError(importError instanceof Error ? importError.message : String(importError));
8877
+ }
8878
+ }
7640
8879
  useEffect(() => {
7641
8880
  injectWidgetChromeCss();
7642
8881
  injectBuTeXEditorStyles(typeof document !== "undefined" ? document : void 0);
@@ -8797,7 +10036,7 @@ ${arabic || currentMessages.empty}`;
8797
10036
  /* @__PURE__ */ jsx("div", { ref: renderErrorRef, className: "error", hidden: true })
8798
10037
  ] }),
8799
10038
  /* @__PURE__ */ jsx("section", { className: "panel panel-preview", "aria-label": messages.renderPreview, children: /* @__PURE__ */ jsx("div", { ref: mathOutputRef, className: "preview-box" }) }),
8800
- debug ? /* @__PURE__ */ jsxs("section", { className: "panel", children: [
10039
+ debugEnabled ? /* @__PURE__ */ jsxs("section", { className: "panel", children: [
8801
10040
  /* @__PURE__ */ jsxs("div", { className: "dev-strip", children: [
8802
10041
  /* @__PURE__ */ jsx("span", { className: "dev-badge", children: messages.development }),
8803
10042
  /* @__PURE__ */ jsx("div", { ref: latexLinesRef, className: "ascii" }),
@@ -8805,6 +10044,8 @@ ${arabic || currentMessages.empty}`;
8805
10044
  ] }),
8806
10045
  /* @__PURE__ */ jsxs("div", { style: { marginTop: 12 }, children: [
8807
10046
  /* @__PURE__ */ jsxs("div", { className: "debug-head", children: [
10047
+ /* @__PURE__ */ jsx("button", { type: "button", onClick: () => void copyDebugText(currentMathObjectJsonText()), children: "Copy MathObject JSON" }),
10048
+ /* @__PURE__ */ jsx("button", { type: "button", "aria-expanded": debugImportOpen, onClick: openDebugMathObjectImport, children: "Import MathObject JSON" }),
8808
10049
  /* @__PURE__ */ jsx(
8809
10050
  "button",
8810
10051
  {
@@ -8840,6 +10081,35 @@ ${arabic || currentMessages.empty}`;
8840
10081
  }
8841
10082
  )
8842
10083
  ] }),
10084
+ debugImportOpen ? /* @__PURE__ */ jsxs("div", { className: "debug-json-import", children: [
10085
+ /* @__PURE__ */ jsx(
10086
+ "textarea",
10087
+ {
10088
+ value: debugImportText,
10089
+ "aria-label": "Import MathObject JSON",
10090
+ spellCheck: false,
10091
+ onChange: (event) => {
10092
+ setDebugImportText(event.currentTarget.value);
10093
+ setDebugImportError("");
10094
+ }
10095
+ }
10096
+ ),
10097
+ /* @__PURE__ */ jsxs("div", { className: "debug-head", children: [
10098
+ /* @__PURE__ */ jsx("button", { type: "button", onClick: applyDebugMathObjectImport, children: "Apply" }),
10099
+ /* @__PURE__ */ jsx(
10100
+ "button",
10101
+ {
10102
+ type: "button",
10103
+ onClick: () => {
10104
+ setDebugImportOpen(false);
10105
+ setDebugImportError("");
10106
+ },
10107
+ children: "Cancel"
10108
+ }
10109
+ )
10110
+ ] }),
10111
+ debugImportError ? /* @__PURE__ */ jsx("p", { className: "debug-json-error", children: debugImportError }) : null
10112
+ ] }) : null,
8843
10113
  /* @__PURE__ */ jsx("div", { ref: debugLogRef, className: "debug-log" })
8844
10114
  ] })
8845
10115
  ] }) : null