@drghaliasri/butex 5.5.2 → 5.5.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -143,7 +143,20 @@ type MathObject2Json = {
143
143
  math_mode: string;
144
144
  lines: ChainJson[];
145
145
  closing: string;
146
+ /** Side used by the delimited source stored in the containing value/cell. */
147
+ source_side?: Document2ParseMode;
148
+ /** Preserve whether LaTeX export should prefer imported AST rendering or editor source. */
149
+ source_owner?: 'imported-structured' | 'editor';
150
+ label_enabled?: boolean;
151
+ label?: string;
152
+ };
153
+ /** Positional metadata for a raw-only span that has no structured equation AST. */
154
+ type RawMathObject2Json = {
155
+ node_type: 'RawMathObject';
156
+ label_enabled?: boolean;
157
+ label?: string;
146
158
  };
159
+ type Document2MathObjectJson = MathObject2Json | RawMathObject2Json;
147
160
  type ReferenceFieldSeparator = ',' | '،';
148
161
  type Reference2Json = {
149
162
  key: string;
@@ -179,7 +192,7 @@ type Document2BlockJson = {
179
192
  formats?: TextFormatSpan2Json[];
180
193
  /** Stable host asset identity (e.g. S3 key / UUID); optional for \\includegraphics. */
181
194
  asset_id?: string;
182
- math_objects?: MathObject2Json[];
195
+ math_objects?: Array<Document2MathObjectJson | null>;
183
196
  options?: Record<string, string>;
184
197
  closing?: string;
185
198
  items?: Document2ListItemJson[];
@@ -195,7 +208,7 @@ type Document2BlockJson = {
195
208
  type Document2ListItemJson = {
196
209
  value: string;
197
210
  formats?: TextFormatSpan2Json[];
198
- math_objects?: MathObject2Json[];
211
+ math_objects?: Array<Document2MathObjectJson | null>;
199
212
  blocks?: Document2BlockJson[];
200
213
  };
201
214
  type Document2Diagnostic = {
@@ -463,6 +476,8 @@ type ButexDocumentEditor2Props = {
463
476
  value: string;
464
477
  }) => string;
465
478
  onDocumentChange?: (document: Document2Node) => void;
479
+ /** Canonical JSON wire format for API/database persistence. */
480
+ onDocumentJsonChange?: (document: Document2Json) => void;
466
481
  onLatexChange?: (latex: string) => void;
467
482
  };
468
483
  type Document2InitialState = {
@@ -470,7 +485,7 @@ type Document2InitialState = {
470
485
  error: string | null;
471
486
  };
472
487
  declare function resolveInitialDocument2(initialDocument: Document2Json | Document2Node | undefined, uiLocale?: ButexUiLocale, documentMeta?: Document2MetaProp): Document2InitialState;
473
- declare function ButexDocumentEditor2({ initialDocument, documentMeta, className, debug, documentDirection, equationSide, editableEquations, previewOnly, uiLocale, mathOutput, digitForm: digitFormProp, onDigitFormChange, resolveImageUrl, onDocumentChange, onLatexChange, }: ButexDocumentEditor2Props): react_jsx_runtime.JSX.Element;
488
+ declare function ButexDocumentEditor2({ initialDocument, documentMeta, className, debug, documentDirection, equationSide, editableEquations, previewOnly, uiLocale, mathOutput, digitForm: digitFormProp, onDigitFormChange, resolveImageUrl, onDocumentChange, onDocumentJsonChange, onLatexChange, }: ButexDocumentEditor2Props): react_jsx_runtime.JSX.Element;
474
489
 
475
490
  type DocumentInsertToolbarProps = {
476
491
  canUndo: boolean;
@@ -143,7 +143,20 @@ type MathObject2Json = {
143
143
  math_mode: string;
144
144
  lines: ChainJson[];
145
145
  closing: string;
146
+ /** Side used by the delimited source stored in the containing value/cell. */
147
+ source_side?: Document2ParseMode;
148
+ /** Preserve whether LaTeX export should prefer imported AST rendering or editor source. */
149
+ source_owner?: 'imported-structured' | 'editor';
150
+ label_enabled?: boolean;
151
+ label?: string;
152
+ };
153
+ /** Positional metadata for a raw-only span that has no structured equation AST. */
154
+ type RawMathObject2Json = {
155
+ node_type: 'RawMathObject';
156
+ label_enabled?: boolean;
157
+ label?: string;
146
158
  };
159
+ type Document2MathObjectJson = MathObject2Json | RawMathObject2Json;
147
160
  type ReferenceFieldSeparator = ',' | '،';
148
161
  type Reference2Json = {
149
162
  key: string;
@@ -179,7 +192,7 @@ type Document2BlockJson = {
179
192
  formats?: TextFormatSpan2Json[];
180
193
  /** Stable host asset identity (e.g. S3 key / UUID); optional for \\includegraphics. */
181
194
  asset_id?: string;
182
- math_objects?: MathObject2Json[];
195
+ math_objects?: Array<Document2MathObjectJson | null>;
183
196
  options?: Record<string, string>;
184
197
  closing?: string;
185
198
  items?: Document2ListItemJson[];
@@ -195,7 +208,7 @@ type Document2BlockJson = {
195
208
  type Document2ListItemJson = {
196
209
  value: string;
197
210
  formats?: TextFormatSpan2Json[];
198
- math_objects?: MathObject2Json[];
211
+ math_objects?: Array<Document2MathObjectJson | null>;
199
212
  blocks?: Document2BlockJson[];
200
213
  };
201
214
  type Document2Diagnostic = {
@@ -463,6 +476,8 @@ type ButexDocumentEditor2Props = {
463
476
  value: string;
464
477
  }) => string;
465
478
  onDocumentChange?: (document: Document2Node) => void;
479
+ /** Canonical JSON wire format for API/database persistence. */
480
+ onDocumentJsonChange?: (document: Document2Json) => void;
466
481
  onLatexChange?: (latex: string) => void;
467
482
  };
468
483
  type Document2InitialState = {
@@ -470,7 +485,7 @@ type Document2InitialState = {
470
485
  error: string | null;
471
486
  };
472
487
  declare function resolveInitialDocument2(initialDocument: Document2Json | Document2Node | undefined, uiLocale?: ButexUiLocale, documentMeta?: Document2MetaProp): Document2InitialState;
473
- declare function ButexDocumentEditor2({ initialDocument, documentMeta, className, debug, documentDirection, equationSide, editableEquations, previewOnly, uiLocale, mathOutput, digitForm: digitFormProp, onDigitFormChange, resolveImageUrl, onDocumentChange, onLatexChange, }: ButexDocumentEditor2Props): react_jsx_runtime.JSX.Element;
488
+ declare function ButexDocumentEditor2({ initialDocument, documentMeta, className, debug, documentDirection, equationSide, editableEquations, previewOnly, uiLocale, mathOutput, digitForm: digitFormProp, onDigitFormChange, resolveImageUrl, onDocumentChange, onDocumentJsonChange, onLatexChange, }: ButexDocumentEditor2Props): react_jsx_runtime.JSX.Element;
474
489
 
475
490
  type DocumentInsertToolbarProps = {
476
491
  canUndo: boolean;
@@ -739,6 +739,66 @@ function fromMathObjectJson(json, mode = "english") {
739
739
  closing: json.closing
740
740
  };
741
741
  }
742
+ function scriptsToJson(node, path) {
743
+ return {
744
+ ...node.superscript ? { superscript: chainToJson(node.superscript, `${path}.superscript`) } : {},
745
+ ...node.subscript ? { subscript: chainToJson(node.subscript, `${path}.subscript`) } : {}
746
+ };
747
+ }
748
+ function nodeToJson(node, path) {
749
+ const scripts = scriptsToJson(node, path);
750
+ if (node instanceof CharNode || node instanceof NumberNode || node instanceof OperatorNode) {
751
+ return { node_type: node.nodeType, expr: node.expr, ...scripts };
752
+ }
753
+ if (node instanceof DelimiterNode) {
754
+ return {
755
+ node_type: "DelimiterObject",
756
+ left_delim_expr: node.leftDelimExpr,
757
+ inner_expr: chainToJson(node.innerExpr, `${path}.inner_expr`),
758
+ right_delim_expr: node.rightDelimExpr,
759
+ ...scripts
760
+ };
761
+ }
762
+ if (node instanceof CommandNode) {
763
+ return {
764
+ node_type: "CommandObject",
765
+ name: node.name,
766
+ optional_args: node.optionalArgs.map((arg, index) => chainToJson(arg, `${path}.optional_args[${String(index)}]`)),
767
+ mandatory_args: node.mandatoryArgs.map((arg, index) => chainToJson(arg, `${path}.mandatory_args[${String(index)}]`)),
768
+ ...scripts
769
+ };
770
+ }
771
+ if (node instanceof EnvNode) {
772
+ return {
773
+ node_type: "EnvObject",
774
+ opening: node.opening,
775
+ lines: node.lines.map((line, index) => chainToJson(line, `${path}.lines[${String(index)}]`)),
776
+ closing: node.closing,
777
+ ...scripts
778
+ };
779
+ }
780
+ throw new Error(`Unsupported runtime math node at ${path}: ${node.nodeType}`);
781
+ }
782
+ function chainToJson(chain, path) {
783
+ if (!(chain instanceof ChainNode)) {
784
+ throw new Error(`Expected live ChainNode at ${path}`);
785
+ }
786
+ return {
787
+ node_type: "ChainClass",
788
+ chain: chain.chain.map((node, index) => nodeToJson(node, `${path}.chain[${String(index)}]`))
789
+ };
790
+ }
791
+ function toMathObjectJson(math) {
792
+ if (math.nodeType !== "MathObject" || !Array.isArray(math.lines) || math.lines.length === 0) {
793
+ throw new Error("Cannot serialize invalid runtime MathObject");
794
+ }
795
+ return {
796
+ node_type: "MathObject",
797
+ math_mode: math.mathMode,
798
+ lines: math.lines.map((line, index) => chainToJson(line, `$.lines[${String(index)}]`)),
799
+ closing: math.closing
800
+ };
801
+ }
742
802
  function renderMathNodeLatex(math) {
743
803
  const lines = math.lines.map((line) => line.arabicLatex());
744
804
  if (math.mathMode === "$" || math.mathMode === "\\(") {
@@ -1203,10 +1263,11 @@ function createInlineField2(value = "", mathObjects = [], options = {}, path = "
1203
1263
  }
1204
1264
  const mathJson = mathObjects[mathObjectIndex];
1205
1265
  mathObjectIndex += 1;
1206
- if (mathJson && (mathJson.math_mode !== span.opening || mathJson.closing !== span.closing)) {
1266
+ const structuredMathJson = mathJson?.node_type === "MathObject" ? mathJson : null;
1267
+ if (structuredMathJson && (structuredMathJson.math_mode !== span.opening || structuredMathJson.closing !== span.closing)) {
1207
1268
  pushDiagnostic(diagnostics, options, path, "math_objects order mismatch");
1208
1269
  }
1209
- if (mathJson && mathJson.math_mode === span.opening && mathJson.closing === span.closing) {
1270
+ if (structuredMathJson && structuredMathJson.math_mode === span.opening && structuredMathJson.closing === span.closing) {
1210
1271
  tokens.push({
1211
1272
  id: document2Id("math"),
1212
1273
  kind: "math",
@@ -1214,10 +1275,12 @@ function createInlineField2(value = "", mathObjects = [], options = {}, path = "
1214
1275
  opening: span.opening,
1215
1276
  closing: span.closing,
1216
1277
  source: span.source,
1217
- sourceSide: mode,
1218
- math: fromMathObjectJson(mathJson, mode),
1278
+ sourceSide: structuredMathJson.source_side === "arabic" || structuredMathJson.source_side === "english" ? structuredMathJson.source_side : mode,
1279
+ math: fromMathObjectJson(structuredMathJson, mode),
1219
1280
  editable: true,
1220
- sourceOwner: "imported-structured"
1281
+ sourceOwner: structuredMathJson.source_owner === "editor" ? "editor" : "imported-structured",
1282
+ ...typeof structuredMathJson.label_enabled === "boolean" ? { labelEnabled: structuredMathJson.label_enabled } : {},
1283
+ ...typeof structuredMathJson.label === "string" ? { label: structuredMathJson.label } : {}
1221
1284
  });
1222
1285
  } else {
1223
1286
  tokens.push({
@@ -1230,7 +1293,9 @@ function createInlineField2(value = "", mathObjects = [], options = {}, path = "
1230
1293
  math: null,
1231
1294
  editable: false,
1232
1295
  reason: "\u0647\u0630\u0647 \u0627\u0644\u0645\u0639\u0627\u062F\u0644\u0629 \u0645\u062D\u0641\u0648\u0638\u0629 \u0643\u0646\u0635 \u062E\u0627\u0645 \u0641\u0642\u0637 \u062D\u0627\u0644\u064A\u0627",
1233
- sourceOwner: "raw"
1296
+ sourceOwner: "raw",
1297
+ ...mathJson?.node_type === "RawMathObject" && typeof mathJson.label_enabled === "boolean" ? { labelEnabled: mathJson.label_enabled } : {},
1298
+ ...mathJson?.node_type === "RawMathObject" && typeof mathJson.label === "string" ? { label: mathJson.label } : {}
1234
1299
  });
1235
1300
  }
1236
1301
  index = span.end;
@@ -9288,6 +9353,155 @@ ${body}
9288
9353
  `;
9289
9354
  }
9290
9355
 
9356
+ // src/document2/exportJson.ts
9357
+ function serializeField(field) {
9358
+ let value = "";
9359
+ let hasPersistedMath = false;
9360
+ const formats = [];
9361
+ const mathObjects = [];
9362
+ for (const token of field.tokens) {
9363
+ if (token.kind === "text") {
9364
+ const start = value.length;
9365
+ value += token.text;
9366
+ if (token.text.length > 0 && (token.style?.bold || token.style?.italic || token.style?.underline)) {
9367
+ formats.push({
9368
+ start,
9369
+ end: value.length,
9370
+ ...token.style.bold ? { bold: true } : {},
9371
+ ...token.style.italic ? { italic: true } : {},
9372
+ ...token.style.underline ? { underline: true } : {}
9373
+ });
9374
+ }
9375
+ continue;
9376
+ }
9377
+ if (token.kind === "cite") {
9378
+ value += citeTokenLatex(token.keys);
9379
+ continue;
9380
+ }
9381
+ if (token.kind === "ref") {
9382
+ value += refTokenLatex(token.keys, token.refCommand);
9383
+ continue;
9384
+ }
9385
+ value += token.source;
9386
+ if (!token.math || token.sourceOwner === "raw") {
9387
+ if (token.labelEnabled !== void 0 || token.label !== void 0) {
9388
+ hasPersistedMath = true;
9389
+ mathObjects.push({
9390
+ node_type: "RawMathObject",
9391
+ ...token.labelEnabled !== void 0 ? { label_enabled: token.labelEnabled } : {},
9392
+ ...token.label !== void 0 ? { label: token.label } : {}
9393
+ });
9394
+ } else {
9395
+ mathObjects.push(null);
9396
+ }
9397
+ continue;
9398
+ }
9399
+ hasPersistedMath = true;
9400
+ mathObjects.push({
9401
+ ...toMathObjectJson(token.math),
9402
+ ...token.sourceSide ? { source_side: token.sourceSide } : {},
9403
+ source_owner: token.sourceOwner,
9404
+ ...token.labelEnabled !== void 0 ? { label_enabled: token.labelEnabled } : {},
9405
+ ...token.label !== void 0 ? { label: token.label } : {}
9406
+ });
9407
+ }
9408
+ return { value, formats, mathObjects, hasPersistedMath };
9409
+ }
9410
+ function fieldJson(field) {
9411
+ const serialized = serializeField(field);
9412
+ return {
9413
+ value: serialized.value,
9414
+ ...serialized.formats.length > 0 ? { formats: serialized.formats } : {},
9415
+ ...serialized.hasPersistedMath ? { math_objects: serialized.mathObjects } : {}
9416
+ };
9417
+ }
9418
+ function listItemJson(item) {
9419
+ const field = fieldJson(item.field);
9420
+ return {
9421
+ value: field.value ?? "",
9422
+ ...field.formats ? { formats: field.formats } : {},
9423
+ ...field.math_objects ? { math_objects: field.math_objects } : {},
9424
+ ...item.blocks.length > 0 ? { blocks: item.blocks.map(blockJson) } : {}
9425
+ };
9426
+ }
9427
+ function blockJson(block) {
9428
+ if (block.kind === "textBlock") {
9429
+ return {
9430
+ command: block.command,
9431
+ ...fieldJson(block.field),
9432
+ ...block.command === "\\paragraph" ? { centered: block.centered === true } : {}
9433
+ };
9434
+ }
9435
+ if (block.kind === "list") {
9436
+ return {
9437
+ command: block.command,
9438
+ closing: block.closing,
9439
+ items: block.items.map(listItemJson)
9440
+ };
9441
+ }
9442
+ if (block.kind === "table") {
9443
+ const fields = block.rows.map((row) => row.map(serializeField));
9444
+ const hasPersistedMath = fields.some((row) => row.some((field) => field.hasPersistedMath));
9445
+ return {
9446
+ command: block.command,
9447
+ closing: block.closing,
9448
+ columns: block.columns,
9449
+ rows: fields.map((row) => row.map((field) => field.value)),
9450
+ ...fields.some((row) => row.some((field) => field.formats.length > 0)) ? { cell_formats: fields.map((row) => row.map((field) => field.formats)) } : {},
9451
+ ...hasPersistedMath ? { math_objects: fields.flatMap((row) => row.flatMap((field) => field.mathObjects)) } : {},
9452
+ centered: block.centered,
9453
+ caption_enabled: block.captionEnabled,
9454
+ caption: block.caption,
9455
+ label_enabled: block.labelEnabled,
9456
+ label: block.label
9457
+ };
9458
+ }
9459
+ if (block.kind === "image") {
9460
+ return {
9461
+ command: block.command,
9462
+ value: block.value,
9463
+ ...block.assetId !== void 0 ? { asset_id: block.assetId } : {},
9464
+ options: { ...block.options },
9465
+ centered: block.centered,
9466
+ caption_enabled: block.captionEnabled,
9467
+ caption: block.caption,
9468
+ label_enabled: block.labelEnabled,
9469
+ label: block.label
9470
+ };
9471
+ }
9472
+ if (block.kind === "bibliography") {
9473
+ return { command: block.command, closing: block.closing };
9474
+ }
9475
+ return { command: block.command, value: block.value };
9476
+ }
9477
+ function referenceJson(reference) {
9478
+ return {
9479
+ key: reference.key,
9480
+ authors: reference.authors,
9481
+ title: reference.title,
9482
+ year: reference.year,
9483
+ url: reference.url,
9484
+ venue: reference.venue,
9485
+ field_separator: reference.fieldSeparator
9486
+ };
9487
+ }
9488
+ function toDocumentJson2(document2) {
9489
+ if (document2.nodeType !== "DocumentObject" || !Array.isArray(document2.blocks)) {
9490
+ throw new Error("toDocumentJson2 requires a live Document2Node");
9491
+ }
9492
+ return {
9493
+ node_type: "DocumentObject",
9494
+ meta: {
9495
+ title: document2.meta.title,
9496
+ authors: document2.meta.authors,
9497
+ date: { ...document2.meta.date },
9498
+ abstract: document2.meta.abstract
9499
+ },
9500
+ references: document2.references.map(referenceJson),
9501
+ blocks: document2.blocks.map(blockJson)
9502
+ };
9503
+ }
9504
+
9291
9505
  // src/document2/history.ts
9292
9506
  var DEFAULT_DOCUMENT2_HISTORY_MAX_DEPTH = 100;
9293
9507
  function createDocument2History(maxDepth = DEFAULT_DOCUMENT2_HISTORY_MAX_DEPTH) {
@@ -9631,6 +9845,7 @@ var DOCUMENT2_MESSAGES = {
9631
9845
  imagePath: "\u0645\u0633\u0627\u0631 \u0645\u0644\u0641 \u0627\u0644\u0635\u0648\u0631\u0629",
9632
9846
  imagePathLabel: "\u0645\u0633\u0627\u0631 \u0627\u0644\u0635\u0648\u0631\u0629",
9633
9847
  loadDocumentError: "\u062A\u0639\u0630\u0631 \u062A\u062D\u0645\u064A\u0644 \u0627\u0644\u0645\u0633\u062A\u0646\u062F",
9848
+ runtimePersistenceError: "\u0644\u0627 \u064A\u0645\u0643\u0646 \u062A\u062D\u0645\u064A\u0644 Document2Node \u0628\u0639\u062F JSON.stringify. \u0627\u062D\u0641\u0638 Document2Json \u0628\u0627\u0633\u062A\u062E\u062F\u0627\u0645 toDocumentJson2.",
9634
9849
  emptyImagePath: "\u0644\u0627 \u064A\u0648\u062C\u062F \u0645\u0633\u0627\u0631 \u0635\u0648\u0631\u0629",
9635
9850
  blockSelection: "\u062A\u062D\u062F\u064A\u062F \u0627\u0644\u0643\u062A\u0644",
9636
9851
  selectBlock: "\u062A\u062D\u062F\u064A\u062F \u0627\u0644\u0643\u062A\u0644\u0629",
@@ -9774,6 +9989,7 @@ var DOCUMENT2_MESSAGES = {
9774
9989
  imagePath: "File path (\\includegraphics)",
9775
9990
  imagePathLabel: "Image path",
9776
9991
  loadDocumentError: "Could not load document",
9992
+ runtimePersistenceError: "A Document2Node cannot be loaded after JSON.stringify. Persist Document2Json with toDocumentJson2 instead.",
9777
9993
  emptyImagePath: "Empty image path",
9778
9994
  blockSelection: "Block selection",
9779
9995
  selectBlock: "Select block",
@@ -17038,7 +17254,19 @@ function formatDocument2Diagnostic(d, messages) {
17038
17254
  return `${messages.importWarning}: ${d.message} \u2014 ${messages.path}: ${d.path}`;
17039
17255
  }
17040
17256
  function isDocument2Node(value) {
17041
- return typeof value === "object" && value !== null && value.nodeType === "DocumentObject" && Array.isArray(value.blocks) && Array.isArray(value.diagnostics);
17257
+ const hasRuntimeShape = typeof value === "object" && value !== null && value.nodeType === "DocumentObject" && Array.isArray(value.blocks) && Array.isArray(value.diagnostics);
17258
+ if (!hasRuntimeShape) {
17259
+ return false;
17260
+ }
17261
+ try {
17262
+ toDocumentJson2(value);
17263
+ return true;
17264
+ } catch {
17265
+ return false;
17266
+ }
17267
+ }
17268
+ function hasDocument2RuntimeShape(value) {
17269
+ return typeof value === "object" && value !== null && value.nodeType === "DocumentObject";
17042
17270
  }
17043
17271
  function applyDocumentMetaSeed(document2, documentMeta) {
17044
17272
  const current = document2.meta ?? emptyDocument2Meta();
@@ -17057,6 +17285,13 @@ function resolveInitialDocument2(initialDocument, uiLocale = "ar", documentMeta)
17057
17285
  if (isDocument2Node(initialDocument)) {
17058
17286
  return { document: applyDocumentMetaSeed(initialDocument, documentMeta), error: null };
17059
17287
  }
17288
+ if (hasDocument2RuntimeShape(initialDocument)) {
17289
+ const messages = document2Messages(uiLocale);
17290
+ return {
17291
+ document: applyDocumentMetaSeed(createEmptyDocument2(), documentMeta),
17292
+ error: `${messages.loadDocumentError}: ${messages.runtimePersistenceError}`
17293
+ };
17294
+ }
17060
17295
  try {
17061
17296
  const imported = fromDocumentJson2(initialDocument);
17062
17297
  return {
@@ -17089,6 +17324,7 @@ function ButexDocumentEditor2({
17089
17324
  onDigitFormChange,
17090
17325
  resolveImageUrl,
17091
17326
  onDocumentChange,
17327
+ onDocumentJsonChange,
17092
17328
  onLatexChange
17093
17329
  }) {
17094
17330
  const messages = document2Messages(uiLocale);
@@ -17120,6 +17356,7 @@ function ButexDocumentEditor2({
17120
17356
  const pendingFocusRef = (0, import_react13.useRef)(null);
17121
17357
  documentRef.current = documentNode;
17122
17358
  const latex = document2Latex(documentNode);
17359
+ const documentJson = (0, import_react13.useMemo)(() => toDocumentJson2(documentNode), [documentNode]);
17123
17360
  const documentLabels = (0, import_react13.useMemo)(() => collectDocument2Labels(documentNode), [documentNode]);
17124
17361
  const preview = (0, import_react13.useMemo)(
17125
17362
  () => document2Preview(documentNode, mathOutput, equationSide, {
@@ -17204,6 +17441,9 @@ function ButexDocumentEditor2({
17204
17441
  (0, import_react13.useEffect)(() => {
17205
17442
  onDocumentChange?.(documentNode);
17206
17443
  }, [documentNode, onDocumentChange]);
17444
+ (0, import_react13.useEffect)(() => {
17445
+ onDocumentJsonChange?.(documentJson);
17446
+ }, [documentJson, onDocumentJsonChange]);
17207
17447
  (0, import_react13.useEffect)(() => {
17208
17448
  onLatexChange?.(latex);
17209
17449
  }, [latex, onLatexChange]);