@drghaliasri/butex 5.6.0 → 6.0.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.
@@ -38,8 +38,9 @@ var import_react = require("react");
38
38
 
39
39
  // src/document2/ids.ts
40
40
  var nextId = 1;
41
+ var instanceId = `${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 10)}`;
41
42
  function document2Id(prefix) {
42
- const id = `${prefix}_${String(nextId)}`;
43
+ const id = `${prefix}_${instanceId}_${String(nextId)}`;
43
44
  nextId += 1;
44
45
  return id;
45
46
  }
@@ -1204,6 +1205,19 @@ function asBlockJson(value) {
1204
1205
  }
1205
1206
  return value;
1206
1207
  }
1208
+ function blockIdFromJson(json, state) {
1209
+ const imported = typeof json.id === "string" ? json.id.trim() : "";
1210
+ if (imported.length > 0 && !state.used.has(imported)) {
1211
+ state.used.add(imported);
1212
+ return imported;
1213
+ }
1214
+ let generated = document2Id("block");
1215
+ while (state.used.has(generated)) {
1216
+ generated = document2Id("block");
1217
+ }
1218
+ state.used.add(generated);
1219
+ return generated;
1220
+ }
1207
1221
  function pushDiagnostic(diagnostics, options, path, message) {
1208
1222
  if (options.strict) {
1209
1223
  throw new Error(message);
@@ -1385,40 +1399,44 @@ function pushFormattedTextTokens(tokens, text, sourceStart, formats) {
1385
1399
  function closingForList(command) {
1386
1400
  return command === "\\begin{itemize}" ? "\\end{itemize}" : "\\end{enumerate}";
1387
1401
  }
1388
- function parseTextBlock(json, options, path, diagnostics) {
1402
+ function parseTextBlock(json, options, path, diagnostics, blockIds) {
1389
1403
  const value = requireString(json.value, `${json.command} requires string value`);
1390
1404
  return {
1391
- id: document2Id("block"),
1405
+ id: blockIdFromJson(json, blockIds),
1392
1406
  kind: "textBlock",
1393
1407
  command: json.command,
1394
1408
  field: createInlineField2(value, json.math_objects ?? [], options, `${path}.value`, diagnostics, json.command === "\\paragraph" ? json.formats ?? [] : []),
1395
1409
  ...json.centered === true ? { centered: true } : {}
1396
1410
  };
1397
1411
  }
1398
- function parseListItem(json, options, path, diagnostics) {
1412
+ function parseListItem(json, options, path, diagnostics, blockIds) {
1399
1413
  const value = requireString(json.value, "Document list item requires string value");
1400
1414
  const blocksJson = Array.isArray(json.blocks) ? json.blocks : [];
1401
1415
  return {
1402
1416
  id: document2Id("item"),
1403
1417
  field: createInlineField2(value, json.math_objects ?? [], options, `${path}.value`, diagnostics, json.formats ?? []),
1404
- blocks: blocksJson.map((block, index) => parseBlock(asBlockJson(block), options, `${path}.blocks[${String(index)}]`, diagnostics))
1418
+ blocks: blocksJson.map(
1419
+ (block, index) => parseBlock(asBlockJson(block), options, `${path}.blocks[${String(index)}]`, diagnostics, blockIds)
1420
+ )
1405
1421
  };
1406
1422
  }
1407
- function parseListBlock(json, options, path, diagnostics) {
1423
+ function parseListBlock(json, options, path, diagnostics, blockIds) {
1408
1424
  if (!Array.isArray(json.items)) {
1409
1425
  throw new Error(`${json.command} requires items array`);
1410
1426
  }
1411
1427
  const command = json.command;
1412
1428
  const closing = closingForList(command);
1413
1429
  return {
1414
- id: document2Id("block"),
1430
+ id: blockIdFromJson(json, blockIds),
1415
1431
  kind: "list",
1416
1432
  command,
1417
1433
  closing,
1418
- items: json.items.map((item, index) => parseListItem(item, options, `${path}.items[${String(index)}]`, diagnostics))
1434
+ items: json.items.map(
1435
+ (item, index) => parseListItem(item, options, `${path}.items[${String(index)}]`, diagnostics, blockIds)
1436
+ )
1419
1437
  };
1420
1438
  }
1421
- function parseTableBlock(json, options, path, diagnostics) {
1439
+ function parseTableBlock(json, options, path, diagnostics, blockIds) {
1422
1440
  if (!Array.isArray(json.rows)) {
1423
1441
  throw new Error("\\begin{tabular} requires rows array");
1424
1442
  }
@@ -1441,7 +1459,7 @@ function parseTableBlock(json, options, path, diagnostics) {
1441
1459
  pushDiagnostic(diagnostics, options, path, `math_objects count mismatch: detected ${String(mathObjectIndex)}, got ${String(mathObjects.length)}`);
1442
1460
  }
1443
1461
  return {
1444
- id: document2Id("block"),
1462
+ id: blockIdFromJson(json, blockIds),
1445
1463
  kind: "table",
1446
1464
  command: "\\begin{tabular}",
1447
1465
  closing: "\\end{tabular}",
@@ -1450,11 +1468,11 @@ function parseTableBlock(json, options, path, diagnostics) {
1450
1468
  ...parseFloatMetaFromJson(json)
1451
1469
  };
1452
1470
  }
1453
- function parseImageBlock(json) {
1471
+ function parseImageBlock(json, blockIds) {
1454
1472
  const assetId = typeof json.asset_id === "string" && json.asset_id.length > 0 ? json.asset_id : void 0;
1455
1473
  const value = assetId !== void 0 ? typeof json.value === "string" ? json.value : "" : requireString(json.value, "\\includegraphics requires string value");
1456
1474
  return {
1457
- id: document2Id("block"),
1475
+ id: blockIdFromJson(json, blockIds),
1458
1476
  kind: "image",
1459
1477
  command: "\\includegraphics",
1460
1478
  value,
@@ -1463,43 +1481,43 @@ function parseImageBlock(json) {
1463
1481
  ...parseFloatMetaFromJson(json)
1464
1482
  };
1465
1483
  }
1466
- function parseRawBlock(json) {
1484
+ function parseRawBlock(json, blockIds) {
1467
1485
  return {
1468
- id: document2Id("block"),
1486
+ id: blockIdFromJson(json, blockIds),
1469
1487
  kind: "raw",
1470
1488
  command: "\\raw",
1471
1489
  value: typeof json.value === "string" ? json.value : ""
1472
1490
  };
1473
1491
  }
1474
- function parseBibliographyBlock() {
1492
+ function parseBibliographyBlock(json, blockIds) {
1475
1493
  return {
1476
- id: document2Id("block"),
1494
+ id: blockIdFromJson(json, blockIds),
1477
1495
  kind: "bibliography",
1478
1496
  command: "\\begin{thebibliography}",
1479
1497
  closing: "\\end{thebibliography}"
1480
1498
  };
1481
1499
  }
1482
- function parseBlock(json, options, path, diagnostics) {
1500
+ function parseBlock(json, options, path, diagnostics, blockIds) {
1483
1501
  if (TEXT_COMMANDS.has(json.command)) {
1484
- return parseTextBlock(json, options, path, diagnostics);
1502
+ return parseTextBlock(json, options, path, diagnostics, blockIds);
1485
1503
  }
1486
1504
  if (LIST_COMMANDS.has(json.command)) {
1487
- return parseListBlock(json, options, path, diagnostics);
1505
+ return parseListBlock(json, options, path, diagnostics, blockIds);
1488
1506
  }
1489
1507
  if (json.command === "\\begin{tabular}") {
1490
- return parseTableBlock(json, options, path, diagnostics);
1508
+ return parseTableBlock(json, options, path, diagnostics, blockIds);
1491
1509
  }
1492
1510
  if (json.command === "\\includegraphics") {
1493
- return parseImageBlock(json);
1511
+ return parseImageBlock(json, blockIds);
1494
1512
  }
1495
1513
  if (json.command === "\\begin{thebibliography}" || json.command === "\\bibliography") {
1496
- return parseBibliographyBlock();
1514
+ return parseBibliographyBlock(json, blockIds);
1497
1515
  }
1498
1516
  if (json.command === "\\raw") {
1499
- return parseRawBlock(json);
1517
+ return parseRawBlock(json, blockIds);
1500
1518
  }
1501
1519
  return {
1502
- id: document2Id("block"),
1520
+ id: blockIdFromJson(json, blockIds),
1503
1521
  kind: "raw",
1504
1522
  command: "\\raw",
1505
1523
  value: typeof json.value === "string" ? json.value : json.command
@@ -1513,11 +1531,14 @@ function fromDocumentJson2(json, options = {}) {
1513
1531
  throw new Error("DocumentObject requires blocks array");
1514
1532
  }
1515
1533
  const diagnostics = [];
1534
+ const blockIds = { used: /* @__PURE__ */ new Set() };
1516
1535
  return {
1517
1536
  nodeType: "DocumentObject",
1518
1537
  meta: normalizeDocument2Meta(json.meta),
1519
1538
  references: parseReferences(json.references),
1520
- blocks: json.blocks.map((block, index) => parseBlock(asBlockJson(block), options, `$.blocks[${String(index)}]`, diagnostics)),
1539
+ blocks: json.blocks.map(
1540
+ (block, index) => parseBlock(asBlockJson(block), options, `$.blocks[${String(index)}]`, diagnostics, blockIds)
1541
+ ),
1521
1542
  diagnostics
1522
1543
  };
1523
1544
  }
@@ -8690,12 +8711,23 @@ function addDocument2TableBlock(document2, columns = "lll", rowCount = 3, colCou
8690
8711
  };
8691
8712
  return insertDocument2BlockAfter(document2, afterBlockId, block);
8692
8713
  }
8693
- function addDocument2ImageBlock(document2, src = "", afterBlockId) {
8714
+ function normalizeImageInput(srcOrAsset) {
8715
+ if (typeof srcOrAsset === "string") {
8716
+ return srcOrAsset.length > 0 ? { value: srcOrAsset, assetId: srcOrAsset } : { value: "" };
8717
+ }
8718
+ if (srcOrAsset && srcOrAsset.assetId.length > 0) {
8719
+ return { value: srcOrAsset.value ?? srcOrAsset.assetId, assetId: srcOrAsset.assetId };
8720
+ }
8721
+ return { value: "" };
8722
+ }
8723
+ function addDocument2ImageBlock(document2, srcOrAsset, afterBlockId) {
8724
+ const image = normalizeImageInput(srcOrAsset);
8694
8725
  const block = {
8695
8726
  id: document2Id("block"),
8696
8727
  kind: "image",
8697
8728
  command: "\\includegraphics",
8698
- value: src,
8729
+ value: image.value,
8730
+ ...image.assetId !== void 0 ? { assetId: image.assetId } : {},
8699
8731
  options: { width: "0.8\\columnwidth" },
8700
8732
  ...defaultFloatMeta()
8701
8733
  };
@@ -8722,6 +8754,55 @@ function updateDocument2ImageValue(document2, blockId, value) {
8722
8754
  visit(next.blocks);
8723
8755
  return next;
8724
8756
  }
8757
+ function updateDocument2ImageAsset(document2, blockId, asset) {
8758
+ const next = cloneDocument(document2);
8759
+ const image = normalizeImageInput(asset);
8760
+ function visit(blocks) {
8761
+ for (const block of blocks) {
8762
+ if (block.id === blockId && block.kind === "image") {
8763
+ block.value = image.value;
8764
+ if (image.assetId !== void 0) {
8765
+ block.assetId = image.assetId;
8766
+ } else {
8767
+ delete block.assetId;
8768
+ }
8769
+ return true;
8770
+ }
8771
+ if (block.kind === "list") {
8772
+ for (const item of block.items) {
8773
+ if (visit(item.blocks)) {
8774
+ return true;
8775
+ }
8776
+ }
8777
+ }
8778
+ }
8779
+ return false;
8780
+ }
8781
+ visit(next.blocks);
8782
+ return next;
8783
+ }
8784
+ function clearDocument2ImageAsset(document2, blockId) {
8785
+ const next = cloneDocument(document2);
8786
+ function visit(blocks) {
8787
+ for (const block of blocks) {
8788
+ if (block.id === blockId && block.kind === "image") {
8789
+ block.value = "";
8790
+ delete block.assetId;
8791
+ return true;
8792
+ }
8793
+ if (block.kind === "list") {
8794
+ for (const item of block.items) {
8795
+ if (visit(item.blocks)) {
8796
+ return true;
8797
+ }
8798
+ }
8799
+ }
8800
+ }
8801
+ return false;
8802
+ }
8803
+ visit(next.blocks);
8804
+ return next;
8805
+ }
8725
8806
  function updateDocument2ImageMeta(document2, blockId, patch) {
8726
8807
  const next = cloneDocument(document2);
8727
8808
  function visit(blocks) {
@@ -9017,6 +9098,159 @@ function updateDocument2Meta(document2, patch) {
9017
9098
  return next;
9018
9099
  }
9019
9100
 
9101
+ // src/document2/exportJson.ts
9102
+ function serializeField(field) {
9103
+ let value = "";
9104
+ let hasPersistedMath = false;
9105
+ const formats = [];
9106
+ const mathObjects = [];
9107
+ for (const token of field.tokens) {
9108
+ if (token.kind === "text") {
9109
+ const start = value.length;
9110
+ value += token.text;
9111
+ if (token.text.length > 0 && (token.style?.bold || token.style?.italic || token.style?.underline)) {
9112
+ formats.push({
9113
+ start,
9114
+ end: value.length,
9115
+ ...token.style.bold ? { bold: true } : {},
9116
+ ...token.style.italic ? { italic: true } : {},
9117
+ ...token.style.underline ? { underline: true } : {}
9118
+ });
9119
+ }
9120
+ continue;
9121
+ }
9122
+ if (token.kind === "cite") {
9123
+ value += citeTokenLatex(token.keys);
9124
+ continue;
9125
+ }
9126
+ if (token.kind === "ref") {
9127
+ value += refTokenLatex(token.keys, token.refCommand);
9128
+ continue;
9129
+ }
9130
+ value += token.source;
9131
+ if (!token.math || token.sourceOwner === "raw") {
9132
+ if (token.labelEnabled !== void 0 || token.label !== void 0) {
9133
+ hasPersistedMath = true;
9134
+ mathObjects.push({
9135
+ node_type: "RawMathObject",
9136
+ ...token.labelEnabled !== void 0 ? { label_enabled: token.labelEnabled } : {},
9137
+ ...token.label !== void 0 ? { label: token.label } : {}
9138
+ });
9139
+ } else {
9140
+ mathObjects.push(null);
9141
+ }
9142
+ continue;
9143
+ }
9144
+ hasPersistedMath = true;
9145
+ mathObjects.push({
9146
+ ...toMathObjectJson(token.math),
9147
+ ...token.sourceSide ? { source_side: token.sourceSide } : {},
9148
+ source_owner: token.sourceOwner,
9149
+ ...token.labelEnabled !== void 0 ? { label_enabled: token.labelEnabled } : {},
9150
+ ...token.label !== void 0 ? { label: token.label } : {}
9151
+ });
9152
+ }
9153
+ return { value, formats, mathObjects, hasPersistedMath };
9154
+ }
9155
+ function fieldJson(field) {
9156
+ const serialized = serializeField(field);
9157
+ return {
9158
+ value: serialized.value,
9159
+ ...serialized.formats.length > 0 ? { formats: serialized.formats } : {},
9160
+ ...serialized.hasPersistedMath ? { math_objects: serialized.mathObjects } : {}
9161
+ };
9162
+ }
9163
+ function listItemJson(item) {
9164
+ const field = fieldJson(item.field);
9165
+ return {
9166
+ value: field.value ?? "",
9167
+ ...field.formats ? { formats: field.formats } : {},
9168
+ ...field.math_objects ? { math_objects: field.math_objects } : {},
9169
+ ...item.blocks.length > 0 ? { blocks: item.blocks.map(blockJson) } : {}
9170
+ };
9171
+ }
9172
+ function blockJson(block) {
9173
+ if (block.kind === "textBlock") {
9174
+ return {
9175
+ id: block.id,
9176
+ command: block.command,
9177
+ ...fieldJson(block.field),
9178
+ ...block.command === "\\paragraph" ? { centered: block.centered === true } : {}
9179
+ };
9180
+ }
9181
+ if (block.kind === "list") {
9182
+ return {
9183
+ id: block.id,
9184
+ command: block.command,
9185
+ closing: block.closing,
9186
+ items: block.items.map(listItemJson)
9187
+ };
9188
+ }
9189
+ if (block.kind === "table") {
9190
+ const fields = block.rows.map((row) => row.map(serializeField));
9191
+ const hasPersistedMath = fields.some((row) => row.some((field) => field.hasPersistedMath));
9192
+ return {
9193
+ id: block.id,
9194
+ command: block.command,
9195
+ closing: block.closing,
9196
+ columns: block.columns,
9197
+ rows: fields.map((row) => row.map((field) => field.value)),
9198
+ ...fields.some((row) => row.some((field) => field.formats.length > 0)) ? { cell_formats: fields.map((row) => row.map((field) => field.formats)) } : {},
9199
+ ...hasPersistedMath ? { math_objects: fields.flatMap((row) => row.flatMap((field) => field.mathObjects)) } : {},
9200
+ centered: block.centered,
9201
+ caption_enabled: block.captionEnabled,
9202
+ caption: block.caption,
9203
+ label_enabled: block.labelEnabled,
9204
+ label: block.label
9205
+ };
9206
+ }
9207
+ if (block.kind === "image") {
9208
+ return {
9209
+ id: block.id,
9210
+ command: block.command,
9211
+ value: block.value,
9212
+ ...block.assetId !== void 0 ? { asset_id: block.assetId } : {},
9213
+ options: { ...block.options },
9214
+ centered: block.centered,
9215
+ caption_enabled: block.captionEnabled,
9216
+ caption: block.caption,
9217
+ label_enabled: block.labelEnabled,
9218
+ label: block.label
9219
+ };
9220
+ }
9221
+ if (block.kind === "bibliography") {
9222
+ return { id: block.id, command: block.command, closing: block.closing };
9223
+ }
9224
+ return { id: block.id, command: block.command, value: block.value };
9225
+ }
9226
+ function referenceJson(reference) {
9227
+ return {
9228
+ key: reference.key,
9229
+ authors: reference.authors,
9230
+ title: reference.title,
9231
+ year: reference.year,
9232
+ url: reference.url,
9233
+ venue: reference.venue,
9234
+ field_separator: reference.fieldSeparator
9235
+ };
9236
+ }
9237
+ function toDocumentJson2(document2) {
9238
+ if (document2.nodeType !== "DocumentObject" || !Array.isArray(document2.blocks)) {
9239
+ throw new Error("toDocumentJson2 requires a live Document2Node");
9240
+ }
9241
+ return {
9242
+ node_type: "DocumentObject",
9243
+ meta: {
9244
+ title: document2.meta.title,
9245
+ authors: document2.meta.authors,
9246
+ date: { ...document2.meta.date },
9247
+ abstract: document2.meta.abstract
9248
+ },
9249
+ references: document2.references.map(referenceJson),
9250
+ blocks: document2.blocks.map(blockJson)
9251
+ };
9252
+ }
9253
+
9020
9254
  // src/document2/keys.ts
9021
9255
  var DOCUMENT2_KEY_PATTERN = /^[\p{L}\p{M}0-9:._-]+$/u;
9022
9256
  function normalizeDocument2Key(key) {
@@ -9497,155 +9731,6 @@ ${body}
9497
9731
  `;
9498
9732
  }
9499
9733
 
9500
- // src/document2/exportJson.ts
9501
- function serializeField(field) {
9502
- let value = "";
9503
- let hasPersistedMath = false;
9504
- const formats = [];
9505
- const mathObjects = [];
9506
- for (const token of field.tokens) {
9507
- if (token.kind === "text") {
9508
- const start = value.length;
9509
- value += token.text;
9510
- if (token.text.length > 0 && (token.style?.bold || token.style?.italic || token.style?.underline)) {
9511
- formats.push({
9512
- start,
9513
- end: value.length,
9514
- ...token.style.bold ? { bold: true } : {},
9515
- ...token.style.italic ? { italic: true } : {},
9516
- ...token.style.underline ? { underline: true } : {}
9517
- });
9518
- }
9519
- continue;
9520
- }
9521
- if (token.kind === "cite") {
9522
- value += citeTokenLatex(token.keys);
9523
- continue;
9524
- }
9525
- if (token.kind === "ref") {
9526
- value += refTokenLatex(token.keys, token.refCommand);
9527
- continue;
9528
- }
9529
- value += token.source;
9530
- if (!token.math || token.sourceOwner === "raw") {
9531
- if (token.labelEnabled !== void 0 || token.label !== void 0) {
9532
- hasPersistedMath = true;
9533
- mathObjects.push({
9534
- node_type: "RawMathObject",
9535
- ...token.labelEnabled !== void 0 ? { label_enabled: token.labelEnabled } : {},
9536
- ...token.label !== void 0 ? { label: token.label } : {}
9537
- });
9538
- } else {
9539
- mathObjects.push(null);
9540
- }
9541
- continue;
9542
- }
9543
- hasPersistedMath = true;
9544
- mathObjects.push({
9545
- ...toMathObjectJson(token.math),
9546
- ...token.sourceSide ? { source_side: token.sourceSide } : {},
9547
- source_owner: token.sourceOwner,
9548
- ...token.labelEnabled !== void 0 ? { label_enabled: token.labelEnabled } : {},
9549
- ...token.label !== void 0 ? { label: token.label } : {}
9550
- });
9551
- }
9552
- return { value, formats, mathObjects, hasPersistedMath };
9553
- }
9554
- function fieldJson(field) {
9555
- const serialized = serializeField(field);
9556
- return {
9557
- value: serialized.value,
9558
- ...serialized.formats.length > 0 ? { formats: serialized.formats } : {},
9559
- ...serialized.hasPersistedMath ? { math_objects: serialized.mathObjects } : {}
9560
- };
9561
- }
9562
- function listItemJson(item) {
9563
- const field = fieldJson(item.field);
9564
- return {
9565
- value: field.value ?? "",
9566
- ...field.formats ? { formats: field.formats } : {},
9567
- ...field.math_objects ? { math_objects: field.math_objects } : {},
9568
- ...item.blocks.length > 0 ? { blocks: item.blocks.map(blockJson) } : {}
9569
- };
9570
- }
9571
- function blockJson(block) {
9572
- if (block.kind === "textBlock") {
9573
- return {
9574
- command: block.command,
9575
- ...fieldJson(block.field),
9576
- ...block.command === "\\paragraph" ? { centered: block.centered === true } : {}
9577
- };
9578
- }
9579
- if (block.kind === "list") {
9580
- return {
9581
- command: block.command,
9582
- closing: block.closing,
9583
- items: block.items.map(listItemJson)
9584
- };
9585
- }
9586
- if (block.kind === "table") {
9587
- const fields = block.rows.map((row) => row.map(serializeField));
9588
- const hasPersistedMath = fields.some((row) => row.some((field) => field.hasPersistedMath));
9589
- return {
9590
- command: block.command,
9591
- closing: block.closing,
9592
- columns: block.columns,
9593
- rows: fields.map((row) => row.map((field) => field.value)),
9594
- ...fields.some((row) => row.some((field) => field.formats.length > 0)) ? { cell_formats: fields.map((row) => row.map((field) => field.formats)) } : {},
9595
- ...hasPersistedMath ? { math_objects: fields.flatMap((row) => row.flatMap((field) => field.mathObjects)) } : {},
9596
- centered: block.centered,
9597
- caption_enabled: block.captionEnabled,
9598
- caption: block.caption,
9599
- label_enabled: block.labelEnabled,
9600
- label: block.label
9601
- };
9602
- }
9603
- if (block.kind === "image") {
9604
- return {
9605
- command: block.command,
9606
- value: block.value,
9607
- ...block.assetId !== void 0 ? { asset_id: block.assetId } : {},
9608
- options: { ...block.options },
9609
- centered: block.centered,
9610
- caption_enabled: block.captionEnabled,
9611
- caption: block.caption,
9612
- label_enabled: block.labelEnabled,
9613
- label: block.label
9614
- };
9615
- }
9616
- if (block.kind === "bibliography") {
9617
- return { command: block.command, closing: block.closing };
9618
- }
9619
- return { command: block.command, value: block.value };
9620
- }
9621
- function referenceJson(reference) {
9622
- return {
9623
- key: reference.key,
9624
- authors: reference.authors,
9625
- title: reference.title,
9626
- year: reference.year,
9627
- url: reference.url,
9628
- venue: reference.venue,
9629
- field_separator: reference.fieldSeparator
9630
- };
9631
- }
9632
- function toDocumentJson2(document2) {
9633
- if (document2.nodeType !== "DocumentObject" || !Array.isArray(document2.blocks)) {
9634
- throw new Error("toDocumentJson2 requires a live Document2Node");
9635
- }
9636
- return {
9637
- node_type: "DocumentObject",
9638
- meta: {
9639
- title: document2.meta.title,
9640
- authors: document2.meta.authors,
9641
- date: { ...document2.meta.date },
9642
- abstract: document2.meta.abstract
9643
- },
9644
- references: document2.references.map(referenceJson),
9645
- blocks: document2.blocks.map(blockJson)
9646
- };
9647
- }
9648
-
9649
9734
  // src/document2/history.ts
9650
9735
  var DEFAULT_DOCUMENT2_HISTORY_MAX_DEPTH = 100;
9651
9736
  function createDocument2History(maxDepth = DEFAULT_DOCUMENT2_HISTORY_MAX_DEPTH) {
@@ -9991,6 +10076,15 @@ var DOCUMENT2_MESSAGES = {
9991
10076
  loadDocumentError: "\u062A\u0639\u0630\u0631 \u062A\u062D\u0645\u064A\u0644 \u0627\u0644\u0645\u0633\u062A\u0646\u062F",
9992
10077
  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.",
9993
10078
  emptyImagePath: "\u0644\u0627 \u064A\u0648\u062C\u062F \u0645\u0633\u0627\u0631 \u0635\u0648\u0631\u0629",
10079
+ noImageSelected: "\u0644\u0627 \u0635\u0648\u0631\u0629 \u0645\u062D\u062F\u062F\u0629",
10080
+ chooseImage: "\u0627\u062E\u062A\u064A\u0627\u0631 \u0635\u0648\u0631\u0629",
10081
+ changeImage: "\u062A\u063A\u064A\u064A\u0631",
10082
+ removeImage: "\u0625\u0632\u0627\u0644\u0629",
10083
+ selectImageAsset: "\u0627\u062E\u062A\u064A\u0627\u0631 \u0635\u0648\u0631\u0629",
10084
+ noImageAssets: "\u0644\u0627 \u062A\u0648\u062C\u062F \u0635\u0648\u0631 \u0645\u062A\u0627\u062D\u0629",
10085
+ loadingImageAssets: "\u062C\u0627\u0631 \u062A\u062D\u0645\u064A\u0644 \u0627\u0644\u0635\u0648\u0631\u2026",
10086
+ advancedImagePath: "\u062E\u064A\u0627\u0631\u0627\u062A \u0645\u062A\u0642\u062F\u0645\u0629",
10087
+ imagePreviewAlt: "\u0645\u0639\u0627\u064A\u0646\u0629 \u0627\u0644\u0635\u0648\u0631\u0629",
9994
10088
  blockSelection: "\u062A\u062D\u062F\u064A\u062F \u0627\u0644\u0643\u062A\u0644",
9995
10089
  selectBlock: "\u062A\u062D\u062F\u064A\u062F \u0627\u0644\u0643\u062A\u0644\u0629",
9996
10090
  selectedBlockCount: "\u0645\u062D\u062F\u062F\u0629",
@@ -10135,6 +10229,15 @@ var DOCUMENT2_MESSAGES = {
10135
10229
  loadDocumentError: "Could not load document",
10136
10230
  runtimePersistenceError: "A Document2Node cannot be loaded after JSON.stringify. Persist Document2Json with toDocumentJson2 instead.",
10137
10231
  emptyImagePath: "Empty image path",
10232
+ noImageSelected: "No image selected",
10233
+ chooseImage: "Choose image",
10234
+ changeImage: "Change",
10235
+ removeImage: "Remove",
10236
+ selectImageAsset: "Select image",
10237
+ noImageAssets: "No images available",
10238
+ loadingImageAssets: "Loading images\u2026",
10239
+ advancedImagePath: "Advanced",
10240
+ imagePreviewAlt: "Image preview",
10138
10241
  blockSelection: "Block selection",
10139
10242
  selectBlock: "Select block",
10140
10243
  selectedBlockCount: "selected",
@@ -11623,6 +11726,167 @@ function FloatMetaEditor({
11623
11726
  ] }) : null
11624
11727
  ] });
11625
11728
  }
11729
+ function ImageAssetEditor({
11730
+ block,
11731
+ uiLocale,
11732
+ messages,
11733
+ resolveImageUrl,
11734
+ onRequestImagePick,
11735
+ listImageAssets,
11736
+ renderImageBlockEditor,
11737
+ onSelectAsset,
11738
+ onClearAsset,
11739
+ onImageSrcChange,
11740
+ onBlockFocus
11741
+ }) {
11742
+ const [assets, setAssets] = (0, import_react4.useState)([]);
11743
+ const [assetsLoaded, setAssetsLoaded] = (0, import_react4.useState)(false);
11744
+ const [assetsLoading, setAssetsLoading] = (0, import_react4.useState)(false);
11745
+ const [advancedOpen, setAdvancedOpen] = (0, import_react4.useState)(false);
11746
+ const hasImage = block.value.length > 0 || Boolean(block.assetId);
11747
+ const resolvedUrl = hasImage ? resolveImageUrl ? resolveImageUrl({ assetId: block.assetId, value: block.value }) : block.value : "";
11748
+ const current = block.assetId ? { assetId: block.assetId, value: block.value } : null;
11749
+ const canUsePicker = Boolean(onRequestImagePick);
11750
+ (0, import_react4.useEffect)(() => {
11751
+ let alive = true;
11752
+ if (!listImageAssets || onRequestImagePick) {
11753
+ setAssets([]);
11754
+ setAssetsLoaded(false);
11755
+ setAssetsLoading(false);
11756
+ return () => {
11757
+ alive = false;
11758
+ };
11759
+ }
11760
+ setAssetsLoading(true);
11761
+ Promise.resolve(listImageAssets()).then((nextAssets) => {
11762
+ if (!alive) {
11763
+ return;
11764
+ }
11765
+ setAssets(nextAssets);
11766
+ setAssetsLoaded(true);
11767
+ }).catch(() => {
11768
+ if (!alive) {
11769
+ return;
11770
+ }
11771
+ setAssets([]);
11772
+ setAssetsLoaded(true);
11773
+ }).finally(() => {
11774
+ if (alive) {
11775
+ setAssetsLoading(false);
11776
+ }
11777
+ });
11778
+ return () => {
11779
+ alive = false;
11780
+ };
11781
+ }, [listImageAssets, onRequestImagePick]);
11782
+ async function requestImagePick() {
11783
+ if (!onRequestImagePick) {
11784
+ return;
11785
+ }
11786
+ const nextAsset = await onRequestImagePick({ blockId: block.id, current });
11787
+ if (nextAsset) {
11788
+ onSelectAsset?.(block.id, nextAsset);
11789
+ }
11790
+ }
11791
+ function selectListedAsset(assetId) {
11792
+ const nextAsset = assets.find((asset) => asset.assetId === assetId);
11793
+ if (nextAsset) {
11794
+ onSelectAsset?.(block.id, nextAsset);
11795
+ }
11796
+ }
11797
+ const imageFieldId = `butex-d2-img-${block.id}`;
11798
+ const selectId = `butex-d2-img-asset-${block.id}`;
11799
+ const canSelectListedAsset = !canUsePicker && assets.length > 0;
11800
+ function activateImagePicker() {
11801
+ if (canUsePicker) {
11802
+ void requestImagePick();
11803
+ return;
11804
+ }
11805
+ if (canSelectListedAsset && typeof document !== "undefined") {
11806
+ document.getElementById(selectId)?.focus();
11807
+ }
11808
+ }
11809
+ const defaultUi = /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "butex-document2-widget__image-editor", children: [
11810
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: `butex-document2-widget__image-asset ${hasImage ? "butex-document2-widget__image-asset--filled" : "butex-document2-widget__image-asset--empty"}`, children: hasImage ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
11811
+ resolvedUrl ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("img", { className: "butex-document2-widget__image-thumb", src: resolvedUrl, alt: messages.imagePreviewAlt }) : null,
11812
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "butex-document2-widget__image-asset-main", children: [
11813
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "butex-document2-widget__image-asset-title", dir: "ltr", children: block.assetId ?? block.value }),
11814
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "butex-document2-widget__image-actions", children: [
11815
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("button", { type: "button", onClick: activateImagePicker, disabled: !canUsePicker && !canSelectListedAsset, children: messages.changeImage }),
11816
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("button", { type: "button", className: "butex-document2-widget__button--danger", onClick: () => onClearAsset?.(block.id), children: messages.removeImage })
11817
+ ] })
11818
+ ] })
11819
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "butex-document2-widget__image-empty-content", children: [
11820
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("strong", { children: messages.noImageSelected }),
11821
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("button", { type: "button", onClick: activateImagePicker, disabled: !canUsePicker && !canSelectListedAsset, children: messages.chooseImage })
11822
+ ] }) }),
11823
+ !canUsePicker ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("label", { className: "butex-document2-widget__image-select", htmlFor: selectId, children: [
11824
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { children: messages.selectImageAsset }),
11825
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
11826
+ "select",
11827
+ {
11828
+ id: selectId,
11829
+ value: "",
11830
+ disabled: !canSelectListedAsset,
11831
+ onFocus: () => onBlockFocus?.(block.id),
11832
+ onChange: (event) => selectListedAsset(event.currentTarget.value),
11833
+ children: [
11834
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("option", { value: "", children: assetsLoading ? messages.loadingImageAssets : assetsLoaded && assets.length === 0 ? messages.noImageAssets : messages.selectImageAsset }),
11835
+ assets.map((asset) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("option", { value: asset.assetId, children: asset.label ?? asset.value ?? asset.assetId }, asset.assetId))
11836
+ ]
11837
+ }
11838
+ )
11839
+ ] }) : null,
11840
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
11841
+ "details",
11842
+ {
11843
+ className: "butex-document2-widget__image-advanced",
11844
+ open: advancedOpen,
11845
+ onToggle: (event) => setAdvancedOpen(event.currentTarget.open),
11846
+ children: [
11847
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("summary", { children: messages.advancedImagePath }),
11848
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("label", { className: "butex-document2-widget__image-src-label", htmlFor: imageFieldId, children: messages.imagePath }),
11849
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
11850
+ "textarea",
11851
+ {
11852
+ id: imageFieldId,
11853
+ className: "butex-document2-widget__inline-text",
11854
+ value: block.value,
11855
+ dir: "ltr",
11856
+ "aria-label": messages.imagePathLabel,
11857
+ rows: 1,
11858
+ ref: (element) => {
11859
+ if (element) {
11860
+ element.style.height = "auto";
11861
+ element.style.height = `${String(element.scrollHeight)}px`;
11862
+ }
11863
+ },
11864
+ onFocus: () => onBlockFocus?.(block.id),
11865
+ onChange: (event) => {
11866
+ onImageSrcChange?.(block.id, event.currentTarget.value);
11867
+ event.currentTarget.style.height = "auto";
11868
+ event.currentTarget.style.height = `${String(event.currentTarget.scrollHeight)}px`;
11869
+ }
11870
+ }
11871
+ )
11872
+ ]
11873
+ }
11874
+ )
11875
+ ] });
11876
+ if (renderImageBlockEditor) {
11877
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_jsx_runtime7.Fragment, { children: renderImageBlockEditor({
11878
+ blockId: block.id,
11879
+ assetId: block.assetId,
11880
+ value: block.value,
11881
+ resolvedUrl,
11882
+ uiLocale,
11883
+ onSelectAsset: (asset) => onSelectAsset?.(block.id, asset),
11884
+ onClearAsset: () => onClearAsset?.(block.id),
11885
+ defaultUi
11886
+ }) });
11887
+ }
11888
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_jsx_runtime7.Fragment, { children: defaultUi });
11889
+ }
11626
11890
  function blockSummary(block, referenceCount, messages) {
11627
11891
  if (block.kind === "textBlock") {
11628
11892
  return inlineFieldSummary(block.field);
@@ -11671,6 +11935,12 @@ function BlockEditor({
11671
11935
  onRefFocus,
11672
11936
  onFieldBlur,
11673
11937
  onImageSrcChange,
11938
+ onImageAssetChange,
11939
+ onImageAssetClear,
11940
+ resolveImageUrl,
11941
+ onRequestImagePick,
11942
+ listImageAssets,
11943
+ renderImageBlockEditor,
11674
11944
  onFloatMetaChange,
11675
11945
  onParagraphCenteredChange,
11676
11946
  onAddListItem,
@@ -11813,6 +12083,12 @@ function BlockEditor({
11813
12083
  onCiteFocus,
11814
12084
  onFieldBlur,
11815
12085
  onImageSrcChange,
12086
+ onImageAssetChange,
12087
+ onImageAssetClear,
12088
+ resolveImageUrl,
12089
+ onRequestImagePick,
12090
+ listImageAssets,
12091
+ renderImageBlockEditor,
11816
12092
  onAddListItem,
11817
12093
  onRemoveListItem,
11818
12094
  onManageReferences
@@ -11851,31 +12127,22 @@ function BlockEditor({
11851
12127
  ] });
11852
12128
  }
11853
12129
  if (block.kind === "image") {
11854
- const imageFieldId = `butex-d2-img-${block.id}`;
11855
12130
  return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("section", { className: chromeClass, children: [
11856
12131
  header,
11857
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("label", { className: "butex-document2-widget__image-src-label", htmlFor: imageFieldId, children: messages.imagePath }),
11858
12132
  /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
11859
- "textarea",
12133
+ ImageAssetEditor,
11860
12134
  {
11861
- id: imageFieldId,
11862
- className: "butex-document2-widget__inline-text",
11863
- value: block.value,
11864
- dir: "ltr",
11865
- "aria-label": messages.imagePathLabel,
11866
- rows: 1,
11867
- ref: (element) => {
11868
- if (element) {
11869
- element.style.height = "auto";
11870
- element.style.height = `${String(element.scrollHeight)}px`;
11871
- }
11872
- },
11873
- onFocus: () => onBlockFocus?.(block.id),
11874
- onChange: (event) => {
11875
- onImageSrcChange?.(block.id, event.currentTarget.value);
11876
- event.currentTarget.style.height = "auto";
11877
- event.currentTarget.style.height = `${String(event.currentTarget.scrollHeight)}px`;
11878
- }
12135
+ block,
12136
+ uiLocale,
12137
+ messages,
12138
+ resolveImageUrl,
12139
+ onRequestImagePick,
12140
+ listImageAssets,
12141
+ renderImageBlockEditor,
12142
+ onSelectAsset: onImageAssetChange,
12143
+ onClearAsset: onImageAssetClear,
12144
+ onImageSrcChange,
12145
+ onBlockFocus
11879
12146
  }
11880
12147
  ),
11881
12148
  /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
@@ -16750,6 +17017,94 @@ var DOCUMENT2_WIDGET_CSS = `
16750
17017
  border-color: var(--butex-document2-dev-border);
16751
17018
  }
16752
17019
 
17020
+ .butex-document2-widget__image-editor {
17021
+ display: grid;
17022
+ gap: 10px;
17023
+ min-width: 0;
17024
+ }
17025
+
17026
+ .butex-document2-widget__image-asset {
17027
+ align-items: center;
17028
+ border: 1px solid var(--butex-document2-border);
17029
+ border-radius: 8px;
17030
+ display: flex;
17031
+ gap: 12px;
17032
+ min-height: 128px;
17033
+ min-width: 0;
17034
+ padding: 12px;
17035
+ }
17036
+
17037
+ .butex-document2-widget__image-asset--empty {
17038
+ border-style: dashed;
17039
+ justify-content: center;
17040
+ }
17041
+
17042
+ .butex-document2-widget__image-empty-content,
17043
+ .butex-document2-widget__image-asset-main,
17044
+ .butex-document2-widget__image-select,
17045
+ .butex-document2-widget__image-advanced {
17046
+ display: grid;
17047
+ gap: 8px;
17048
+ min-width: 0;
17049
+ }
17050
+
17051
+ .butex-document2-widget__image-empty-content {
17052
+ justify-items: center;
17053
+ text-align: center;
17054
+ }
17055
+
17056
+ .butex-document2-widget__image-thumb {
17057
+ aspect-ratio: 4 / 3;
17058
+ background: var(--butex-document2-bg);
17059
+ border: 1px solid var(--butex-document2-border);
17060
+ border-radius: 8px;
17061
+ flex: 0 0 128px;
17062
+ max-width: 38%;
17063
+ object-fit: cover;
17064
+ width: 128px;
17065
+ }
17066
+
17067
+ .butex-document2-widget__image-asset-title {
17068
+ color: var(--butex-document2-muted);
17069
+ display: block;
17070
+ overflow: hidden;
17071
+ text-align: start;
17072
+ text-overflow: ellipsis;
17073
+ white-space: nowrap;
17074
+ }
17075
+
17076
+ .butex-document2-widget__image-actions {
17077
+ display: flex;
17078
+ flex-wrap: wrap;
17079
+ gap: 8px;
17080
+ }
17081
+
17082
+ .butex-document2-widget__image-select span,
17083
+ .butex-document2-widget__image-src-label {
17084
+ color: var(--butex-document2-muted);
17085
+ font-size: 0.86rem;
17086
+ }
17087
+
17088
+ .butex-document2-widget__image-select select {
17089
+ background: var(--butex-document2-input-bg);
17090
+ border: 1px solid var(--butex-document2-border);
17091
+ border-radius: 8px;
17092
+ color: var(--butex-document2-fg);
17093
+ min-height: 36px;
17094
+ padding: 6px 8px;
17095
+ }
17096
+
17097
+ .butex-document2-widget__image-select select:disabled {
17098
+ cursor: not-allowed;
17099
+ opacity: 0.55;
17100
+ }
17101
+
17102
+ .butex-document2-widget__image-advanced summary {
17103
+ color: var(--butex-document2-muted);
17104
+ cursor: pointer;
17105
+ font-size: 0.86rem;
17106
+ }
17107
+
16753
17108
  .butex-document2-widget__dev pre {
16754
17109
  background: var(--butex-document2-dev-code-bg);
16755
17110
  color: var(--butex-document2-dev-code-fg);
@@ -17628,951 +17983,995 @@ function resolveInitialDocument2(initialDocument, uiLocale = "ar", documentMeta)
17628
17983
  };
17629
17984
  }
17630
17985
  }
17631
- function ButexDocumentEditor2({
17632
- initialDocument,
17633
- documentMeta,
17634
- className,
17635
- debug = false,
17636
- documentDirection = "rtl",
17637
- equationSide = "arabic",
17638
- editableEquations = true,
17639
- previewOnly = false,
17640
- uiLocale = "ar",
17641
- mathOutput = "svg",
17642
- digitForm: digitFormProp,
17643
- onDigitFormChange,
17644
- resolveImageUrl,
17645
- onDocumentChange,
17646
- onDocumentJsonChange,
17647
- onLatexChange
17648
- }) {
17649
- const messages = document2Messages(uiLocale);
17650
- const initialState = (0, import_react13.useMemo)(
17651
- () => resolveInitialDocument2(initialDocument, uiLocale, documentMeta),
17652
- [initialDocument, documentMeta, uiLocale]
17653
- );
17654
- const [documentNode, setDocumentNode] = (0, import_react13.useState)(initialState.document);
17655
- const [error, setError] = (0, import_react13.useState)(initialState.error);
17656
- const [editorOpen, setEditorOpen] = (0, import_react13.useState)(true);
17657
- const [previewOpen, setPreviewOpen] = (0, import_react13.useState)(true);
17658
- const [selectedMath, setSelectedMath] = (0, import_react13.useState)(null);
17659
- const [citePicker, setCitePicker] = (0, import_react13.useState)(null);
17660
- const [referencesOpen, setReferencesOpen] = (0, import_react13.useState)(false);
17661
- const [labelsOpen, setLabelsOpen] = (0, import_react13.useState)(false);
17662
- const [refPicker, setRefPicker] = (0, import_react13.useState)(null);
17663
- const [digitFormState, setDigitFormState] = (0, import_react13.useState)(null);
17664
- const [debugImportOpen, setDebugImportOpen] = (0, import_react13.useState)(false);
17665
- const [debugImportText, setDebugImportText] = (0, import_react13.useState)("");
17666
- const [debugImportError, setDebugImportError] = (0, import_react13.useState)("");
17667
- const [editorFocus, setEditorFocus] = (0, import_react13.useState)(createEmptyDocument2EditorFocus());
17668
- const [collapsedBlockIds, setCollapsedBlockIds] = (0, import_react13.useState)(() => /* @__PURE__ */ new Set());
17669
- const [blockSelectionState, setBlockSelectionState] = (0, import_react13.useState)(emptyBlockSelection);
17670
- const [historyTick, setHistoryTick] = (0, import_react13.useState)(0);
17671
- const digitForm = digitFormProp ?? digitFormState ?? (documentDirection === "rtl" ? "arabicIndic" : "western");
17672
- function equationSessionWithDocumentDigits(session) {
17673
- return setCurrentDigitForm(session, digitForm);
17674
- }
17675
- const historyRef = (0, import_react13.useRef)(createDocument2History());
17676
- const documentRef = (0, import_react13.useRef)(documentNode);
17677
- const textSnapshotArmedRef = (0, import_react13.useRef)(false);
17678
- const textDebounceRef = (0, import_react13.useRef)(null);
17679
- const widgetRef = (0, import_react13.useRef)(null);
17680
- const articleMetaPanelRef = (0, import_react13.useRef)(null);
17681
- const pendingFocusRef = (0, import_react13.useRef)(null);
17682
- documentRef.current = documentNode;
17683
- const latex = document2Latex(documentNode, { digitForm });
17684
- const documentJson = (0, import_react13.useMemo)(() => toDocumentJson2(documentNode), [documentNode]);
17685
- const documentLabels = (0, import_react13.useMemo)(() => collectDocument2Labels(documentNode), [documentNode]);
17686
- const preview = (0, import_react13.useMemo)(
17687
- () => document2Preview(documentNode, mathOutput, equationSide, {
17688
- documentDirection,
17689
- digitForm,
17690
- uiLocale
17691
- }),
17692
- [documentNode, documentDirection, digitForm, equationSide, mathOutput, uiLocale]
17693
- );
17694
- const debugEnabled = (0, import_react13.useMemo)(() => {
17695
- if (debug) {
17696
- return true;
17697
- }
17698
- if (typeof window === "undefined") {
17699
- return false;
17700
- }
17701
- return new URLSearchParams(window.location.search).get("debug") === "1";
17702
- }, [debug]);
17703
- const canUndo = (0, import_react13.useMemo)(() => document2HistoryCanUndo(historyRef.current), [historyTick, documentNode]);
17704
- const canRedo = (0, import_react13.useMemo)(() => document2HistoryCanRedo(historyRef.current), [historyTick, documentNode]);
17705
- const blockSelection = normalizeBlockSelection(blockSelectionState.selection, documentNode.blocks.length);
17706
- const selectionCount = blockSelection ? blockSelection.to - blockSelection.from + 1 : 0;
17707
- const canMoveSelectionUp = Boolean(blockSelection && blockSelection.from > 0);
17708
- const canMoveSelectionDown = Boolean(blockSelection && blockSelection.to < documentNode.blocks.length - 1);
17709
- const formatTextToken = (0, import_react13.useMemo)(() => findFormatTextToken(documentNode, editorFocus), [documentNode, editorFocus]);
17710
- const canFormatText = formatTextToken !== null;
17711
- const activeTextStyles = formatTextToken?.style ?? {};
17712
- function clearBlockSelection() {
17713
- setBlockSelectionState(emptyBlockSelection());
17714
- }
17715
- function toggleSelectBlock(blockId, shiftKey) {
17716
- const index = documentRef.current.blocks.findIndex((block) => block.id === blockId);
17717
- if (index < 0) {
17718
- return;
17719
- }
17720
- const blockCount = documentRef.current.blocks.length;
17721
- setBlockSelectionState(
17722
- (current) => shiftKey ? extendBlockSelectionFromAnchor(current, index, blockCount) : toggleBlockInSelection(current, index, blockCount)
17986
+ var ButexDocumentEditor2 = (0, import_react13.forwardRef)(
17987
+ function ButexDocumentEditor22({
17988
+ initialDocument,
17989
+ documentMeta,
17990
+ className,
17991
+ debug = false,
17992
+ documentDirection = "rtl",
17993
+ equationSide = "arabic",
17994
+ editableEquations = true,
17995
+ previewOnly = false,
17996
+ uiLocale = "ar",
17997
+ mathOutput = "svg",
17998
+ digitForm: digitFormProp,
17999
+ onDigitFormChange,
18000
+ resolveImageUrl,
18001
+ onRequestImagePick,
18002
+ listImageAssets,
18003
+ renderImageBlockEditor,
18004
+ onDocumentChange,
18005
+ onDocumentJsonChange,
18006
+ onLatexChange
18007
+ }, ref) {
18008
+ const messages = document2Messages(uiLocale);
18009
+ const initialState = (0, import_react13.useMemo)(
18010
+ () => resolveInitialDocument2(initialDocument, uiLocale, documentMeta),
18011
+ [initialDocument, documentMeta, uiLocale]
17723
18012
  );
17724
- }
17725
- function moveSelectedBlocks(direction) {
17726
- const selection = normalizeBlockSelection(blockSelectionState.selection, documentRef.current.blocks.length);
17727
- if (!selection) {
17728
- return;
17729
- }
17730
- const next = moveDocument2BlockRange(documentRef.current, selection.from, selection.to, direction);
17731
- if (next === documentRef.current) {
17732
- return;
17733
- }
17734
- applyDocument(next, "immediate");
17735
- const remapped = remapSelectionAfterRangeMove(selection, direction);
17736
- setBlockSelectionState({
17737
- selection: normalizeBlockSelection(remapped, next.blocks.length),
17738
- anchor: blockSelectionState.anchor === null ? null : blockSelectionState.anchor + direction
17739
- });
17740
- }
17741
- function deleteSelectedBlocks() {
17742
- const selection = normalizeBlockSelection(blockSelectionState.selection, documentRef.current.blocks.length);
17743
- if (!selection) {
17744
- return;
17745
- }
17746
- applyDocument(removeDocument2BlockRange(documentRef.current, selection.from, selection.to), "immediate");
17747
- clearBlockSelection();
17748
- }
17749
- (0, import_react13.useEffect)(() => {
17750
- injectBuTeXDocument2Styles();
17751
- }, []);
17752
- (0, import_react13.useEffect)(() => {
17753
- if (!editableEquations || previewOnly) {
17754
- setSelectedMath(null);
17755
- }
17756
- }, [editableEquations, previewOnly]);
17757
- (0, import_react13.useEffect)(() => {
17758
- const next = resolveInitialDocument2(initialDocument, uiLocale, documentMeta);
17759
- setDocumentNode(next.document);
17760
- setError(next.error);
17761
- setSelectedMath(null);
17762
- setCollapsedBlockIds(/* @__PURE__ */ new Set());
17763
- historyRef.current = createDocument2History();
17764
- setHistoryTick((tick) => tick + 1);
17765
- }, [initialDocument, documentMeta, uiLocale]);
17766
- (0, import_react13.useEffect)(() => {
17767
- onDocumentChange?.(documentNode);
17768
- }, [documentNode, onDocumentChange]);
17769
- (0, import_react13.useEffect)(() => {
17770
- onDocumentJsonChange?.(documentJson);
17771
- }, [documentJson, onDocumentJsonChange]);
17772
- (0, import_react13.useEffect)(() => {
17773
- onLatexChange?.(latex);
17774
- }, [latex, onLatexChange]);
17775
- (0, import_react13.useEffect)(() => {
17776
- const pending = pendingFocusRef.current;
17777
- const root = widgetRef.current;
17778
- if (!pending || !root) {
17779
- return;
18013
+ const [documentNode, setDocumentNode] = (0, import_react13.useState)(initialState.document);
18014
+ const [error, setError] = (0, import_react13.useState)(initialState.error);
18015
+ const [editorOpen, setEditorOpen] = (0, import_react13.useState)(true);
18016
+ const [previewOpen, setPreviewOpen] = (0, import_react13.useState)(true);
18017
+ const [selectedMath, setSelectedMath] = (0, import_react13.useState)(null);
18018
+ const [citePicker, setCitePicker] = (0, import_react13.useState)(null);
18019
+ const [referencesOpen, setReferencesOpen] = (0, import_react13.useState)(false);
18020
+ const [labelsOpen, setLabelsOpen] = (0, import_react13.useState)(false);
18021
+ const [refPicker, setRefPicker] = (0, import_react13.useState)(null);
18022
+ const [digitFormState, setDigitFormState] = (0, import_react13.useState)(null);
18023
+ const [debugImportOpen, setDebugImportOpen] = (0, import_react13.useState)(false);
18024
+ const [debugImportText, setDebugImportText] = (0, import_react13.useState)("");
18025
+ const [debugImportError, setDebugImportError] = (0, import_react13.useState)("");
18026
+ const [editorFocus, setEditorFocus] = (0, import_react13.useState)(createEmptyDocument2EditorFocus());
18027
+ const [collapsedBlockIds, setCollapsedBlockIds] = (0, import_react13.useState)(() => /* @__PURE__ */ new Set());
18028
+ const [blockSelectionState, setBlockSelectionState] = (0, import_react13.useState)(emptyBlockSelection);
18029
+ const [historyTick, setHistoryTick] = (0, import_react13.useState)(0);
18030
+ const digitForm = digitFormProp ?? digitFormState ?? (documentDirection === "rtl" ? "arabicIndic" : "western");
18031
+ function equationSessionWithDocumentDigits(session) {
18032
+ return setCurrentDigitForm(session, digitForm);
18033
+ }
18034
+ const historyRef = (0, import_react13.useRef)(createDocument2History());
18035
+ const documentRef = (0, import_react13.useRef)(documentNode);
18036
+ const editorFocusRef = (0, import_react13.useRef)(editorFocus);
18037
+ const textSnapshotArmedRef = (0, import_react13.useRef)(false);
18038
+ const textDebounceRef = (0, import_react13.useRef)(null);
18039
+ const widgetRef = (0, import_react13.useRef)(null);
18040
+ const articleMetaPanelRef = (0, import_react13.useRef)(null);
18041
+ const pendingFocusRef = (0, import_react13.useRef)(null);
18042
+ documentRef.current = documentNode;
18043
+ editorFocusRef.current = editorFocus;
18044
+ const latex = document2Latex(documentNode, { digitForm });
18045
+ const documentJson = (0, import_react13.useMemo)(() => toDocumentJson2(documentNode), [documentNode]);
18046
+ const documentLabels = (0, import_react13.useMemo)(() => collectDocument2Labels(documentNode), [documentNode]);
18047
+ const preview = (0, import_react13.useMemo)(
18048
+ () => document2Preview(documentNode, mathOutput, equationSide, {
18049
+ documentDirection,
18050
+ digitForm,
18051
+ uiLocale
18052
+ }),
18053
+ [documentNode, documentDirection, digitForm, equationSide, mathOutput, uiLocale]
18054
+ );
18055
+ const debugEnabled = (0, import_react13.useMemo)(() => {
18056
+ if (debug) {
18057
+ return true;
18058
+ }
18059
+ if (typeof window === "undefined") {
18060
+ return false;
18061
+ }
18062
+ return new URLSearchParams(window.location.search).get("debug") === "1";
18063
+ }, [debug]);
18064
+ const canUndo = (0, import_react13.useMemo)(() => document2HistoryCanUndo(historyRef.current), [historyTick, documentNode]);
18065
+ const canRedo = (0, import_react13.useMemo)(() => document2HistoryCanRedo(historyRef.current), [historyTick, documentNode]);
18066
+ const blockSelection = normalizeBlockSelection(blockSelectionState.selection, documentNode.blocks.length);
18067
+ const selectionCount = blockSelection ? blockSelection.to - blockSelection.from + 1 : 0;
18068
+ const canMoveSelectionUp = Boolean(blockSelection && blockSelection.from > 0);
18069
+ const canMoveSelectionDown = Boolean(blockSelection && blockSelection.to < documentNode.blocks.length - 1);
18070
+ const formatTextToken = (0, import_react13.useMemo)(() => findFormatTextToken(documentNode, editorFocus), [documentNode, editorFocus]);
18071
+ const canFormatText = formatTextToken !== null;
18072
+ const activeTextStyles = formatTextToken?.style ?? {};
18073
+ function clearBlockSelection() {
18074
+ setBlockSelectionState(emptyBlockSelection());
18075
+ }
18076
+ function toggleSelectBlock(blockId, shiftKey) {
18077
+ const index = documentRef.current.blocks.findIndex((block) => block.id === blockId);
18078
+ if (index < 0) {
18079
+ return;
18080
+ }
18081
+ const blockCount = documentRef.current.blocks.length;
18082
+ setBlockSelectionState(
18083
+ (current) => shiftKey ? extendBlockSelectionFromAnchor(current, index, blockCount) : toggleBlockInSelection(current, index, blockCount)
18084
+ );
17780
18085
  }
17781
- pendingFocusRef.current = null;
17782
- requestAnimationFrame(() => {
17783
- if (pending.kind === "math") {
17784
- const mathButton = Array.from(root.querySelectorAll("[data-math-token-id]")).find(
17785
- (button) => button.dataset.mathTokenId === pending.tokenId
17786
- );
17787
- mathButton?.focus();
18086
+ function moveSelectedBlocks(direction) {
18087
+ const selection = normalizeBlockSelection(blockSelectionState.selection, documentRef.current.blocks.length);
18088
+ if (!selection) {
17788
18089
  return;
17789
18090
  }
17790
- if (pending.kind === "cite") {
17791
- const citeButton = Array.from(root.querySelectorAll("[data-cite-token-id]")).find(
17792
- (button) => button.dataset.citeTokenId === pending.tokenId
17793
- );
17794
- citeButton?.focus();
18091
+ const next = moveDocument2BlockRange(documentRef.current, selection.from, selection.to, direction);
18092
+ if (next === documentRef.current) {
17795
18093
  return;
17796
18094
  }
17797
- if (pending.kind === "ref") {
17798
- const refButton = Array.from(root.querySelectorAll("[data-ref-token-id]")).find(
17799
- (button) => button.dataset.refTokenId === pending.tokenId
17800
- );
17801
- refButton?.focus();
18095
+ applyDocument(next, "immediate");
18096
+ const remapped = remapSelectionAfterRangeMove(selection, direction);
18097
+ setBlockSelectionState({
18098
+ selection: normalizeBlockSelection(remapped, next.blocks.length),
18099
+ anchor: blockSelectionState.anchor === null ? null : blockSelectionState.anchor + direction
18100
+ });
18101
+ }
18102
+ function deleteSelectedBlocks() {
18103
+ const selection = normalizeBlockSelection(blockSelectionState.selection, documentRef.current.blocks.length);
18104
+ if (!selection) {
17802
18105
  return;
17803
18106
  }
17804
- const textarea = Array.from(root.querySelectorAll("textarea[data-field-id][data-text-token-id]")).find(
17805
- (element) => element.dataset.fieldId === pending.fieldId && element.dataset.textTokenId === pending.textTokenId
17806
- );
17807
- if (!textarea) {
18107
+ applyDocument(removeDocument2BlockRange(documentRef.current, selection.from, selection.to), "immediate");
18108
+ clearBlockSelection();
18109
+ }
18110
+ (0, import_react13.useEffect)(() => {
18111
+ injectBuTeXDocument2Styles();
18112
+ }, []);
18113
+ (0, import_react13.useEffect)(() => {
18114
+ if (!editableEquations || previewOnly) {
18115
+ setSelectedMath(null);
18116
+ }
18117
+ }, [editableEquations, previewOnly]);
18118
+ (0, import_react13.useEffect)(() => {
18119
+ const next = resolveInitialDocument2(initialDocument, uiLocale, documentMeta);
18120
+ setDocumentNode(next.document);
18121
+ setError(next.error);
18122
+ setSelectedMath(null);
18123
+ setCollapsedBlockIds(/* @__PURE__ */ new Set());
18124
+ historyRef.current = createDocument2History();
18125
+ setHistoryTick((tick) => tick + 1);
18126
+ }, [initialDocument, documentMeta, uiLocale]);
18127
+ (0, import_react13.useEffect)(() => {
18128
+ onDocumentChange?.(documentNode);
18129
+ }, [documentNode, onDocumentChange]);
18130
+ (0, import_react13.useEffect)(() => {
18131
+ onDocumentJsonChange?.(documentJson);
18132
+ }, [documentJson, onDocumentJsonChange]);
18133
+ (0, import_react13.useEffect)(() => {
18134
+ onLatexChange?.(latex);
18135
+ }, [latex, onLatexChange]);
18136
+ (0, import_react13.useEffect)(() => {
18137
+ const pending = pendingFocusRef.current;
18138
+ const root = widgetRef.current;
18139
+ if (!pending || !root) {
17808
18140
  return;
17809
18141
  }
17810
- const safeOffset = Math.max(0, Math.min(pending.caretOffset, textarea.value.length));
17811
- textarea.focus();
17812
- textarea.setSelectionRange(safeOffset, safeOffset);
17813
- });
17814
- }, [documentNode]);
17815
- const bumpHistoryUi = (0, import_react13.useCallback)(() => {
17816
- setHistoryTick((tick) => tick + 1);
17817
- }, []);
17818
- const applyDocument = (0, import_react13.useCallback)(
17819
- (next, mode = "immediate") => {
17820
- if (mode === "immediate") {
17821
- pushDocument2Snapshot(historyRef.current, documentRef.current);
17822
- bumpHistoryUi();
17823
- } else if (mode === "text") {
17824
- if (!textSnapshotArmedRef.current) {
18142
+ pendingFocusRef.current = null;
18143
+ requestAnimationFrame(() => {
18144
+ if (pending.kind === "math") {
18145
+ const mathButton = Array.from(root.querySelectorAll("[data-math-token-id]")).find(
18146
+ (button) => button.dataset.mathTokenId === pending.tokenId
18147
+ );
18148
+ mathButton?.focus();
18149
+ return;
18150
+ }
18151
+ if (pending.kind === "cite") {
18152
+ const citeButton = Array.from(root.querySelectorAll("[data-cite-token-id]")).find(
18153
+ (button) => button.dataset.citeTokenId === pending.tokenId
18154
+ );
18155
+ citeButton?.focus();
18156
+ return;
18157
+ }
18158
+ if (pending.kind === "ref") {
18159
+ const refButton = Array.from(root.querySelectorAll("[data-ref-token-id]")).find(
18160
+ (button) => button.dataset.refTokenId === pending.tokenId
18161
+ );
18162
+ refButton?.focus();
18163
+ return;
18164
+ }
18165
+ const textarea = Array.from(root.querySelectorAll("textarea[data-field-id][data-text-token-id]")).find(
18166
+ (element) => element.dataset.fieldId === pending.fieldId && element.dataset.textTokenId === pending.textTokenId
18167
+ );
18168
+ if (!textarea) {
18169
+ return;
18170
+ }
18171
+ const safeOffset = Math.max(0, Math.min(pending.caretOffset, textarea.value.length));
18172
+ textarea.focus();
18173
+ textarea.setSelectionRange(safeOffset, safeOffset);
18174
+ });
18175
+ }, [documentNode]);
18176
+ const bumpHistoryUi = (0, import_react13.useCallback)(() => {
18177
+ setHistoryTick((tick) => tick + 1);
18178
+ }, []);
18179
+ const applyDocument = (0, import_react13.useCallback)(
18180
+ (next, mode = "immediate") => {
18181
+ if (mode === "immediate") {
17825
18182
  pushDocument2Snapshot(historyRef.current, documentRef.current);
17826
- textSnapshotArmedRef.current = true;
17827
18183
  bumpHistoryUi();
18184
+ } else if (mode === "text") {
18185
+ if (!textSnapshotArmedRef.current) {
18186
+ pushDocument2Snapshot(historyRef.current, documentRef.current);
18187
+ textSnapshotArmedRef.current = true;
18188
+ bumpHistoryUi();
18189
+ }
18190
+ if (textDebounceRef.current !== null) {
18191
+ window.clearTimeout(textDebounceRef.current);
18192
+ }
18193
+ textDebounceRef.current = window.setTimeout(() => {
18194
+ textSnapshotArmedRef.current = false;
18195
+ }, 400);
17828
18196
  }
17829
- if (textDebounceRef.current !== null) {
17830
- window.clearTimeout(textDebounceRef.current);
18197
+ documentRef.current = next;
18198
+ setDocumentNode(next);
18199
+ },
18200
+ [bumpHistoryUi]
18201
+ );
18202
+ const afterBlockId = (0, import_react13.useCallback)(() => resolveInsertAfterBlockId(documentRef.current, editorFocusRef.current), []);
18203
+ const insertImageBlock = (0, import_react13.useCallback)(
18204
+ (srcOrAsset = "") => {
18205
+ clearBlockSelection();
18206
+ const after = resolveInsertAfterBlockId(documentRef.current, editorFocusRef.current);
18207
+ applyDocument(addDocument2ImageBlock(documentRef.current, srcOrAsset, after), "immediate");
18208
+ },
18209
+ [applyDocument]
18210
+ );
18211
+ (0, import_react13.useImperativeHandle)(
18212
+ ref,
18213
+ () => ({
18214
+ insertImageBlock,
18215
+ updateImageBlockValue(blockId, value) {
18216
+ applyDocument(updateDocument2ImageValue(documentRef.current, blockId, value), "immediate");
18217
+ },
18218
+ updateImageBlockAsset(blockId, asset) {
18219
+ applyDocument(updateDocument2ImageAsset(documentRef.current, blockId, asset), "immediate");
18220
+ },
18221
+ getDocumentJson() {
18222
+ return toDocumentJson2(documentRef.current);
17831
18223
  }
17832
- textDebounceRef.current = window.setTimeout(() => {
17833
- textSnapshotArmedRef.current = false;
17834
- }, 400);
18224
+ }),
18225
+ [applyDocument, insertImageBlock]
18226
+ );
18227
+ function undoDocument() {
18228
+ const restored = restoreDocument2Undo(historyRef.current, documentRef.current);
18229
+ if (!restored) {
18230
+ return;
17835
18231
  }
17836
- setDocumentNode(next);
17837
- },
17838
- [bumpHistoryUi]
17839
- );
17840
- const afterBlockId = (0, import_react13.useCallback)(() => resolveInsertAfterBlockId(documentRef.current, editorFocus), [editorFocus]);
17841
- function undoDocument() {
17842
- const restored = restoreDocument2Undo(historyRef.current, documentRef.current);
17843
- if (!restored) {
17844
- return;
17845
- }
17846
- textSnapshotArmedRef.current = false;
17847
- clearBlockSelection();
17848
- setDocumentNode(restored);
17849
- bumpHistoryUi();
17850
- }
17851
- function redoDocument() {
17852
- const restored = restoreDocument2Redo(historyRef.current, documentRef.current);
17853
- if (!restored) {
17854
- return;
18232
+ textSnapshotArmedRef.current = false;
18233
+ clearBlockSelection();
18234
+ documentRef.current = restored;
18235
+ setDocumentNode(restored);
18236
+ bumpHistoryUi();
17855
18237
  }
17856
- textSnapshotArmedRef.current = false;
17857
- clearBlockSelection();
17858
- setDocumentNode(restored);
17859
- bumpHistoryUi();
17860
- }
17861
- function openBlock(blockId) {
17862
- if (!blockId) {
17863
- return;
18238
+ function redoDocument() {
18239
+ const restored = restoreDocument2Redo(historyRef.current, documentRef.current);
18240
+ if (!restored) {
18241
+ return;
18242
+ }
18243
+ textSnapshotArmedRef.current = false;
18244
+ clearBlockSelection();
18245
+ documentRef.current = restored;
18246
+ setDocumentNode(restored);
18247
+ bumpHistoryUi();
17864
18248
  }
17865
- setCollapsedBlockIds((current) => {
17866
- if (!current.has(blockId)) {
17867
- return current;
18249
+ function openBlock(blockId) {
18250
+ if (!blockId) {
18251
+ return;
17868
18252
  }
17869
- const next = new Set(current);
17870
- next.delete(blockId);
17871
- return next;
17872
- });
17873
- }
17874
- function toggleBlockCollapse(blockId) {
17875
- setCollapsedBlockIds((current) => {
17876
- const next = new Set(current);
17877
- if (next.has(blockId)) {
18253
+ setCollapsedBlockIds((current) => {
18254
+ if (!current.has(blockId)) {
18255
+ return current;
18256
+ }
18257
+ const next = new Set(current);
17878
18258
  next.delete(blockId);
17879
- } else {
17880
- next.add(blockId);
17881
- }
17882
- return next;
17883
- });
17884
- }
17885
- function collapseAllBlocks() {
17886
- setCollapsedBlockIds(new Set(documentRef.current.blocks.map((block) => block.id)));
17887
- }
17888
- function openAllBlocks() {
17889
- setCollapsedBlockIds(/* @__PURE__ */ new Set());
17890
- }
17891
- async function copyDebugText(text) {
17892
- try {
17893
- await navigator.clipboard.writeText(text);
17894
- } catch {
18259
+ return next;
18260
+ });
17895
18261
  }
17896
- }
17897
- function openDebugJsonImport() {
17898
- setDebugImportText(JSON.stringify(documentJson, null, 2));
17899
- setDebugImportError("");
17900
- setDebugImportOpen(true);
17901
- }
17902
- function applyDebugJsonImport() {
17903
- const parsed = parseJsonDebugInput(debugImportText);
17904
- if (!parsed.ok) {
17905
- setDebugImportError(parsed.error);
17906
- return;
18262
+ function toggleBlockCollapse(blockId) {
18263
+ setCollapsedBlockIds((current) => {
18264
+ const next = new Set(current);
18265
+ if (next.has(blockId)) {
18266
+ next.delete(blockId);
18267
+ } else {
18268
+ next.add(blockId);
18269
+ }
18270
+ return next;
18271
+ });
17907
18272
  }
17908
- try {
17909
- const imported = fromDocumentJson2(parsed.value);
17910
- textSnapshotArmedRef.current = false;
17911
- if (textDebounceRef.current !== null) {
17912
- window.clearTimeout(textDebounceRef.current);
17913
- textDebounceRef.current = null;
17914
- }
17915
- setDocumentNode(imported);
17916
- setError(null);
17917
- setSelectedMath(null);
17918
- setCitePicker(null);
17919
- setRefPicker(null);
17920
- clearBlockSelection();
18273
+ function collapseAllBlocks() {
18274
+ setCollapsedBlockIds(new Set(documentRef.current.blocks.map((block) => block.id)));
18275
+ }
18276
+ function openAllBlocks() {
17921
18277
  setCollapsedBlockIds(/* @__PURE__ */ new Set());
17922
- setEditorFocus(createEmptyDocument2EditorFocus());
17923
- historyRef.current = createDocument2History();
17924
- bumpHistoryUi();
18278
+ }
18279
+ async function copyDebugText(text) {
18280
+ try {
18281
+ await navigator.clipboard.writeText(text);
18282
+ } catch {
18283
+ }
18284
+ }
18285
+ function openDebugJsonImport() {
18286
+ setDebugImportText(JSON.stringify(documentJson, null, 2));
17925
18287
  setDebugImportError("");
17926
- setDebugImportOpen(false);
17927
- } catch (importError) {
17928
- setDebugImportError(importError instanceof Error ? importError.message : String(importError));
18288
+ setDebugImportOpen(true);
17929
18289
  }
17930
- }
17931
- (0, import_react13.useEffect)(() => {
17932
- const root = widgetRef.current;
17933
- if (!root) {
17934
- return;
18290
+ function applyDebugJsonImport() {
18291
+ const parsed = parseJsonDebugInput(debugImportText);
18292
+ if (!parsed.ok) {
18293
+ setDebugImportError(parsed.error);
18294
+ return;
18295
+ }
18296
+ try {
18297
+ const imported = fromDocumentJson2(parsed.value);
18298
+ textSnapshotArmedRef.current = false;
18299
+ if (textDebounceRef.current !== null) {
18300
+ window.clearTimeout(textDebounceRef.current);
18301
+ textDebounceRef.current = null;
18302
+ }
18303
+ setDocumentNode(imported);
18304
+ setError(null);
18305
+ setSelectedMath(null);
18306
+ setCitePicker(null);
18307
+ setRefPicker(null);
18308
+ clearBlockSelection();
18309
+ setCollapsedBlockIds(/* @__PURE__ */ new Set());
18310
+ setEditorFocus(createEmptyDocument2EditorFocus());
18311
+ historyRef.current = createDocument2History();
18312
+ bumpHistoryUi();
18313
+ setDebugImportError("");
18314
+ setDebugImportOpen(false);
18315
+ } catch (importError) {
18316
+ setDebugImportError(importError instanceof Error ? importError.message : String(importError));
18317
+ }
17935
18318
  }
17936
- const onKeyDown = (event) => {
17937
- if (!editorOpen) {
18319
+ (0, import_react13.useEffect)(() => {
18320
+ const root = widgetRef.current;
18321
+ if (!root) {
17938
18322
  return;
17939
18323
  }
17940
- const mod = event.ctrlKey || event.metaKey;
17941
- if (!mod) {
18324
+ const onKeyDown = (event) => {
18325
+ if (!editorOpen) {
18326
+ return;
18327
+ }
18328
+ const mod = event.ctrlKey || event.metaKey;
18329
+ if (!mod) {
18330
+ return;
18331
+ }
18332
+ if (event.key.toLowerCase() === "z" && !event.shiftKey) {
18333
+ event.preventDefault();
18334
+ undoDocument();
18335
+ } else if (event.key.toLowerCase() === "z" && event.shiftKey || event.key.toLowerCase() === "y") {
18336
+ event.preventDefault();
18337
+ redoDocument();
18338
+ }
18339
+ };
18340
+ root.addEventListener("keydown", onKeyDown);
18341
+ return () => root.removeEventListener("keydown", onKeyDown);
18342
+ }, [editorOpen]);
18343
+ function rememberFieldFocus(blockId, fieldId, textTokenId, caretOffset, selectionStart, selectionEnd) {
18344
+ openBlock(blockId);
18345
+ clearBlockSelection();
18346
+ setEditorFocus({ blockId, fieldId, textTokenId, caretOffset, selectionStart, selectionEnd });
18347
+ }
18348
+ function rememberMathFocus(blockId, fieldId) {
18349
+ openBlock(blockId);
18350
+ clearBlockSelection();
18351
+ setEditorFocus({ blockId, fieldId, textTokenId: null, caretOffset: 0, selectionStart: 0, selectionEnd: 0 });
18352
+ }
18353
+ function rememberBlockFocus(blockId) {
18354
+ openBlock(blockId);
18355
+ setEditorFocus((current) => ({ ...current, blockId }));
18356
+ }
18357
+ function onFieldBlur() {
18358
+ textSnapshotArmedRef.current = false;
18359
+ }
18360
+ function openNewEquation(mode) {
18361
+ if (!editableEquations) {
17942
18362
  return;
17943
18363
  }
17944
- if (event.key.toLowerCase() === "z" && !event.shiftKey) {
17945
- event.preventDefault();
17946
- undoDocument();
17947
- } else if (event.key.toLowerCase() === "z" && event.shiftKey || event.key.toLowerCase() === "y") {
17948
- event.preventDefault();
17949
- redoDocument();
18364
+ const target = resolveInsertField(documentRef.current, editorFocus);
18365
+ if (target?.fieldId) {
18366
+ openBlock(topLevelBlockIdForField(documentRef.current, target.fieldId));
17950
18367
  }
17951
- };
17952
- root.addEventListener("keydown", onKeyDown);
17953
- return () => root.removeEventListener("keydown", onKeyDown);
17954
- }, [editorOpen]);
17955
- function rememberFieldFocus(blockId, fieldId, textTokenId, caretOffset, selectionStart, selectionEnd) {
17956
- openBlock(blockId);
17957
- clearBlockSelection();
17958
- setEditorFocus({ blockId, fieldId, textTokenId, caretOffset, selectionStart, selectionEnd });
17959
- }
17960
- function rememberMathFocus(blockId, fieldId) {
17961
- openBlock(blockId);
17962
- clearBlockSelection();
17963
- setEditorFocus({ blockId, fieldId, textTokenId: null, caretOffset: 0, selectionStart: 0, selectionEnd: 0 });
17964
- }
17965
- function rememberBlockFocus(blockId) {
17966
- openBlock(blockId);
17967
- setEditorFocus((current) => ({ ...current, blockId }));
17968
- }
17969
- function onFieldBlur() {
17970
- textSnapshotArmedRef.current = false;
17971
- }
17972
- function openNewEquation(mode) {
17973
- if (!editableEquations) {
17974
- return;
17975
- }
17976
- const target = resolveInsertField(documentRef.current, editorFocus);
17977
- if (target?.fieldId) {
17978
- openBlock(topLevelBlockIdForField(documentRef.current, target.fieldId));
17979
- }
17980
- setSelectedMath({
17981
- tokenId: null,
17982
- fieldId: target?.fieldId ?? null,
17983
- textTokenId: target?.textTokenId ?? null,
17984
- insertAt: target?.caretOffset ?? 0,
17985
- mathMode: mode,
17986
- session: equationSessionWithDocumentDigits(createEmptyEquationSession(equationSide)),
17987
- reason: null,
17988
- labelEnabled: false,
17989
- label: ""
17990
- });
17991
- }
17992
- function openMath(token) {
17993
- if (!editableEquations) {
17994
- return;
18368
+ setSelectedMath({
18369
+ tokenId: null,
18370
+ fieldId: target?.fieldId ?? null,
18371
+ textTokenId: target?.textTokenId ?? null,
18372
+ insertAt: target?.caretOffset ?? 0,
18373
+ mathMode: mode,
18374
+ session: equationSessionWithDocumentDigits(createEmptyEquationSession(equationSide)),
18375
+ reason: null,
18376
+ labelEnabled: false,
18377
+ label: ""
18378
+ });
17995
18379
  }
17996
- openBlock(topLevelBlockIdForToken(documentRef.current, token.id));
17997
- const result = mathTokenToEditorSession(token.math, equationSide);
17998
- const mode = token.display ? "display" : "inline";
17999
- if (!result.editable) {
18380
+ function openMath(token) {
18381
+ if (!editableEquations) {
18382
+ return;
18383
+ }
18384
+ openBlock(topLevelBlockIdForToken(documentRef.current, token.id));
18385
+ const result = mathTokenToEditorSession(token.math, equationSide);
18386
+ const mode = token.display ? "display" : "inline";
18387
+ if (!result.editable) {
18388
+ setSelectedMath({
18389
+ tokenId: token.id,
18390
+ fieldId: null,
18391
+ insertAt: 0,
18392
+ textTokenId: null,
18393
+ mathMode: mode,
18394
+ session: equationSessionWithDocumentDigits(createEmptyEquationSession(equationSide)),
18395
+ reason: result.reason,
18396
+ labelEnabled: Boolean(token.labelEnabled),
18397
+ label: token.label ?? ""
18398
+ });
18399
+ return;
18400
+ }
18000
18401
  setSelectedMath({
18001
18402
  tokenId: token.id,
18002
18403
  fieldId: null,
18003
18404
  insertAt: 0,
18004
18405
  textTokenId: null,
18005
18406
  mathMode: mode,
18006
- session: equationSessionWithDocumentDigits(createEmptyEquationSession(equationSide)),
18007
- reason: result.reason,
18407
+ session: equationSessionWithDocumentDigits(result.session),
18408
+ reason: null,
18008
18409
  labelEnabled: Boolean(token.labelEnabled),
18009
18410
  label: token.label ?? ""
18010
18411
  });
18011
- return;
18012
18412
  }
18013
- setSelectedMath({
18014
- tokenId: token.id,
18015
- fieldId: null,
18016
- insertAt: 0,
18017
- textTokenId: null,
18018
- mathMode: mode,
18019
- session: equationSessionWithDocumentDigits(result.session),
18020
- reason: null,
18021
- labelEnabled: Boolean(token.labelEnabled),
18022
- label: token.label ?? ""
18023
- });
18024
- }
18025
- function toggleEditorPanel() {
18026
- setEditorOpen((open) => {
18027
- if (open && !previewOpen) {
18028
- setPreviewOpen(true);
18413
+ function toggleEditorPanel() {
18414
+ setEditorOpen((open) => {
18415
+ if (open && !previewOpen) {
18416
+ setPreviewOpen(true);
18417
+ }
18418
+ return !open;
18419
+ });
18420
+ }
18421
+ function togglePreviewPanel() {
18422
+ setPreviewOpen((open) => {
18423
+ if (open && !editorOpen) {
18424
+ setEditorOpen(true);
18425
+ }
18426
+ return !open;
18427
+ });
18428
+ }
18429
+ function saveEquation(session) {
18430
+ if (!selectedMath) {
18431
+ return;
18029
18432
  }
18030
- return !open;
18031
- });
18032
- }
18033
- function togglePreviewPanel() {
18034
- setPreviewOpen((open) => {
18035
- if (open && !editorOpen) {
18036
- setEditorOpen(true);
18433
+ const delimiters = mathDelimiters(selectedMath.mathMode);
18434
+ if (selectedMath.tokenId) {
18435
+ let next2 = replaceMathTokenFromSession(
18436
+ documentRef.current,
18437
+ selectedMath.tokenId,
18438
+ session,
18439
+ delimiters.opening,
18440
+ delimiters.closing,
18441
+ equationSide
18442
+ );
18443
+ if (selectedMath.mathMode === "display") {
18444
+ next2 = updateMathTokenLabel(next2, selectedMath.tokenId, {
18445
+ labelEnabled: selectedMath.labelEnabled,
18446
+ label: selectedMath.label
18447
+ });
18448
+ } else {
18449
+ next2 = updateMathTokenLabel(next2, selectedMath.tokenId, { labelEnabled: false, label: "" });
18450
+ }
18451
+ pendingFocusRef.current = { kind: "math", tokenId: selectedMath.tokenId };
18452
+ applyDocument(next2, "immediate");
18453
+ setSelectedMath(null);
18454
+ return;
18037
18455
  }
18038
- return !open;
18039
- });
18040
- }
18041
- function saveEquation(session) {
18042
- if (!selectedMath) {
18043
- return;
18044
- }
18045
- const delimiters = mathDelimiters(selectedMath.mathMode);
18046
- if (selectedMath.tokenId) {
18047
- let next2 = replaceMathTokenFromSession(
18048
- documentRef.current,
18049
- selectedMath.tokenId,
18456
+ let current = documentRef.current;
18457
+ let fieldId = selectedMath.fieldId;
18458
+ let textTokenId = selectedMath.textTokenId;
18459
+ let insertAt = selectedMath.insertAt;
18460
+ if (!fieldId) {
18461
+ const anchor = afterBlockId();
18462
+ current = addDocument2TextBlock(current, "\\paragraph", anchor);
18463
+ const anchorIndex = anchor ? current.blocks.findIndex((block) => block.id === anchor) : -1;
18464
+ const newIndex = anchorIndex >= 0 ? anchorIndex + 1 : current.blocks.length - 1;
18465
+ const newBlock = current.blocks[newIndex];
18466
+ const field = newBlock?.kind === "textBlock" ? newBlock.field : null;
18467
+ if (!field) {
18468
+ setSelectedMath(null);
18469
+ return;
18470
+ }
18471
+ fieldId = field.id;
18472
+ textTokenId = field.tokens.find((token) => token.kind === "text")?.id ?? null;
18473
+ insertAt = 0;
18474
+ }
18475
+ let next = insertMathTokenAtCaret(
18476
+ current,
18477
+ fieldId,
18478
+ textTokenId,
18479
+ insertAt,
18050
18480
  session,
18051
18481
  delimiters.opening,
18052
18482
  delimiters.closing,
18053
18483
  equationSide
18054
18484
  );
18055
- if (selectedMath.mathMode === "display") {
18056
- next2 = updateMathTokenLabel(next2, selectedMath.tokenId, {
18057
- labelEnabled: selectedMath.labelEnabled,
18485
+ const trailingTextTokenId = trailingTextTokenAfterInsertedMath(current, next, fieldId, textTokenId, insertAt);
18486
+ const fieldAfter = findInlineFieldById(next, fieldId);
18487
+ const insertedMathId = trailingTextTokenId && fieldAfter ? fieldAfter.tokens[Math.max(
18488
+ 0,
18489
+ fieldAfter.tokens.findIndex((token) => token.id === trailingTextTokenId) - 1
18490
+ )]?.kind === "math" ? fieldAfter.tokens[Math.max(
18491
+ 0,
18492
+ fieldAfter.tokens.findIndex((token) => token.id === trailingTextTokenId) - 1
18493
+ )]?.id : null : fieldAfter?.tokens.filter((token) => token.kind === "math").at(-1)?.id ?? null;
18494
+ if (insertedMathId && selectedMath.mathMode === "display" && selectedMath.labelEnabled) {
18495
+ next = updateMathTokenLabel(next, insertedMathId, {
18496
+ labelEnabled: true,
18058
18497
  label: selectedMath.label
18059
18498
  });
18060
- } else {
18061
- next2 = updateMathTokenLabel(next2, selectedMath.tokenId, { labelEnabled: false, label: "" });
18062
18499
  }
18063
- pendingFocusRef.current = { kind: "math", tokenId: selectedMath.tokenId };
18064
- applyDocument(next2, "immediate");
18500
+ if (trailingTextTokenId) {
18501
+ pendingFocusRef.current = { kind: "text", fieldId, textTokenId: trailingTextTokenId, caretOffset: 0 };
18502
+ }
18503
+ applyDocument(next, "immediate");
18065
18504
  setSelectedMath(null);
18066
- return;
18067
18505
  }
18068
- let current = documentRef.current;
18069
- let fieldId = selectedMath.fieldId;
18070
- let textTokenId = selectedMath.textTokenId;
18071
- let insertAt = selectedMath.insertAt;
18072
- if (!fieldId) {
18073
- const anchor = afterBlockId();
18074
- current = addDocument2TextBlock(current, "\\paragraph", anchor);
18075
- const anchorIndex = anchor ? current.blocks.findIndex((block) => block.id === anchor) : -1;
18076
- const newIndex = anchorIndex >= 0 ? anchorIndex + 1 : current.blocks.length - 1;
18077
- const newBlock = current.blocks[newIndex];
18078
- const field = newBlock?.kind === "textBlock" ? newBlock.field : null;
18079
- if (!field) {
18080
- setSelectedMath(null);
18506
+ function deleteSelectedEquation() {
18507
+ if (!selectedMath?.tokenId) {
18081
18508
  return;
18082
18509
  }
18083
- fieldId = field.id;
18084
- textTokenId = field.tokens.find((token) => token.kind === "text")?.id ?? null;
18085
- insertAt = 0;
18086
- }
18087
- let next = insertMathTokenAtCaret(
18088
- current,
18089
- fieldId,
18090
- textTokenId,
18091
- insertAt,
18092
- session,
18093
- delimiters.opening,
18094
- delimiters.closing,
18095
- equationSide
18096
- );
18097
- const trailingTextTokenId = trailingTextTokenAfterInsertedMath(current, next, fieldId, textTokenId, insertAt);
18098
- const fieldAfter = findInlineFieldById(next, fieldId);
18099
- const insertedMathId = trailingTextTokenId && fieldAfter ? fieldAfter.tokens[Math.max(
18100
- 0,
18101
- fieldAfter.tokens.findIndex((token) => token.id === trailingTextTokenId) - 1
18102
- )]?.kind === "math" ? fieldAfter.tokens[Math.max(
18103
- 0,
18104
- fieldAfter.tokens.findIndex((token) => token.id === trailingTextTokenId) - 1
18105
- )]?.id : null : fieldAfter?.tokens.filter((token) => token.kind === "math").at(-1)?.id ?? null;
18106
- if (insertedMathId && selectedMath.mathMode === "display" && selectedMath.labelEnabled) {
18107
- next = updateMathTokenLabel(next, insertedMathId, {
18108
- labelEnabled: true,
18109
- label: selectedMath.label
18110
- });
18111
- }
18112
- if (trailingTextTokenId) {
18113
- pendingFocusRef.current = { kind: "text", fieldId, textTokenId: trailingTextTokenId, caretOffset: 0 };
18114
- }
18115
- applyDocument(next, "immediate");
18116
- setSelectedMath(null);
18117
- }
18118
- function deleteSelectedEquation() {
18119
- if (!selectedMath?.tokenId) {
18120
- return;
18510
+ const current = documentRef.current;
18511
+ const next = removeMathTokenById(current, selectedMath.tokenId);
18512
+ pendingFocusRef.current = focusAfterDeletedMath(current, next, selectedMath.tokenId);
18513
+ applyDocument(next, "immediate");
18514
+ setSelectedMath(null);
18121
18515
  }
18122
- const current = documentRef.current;
18123
- const next = removeMathTokenById(current, selectedMath.tokenId);
18124
- pendingFocusRef.current = focusAfterDeletedMath(current, next, selectedMath.tokenId);
18125
- applyDocument(next, "immediate");
18126
- setSelectedMath(null);
18127
- }
18128
- function deleteMathToken(tokenId) {
18129
- const current = documentRef.current;
18130
- const next = removeMathTokenById(current, tokenId);
18131
- pendingFocusRef.current = focusAfterDeletedMath(current, next, tokenId);
18132
- applyDocument(next, "immediate");
18133
- }
18134
- function openCitePickerForInsert() {
18135
- const target = resolveInsertField(documentRef.current, editorFocus);
18136
- setCitePicker({
18137
- tokenId: null,
18138
- fieldId: target?.fieldId ?? null,
18139
- textTokenId: target?.textTokenId ?? editorFocus.textTokenId,
18140
- caretOffset: target?.caretOffset ?? editorFocus.caretOffset,
18141
- keys: []
18142
- });
18143
- }
18144
- function openCite(token) {
18145
- setCitePicker({
18146
- tokenId: token.id,
18147
- fieldId: null,
18148
- textTokenId: null,
18149
- caretOffset: 0,
18150
- keys: [...token.keys]
18151
- });
18152
- }
18153
- function confirmCiteKeys(keys) {
18154
- if (!citePicker || keys.length === 0) {
18155
- setCitePicker(null);
18156
- return;
18516
+ function deleteMathToken(tokenId) {
18517
+ const current = documentRef.current;
18518
+ const next = removeMathTokenById(current, tokenId);
18519
+ pendingFocusRef.current = focusAfterDeletedMath(current, next, tokenId);
18520
+ applyDocument(next, "immediate");
18521
+ }
18522
+ function openCitePickerForInsert() {
18523
+ const target = resolveInsertField(documentRef.current, editorFocus);
18524
+ setCitePicker({
18525
+ tokenId: null,
18526
+ fieldId: target?.fieldId ?? null,
18527
+ textTokenId: target?.textTokenId ?? editorFocus.textTokenId,
18528
+ caretOffset: target?.caretOffset ?? editorFocus.caretOffset,
18529
+ keys: []
18530
+ });
18157
18531
  }
18158
- if (citePicker.tokenId) {
18159
- applyDocument(updateCiteTokenKeys(documentRef.current, citePicker.tokenId, keys), "immediate");
18160
- pendingFocusRef.current = { kind: "cite", tokenId: citePicker.tokenId };
18161
- setCitePicker(null);
18162
- return;
18532
+ function openCite(token) {
18533
+ setCitePicker({
18534
+ tokenId: token.id,
18535
+ fieldId: null,
18536
+ textTokenId: null,
18537
+ caretOffset: 0,
18538
+ keys: [...token.keys]
18539
+ });
18163
18540
  }
18164
- const target = resolveInsertField(documentRef.current, editorFocus);
18165
- const fieldId = citePicker.fieldId ?? target?.fieldId;
18166
- if (!fieldId) {
18541
+ function confirmCiteKeys(keys) {
18542
+ if (!citePicker || keys.length === 0) {
18543
+ setCitePicker(null);
18544
+ return;
18545
+ }
18546
+ if (citePicker.tokenId) {
18547
+ applyDocument(updateCiteTokenKeys(documentRef.current, citePicker.tokenId, keys), "immediate");
18548
+ pendingFocusRef.current = { kind: "cite", tokenId: citePicker.tokenId };
18549
+ setCitePicker(null);
18550
+ return;
18551
+ }
18552
+ const target = resolveInsertField(documentRef.current, editorFocus);
18553
+ const fieldId = citePicker.fieldId ?? target?.fieldId;
18554
+ if (!fieldId) {
18555
+ setCitePicker(null);
18556
+ return;
18557
+ }
18558
+ const textTokenId = citePicker.textTokenId ?? target?.textTokenId ?? null;
18559
+ const caretOffset = citePicker.caretOffset ?? target?.caretOffset ?? 0;
18560
+ const next = insertCiteTokenAtCaret(documentRef.current, fieldId, textTokenId, caretOffset, keys);
18561
+ applyDocument(next, "immediate");
18167
18562
  setCitePicker(null);
18168
- return;
18169
18563
  }
18170
- const textTokenId = citePicker.textTokenId ?? target?.textTokenId ?? null;
18171
- const caretOffset = citePicker.caretOffset ?? target?.caretOffset ?? 0;
18172
- const next = insertCiteTokenAtCaret(documentRef.current, fieldId, textTokenId, caretOffset, keys);
18173
- applyDocument(next, "immediate");
18174
- setCitePicker(null);
18175
- }
18176
- function deleteCiteToken(tokenId) {
18177
- applyDocument(removeCiteTokenById(documentRef.current, tokenId), "immediate");
18178
- }
18179
- function openRefPickerForInsert() {
18180
- const target = resolveInsertField(documentRef.current, editorFocus);
18181
- setRefPicker({
18182
- tokenId: null,
18183
- fieldId: target?.fieldId ?? null,
18184
- textTokenId: target?.textTokenId ?? editorFocus.textTokenId,
18185
- caretOffset: target?.caretOffset ?? editorFocus.caretOffset,
18186
- keys: [],
18187
- refCommand: "ref"
18188
- });
18189
- }
18190
- function openRef(token) {
18191
- setRefPicker({
18192
- tokenId: token.id,
18193
- fieldId: null,
18194
- textTokenId: null,
18195
- caretOffset: 0,
18196
- keys: [...token.keys],
18197
- refCommand: token.refCommand
18198
- });
18199
- }
18200
- function confirmRefKeys(keys, refCommand) {
18201
- if (!refPicker || keys.length === 0) {
18202
- setRefPicker(null);
18203
- return;
18564
+ function deleteCiteToken(tokenId) {
18565
+ applyDocument(removeCiteTokenById(documentRef.current, tokenId), "immediate");
18566
+ }
18567
+ function openRefPickerForInsert() {
18568
+ const target = resolveInsertField(documentRef.current, editorFocus);
18569
+ setRefPicker({
18570
+ tokenId: null,
18571
+ fieldId: target?.fieldId ?? null,
18572
+ textTokenId: target?.textTokenId ?? editorFocus.textTokenId,
18573
+ caretOffset: target?.caretOffset ?? editorFocus.caretOffset,
18574
+ keys: [],
18575
+ refCommand: "ref"
18576
+ });
18204
18577
  }
18205
- if (refPicker.tokenId) {
18206
- applyDocument(updateRefTokenKeys(documentRef.current, refPicker.tokenId, keys, refCommand), "immediate");
18207
- pendingFocusRef.current = { kind: "ref", tokenId: refPicker.tokenId };
18208
- setRefPicker(null);
18209
- return;
18578
+ function openRef(token) {
18579
+ setRefPicker({
18580
+ tokenId: token.id,
18581
+ fieldId: null,
18582
+ textTokenId: null,
18583
+ caretOffset: 0,
18584
+ keys: [...token.keys],
18585
+ refCommand: token.refCommand
18586
+ });
18210
18587
  }
18211
- const target = resolveInsertField(documentRef.current, editorFocus);
18212
- const fieldId = refPicker.fieldId ?? target?.fieldId;
18213
- if (!fieldId) {
18588
+ function confirmRefKeys(keys, refCommand) {
18589
+ if (!refPicker || keys.length === 0) {
18590
+ setRefPicker(null);
18591
+ return;
18592
+ }
18593
+ if (refPicker.tokenId) {
18594
+ applyDocument(updateRefTokenKeys(documentRef.current, refPicker.tokenId, keys, refCommand), "immediate");
18595
+ pendingFocusRef.current = { kind: "ref", tokenId: refPicker.tokenId };
18596
+ setRefPicker(null);
18597
+ return;
18598
+ }
18599
+ const target = resolveInsertField(documentRef.current, editorFocus);
18600
+ const fieldId = refPicker.fieldId ?? target?.fieldId;
18601
+ if (!fieldId) {
18602
+ setRefPicker(null);
18603
+ return;
18604
+ }
18605
+ const textTokenId = refPicker.textTokenId ?? target?.textTokenId ?? null;
18606
+ const caretOffset = refPicker.caretOffset ?? target?.caretOffset ?? 0;
18607
+ const next = insertRefTokenAtCaret(documentRef.current, fieldId, textTokenId, caretOffset, keys, refCommand);
18608
+ applyDocument(next, "immediate");
18214
18609
  setRefPicker(null);
18215
- return;
18216
18610
  }
18217
- const textTokenId = refPicker.textTokenId ?? target?.textTokenId ?? null;
18218
- const caretOffset = refPicker.caretOffset ?? target?.caretOffset ?? 0;
18219
- const next = insertRefTokenAtCaret(documentRef.current, fieldId, textTokenId, caretOffset, keys, refCommand);
18220
- applyDocument(next, "immediate");
18221
- setRefPicker(null);
18222
- }
18223
- function deleteRefToken(tokenId) {
18224
- applyDocument(removeRefTokenById(documentRef.current, tokenId), "immediate");
18225
- }
18226
- function toggleInlineTextStyle(styleName) {
18227
- if (!canFormatText || !editorFocus.fieldId || !editorFocus.textTokenId) {
18228
- return;
18611
+ function deleteRefToken(tokenId) {
18612
+ applyDocument(removeRefTokenById(documentRef.current, tokenId), "immediate");
18229
18613
  }
18230
- applyDocument(
18231
- toggleTextTokenStyle(
18232
- documentRef.current,
18233
- editorFocus.fieldId,
18234
- editorFocus.textTokenId,
18235
- editorFocus.selectionStart,
18236
- editorFocus.selectionEnd,
18237
- styleName
18238
- ),
18239
- "immediate"
18240
- );
18241
- }
18242
- const showEditorPanel = !previewOnly && editorOpen;
18243
- const showPreviewPanel = previewOnly || previewOpen;
18244
- return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
18245
- "div",
18246
- {
18247
- ref: widgetRef,
18248
- className: ["butex-document2-widget", className].filter(Boolean).join(" "),
18249
- dir: uiLocaleDirection(uiLocale),
18250
- lang: uiLocale,
18251
- children: /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "butex-document2-widget__shell", children: [
18252
- !previewOnly ? /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "butex-document2-widget__toolbar", children: [
18253
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
18254
- DocumentInsertToolbar,
18255
- {
18256
- canUndo,
18257
- canRedo,
18258
- editableEquations,
18259
- uiLocale,
18260
- digitForm,
18261
- selectionCount,
18262
- canMoveSelectionUp,
18263
- canMoveSelectionDown,
18264
- canFormatText,
18265
- activeTextStyles,
18266
- onUndo: undoDocument,
18267
- onRedo: redoDocument,
18268
- onToggleTextStyle: toggleInlineTextStyle,
18269
- onMoveSelectionUp: () => moveSelectedBlocks(-1),
18270
- onMoveSelectionDown: () => moveSelectedBlocks(1),
18271
- onDeleteSelection: deleteSelectedBlocks,
18272
- onAddSection: () => {
18273
- clearBlockSelection();
18274
- applyDocument(addDocument2TextBlock(documentRef.current, "\\section", afterBlockId()), "immediate");
18275
- },
18276
- onAddSubsection: () => {
18277
- clearBlockSelection();
18278
- applyDocument(addDocument2TextBlock(documentRef.current, "\\subsection", afterBlockId()), "immediate");
18279
- },
18280
- onAddSubsubsection: () => {
18281
- clearBlockSelection();
18282
- applyDocument(addDocument2TextBlock(documentRef.current, "\\subsubsection", afterBlockId()), "immediate");
18283
- },
18284
- onAddParagraph: () => {
18285
- clearBlockSelection();
18286
- applyDocument(addDocument2TextBlock(documentRef.current, "\\paragraph", afterBlockId()), "immediate");
18287
- },
18288
- onAddInlineEquation: () => openNewEquation("inline"),
18289
- onAddDisplayEquation: () => openNewEquation("display"),
18290
- onAddTable: (rowCount, colCount) => {
18291
- clearBlockSelection();
18292
- applyDocument(addDocument2TableBlock(documentRef.current, "l".repeat(colCount), rowCount, colCount, afterBlockId()), "immediate");
18293
- },
18294
- onAddList: () => {
18295
- clearBlockSelection();
18296
- applyDocument(addDocument2ListBlock(documentRef.current, false, afterBlockId()), "immediate");
18297
- },
18298
- onAddEnumerate: () => {
18299
- clearBlockSelection();
18300
- applyDocument(addDocument2ListBlock(documentRef.current, true, afterBlockId()), "immediate");
18301
- },
18302
- onAddFigure: () => {
18303
- clearBlockSelection();
18304
- applyDocument(addDocument2ImageBlock(documentRef.current, "", afterBlockId()), "immediate");
18305
- },
18306
- onInsertCitation: openCitePickerForInsert,
18307
- onInsertInternalRef: openRefPickerForInsert,
18308
- onInsertBibliography: () => {
18309
- clearBlockSelection();
18310
- applyDocument(ensureDocument2BibliographyBlock(documentRef.current, afterBlockId()), "immediate");
18311
- },
18312
- onManageReferences: () => setReferencesOpen(true),
18313
- onManageLabels: () => setLabelsOpen(true),
18314
- onOpenArticleMeta: () => focusArticleMetaPanel(articleMetaPanelRef.current),
18315
- onDigitFormChange: (next) => {
18316
- setDigitFormState(next);
18317
- onDigitFormChange?.(next);
18318
- }
18319
- }
18320
- ),
18321
- /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "butex-document2-widget__toolbar-group", role: "group", "aria-label": messages.panels, children: [
18322
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("button", { type: "button", "aria-pressed": editorOpen, onClick: toggleEditorPanel, children: editorOpen ? messages.hideEditor : messages.editor }),
18323
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("button", { type: "button", "aria-pressed": previewOpen, onClick: togglePreviewPanel, children: previewOpen ? messages.hidePreview : messages.preview })
18324
- ] }),
18325
- /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "butex-document2-widget__toolbar-group", role: "group", "aria-label": messages.collapseBlocks, children: [
18326
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("button", { type: "button", onClick: collapseAllBlocks, children: messages.collapseAll }),
18327
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("button", { type: "button", onClick: openAllBlocks, children: messages.openAll })
18328
- ] })
18329
- ] }) : null,
18330
- error ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("p", { className: "butex-document2-widget__error", children: error }) : null,
18331
- /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
18332
- "div",
18333
- {
18334
- className: `butex-document2-widget__layout butex-document2-widget__layout--editor-${showEditorPanel ? "open" : "closed"} butex-document2-widget__layout--preview-${showPreviewPanel ? "open" : "closed"}`,
18335
- children: [
18336
- !previewOnly ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
18337
- "section",
18338
- {
18339
- className: "butex-document2-widget__panel butex-document2-widget__editor-panel",
18340
- "aria-label": messages.documentEditor,
18341
- "aria-hidden": !showEditorPanel,
18342
- children: /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "butex-document2-widget__blocks", children: [
18343
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
18344
- ArticleMetaPanel,
18345
- {
18346
- meta: documentNode.meta ?? emptyDocument2Meta(),
18347
- uiLocale,
18348
- digitForm,
18349
- panelRef: articleMetaPanelRef,
18350
- onChange: (patch) => {
18351
- clearBlockSelection();
18352
- applyDocument(updateDocument2Meta(documentRef.current, patch), "text");
18353
- }
18354
- }
18355
- ),
18356
- documentNode.blocks.map((block, blockIndex) => /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
18357
- BlockEditor,
18358
- {
18359
- block,
18360
- references: documentNode.references,
18361
- documentNode,
18362
- documentDirection,
18363
- digitForm,
18364
- mathOutput,
18365
- equationSide,
18366
- editableEquations,
18367
- editableCitations: !previewOnly,
18368
- uiLocale,
18369
- isCollapsed: collapsedBlockIds.has(block.id),
18370
- selected: Boolean(blockSelection && blockIndex >= blockSelection.from && blockIndex <= blockSelection.to),
18371
- onToggleSelect: toggleSelectBlock,
18372
- onTextChange: (fieldId, tokenId, text) => {
18373
- clearBlockSelection();
18374
- applyDocument(updateTextToken(documentRef.current, fieldId, tokenId, text), "text");
18375
- },
18376
- onOpenMath: openMath,
18377
- onDeleteMath: editableEquations ? deleteMathToken : void 0,
18378
- onOpenCite: openCite,
18379
- onDeleteCite: deleteCiteToken,
18380
- onOpenRef: openRef,
18381
- onDeleteRef: deleteRefToken,
18382
- onToggleCollapse: toggleBlockCollapse,
18383
- onBlockFocus: rememberBlockFocus,
18384
- onFieldFocus: rememberFieldFocus,
18385
- onMathFocus: rememberMathFocus,
18386
- onFieldBlur,
18387
- onImageSrcChange: (blockId, value) => {
18388
- clearBlockSelection();
18389
- applyDocument(updateDocument2ImageValue(documentRef.current, blockId, value), "text");
18390
- },
18391
- onFloatMetaChange: (blockId, kind, patch) => {
18392
- clearBlockSelection();
18393
- applyDocument(
18394
- kind === "image" ? updateDocument2ImageMeta(documentRef.current, blockId, patch) : updateDocument2TableMeta(documentRef.current, blockId, patch),
18395
- "text"
18396
- );
18397
- },
18398
- onParagraphCenteredChange: (blockId, centered) => {
18399
- clearBlockSelection();
18400
- applyDocument(updateDocument2TextBlockCentered(documentRef.current, blockId, centered), "text");
18401
- },
18402
- onAddListItem: (listBlockId) => {
18403
- clearBlockSelection();
18404
- applyDocument(addDocument2ListItem(documentRef.current, listBlockId), "immediate");
18405
- },
18406
- onRemoveListItem: (listBlockId, itemId) => {
18407
- clearBlockSelection();
18408
- applyDocument(removeDocument2ListItem(documentRef.current, listBlockId, itemId), "immediate");
18409
- },
18410
- onManageReferences: () => setReferencesOpen(true)
18411
- },
18412
- block.id
18413
- ))
18414
- ] })
18415
- }
18416
- ) : null,
18417
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
18418
- "section",
18419
- {
18420
- className: "butex-document2-widget__panel butex-document2-widget__preview-panel",
18421
- "aria-label": messages.documentPreview,
18422
- "aria-hidden": !showPreviewPanel,
18423
- children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
18424
- DocumentPreview,
18425
- {
18426
- blocks: preview.blocks,
18427
- output: mathOutput,
18428
- documentDirection,
18429
- uiLocale,
18430
- digitForm,
18431
- resolveImageUrl
18432
- }
18433
- )
18434
- }
18435
- )
18436
- ]
18437
- }
18614
+ function toggleInlineTextStyle(styleName) {
18615
+ if (!canFormatText || !editorFocus.fieldId || !editorFocus.textTokenId) {
18616
+ return;
18617
+ }
18618
+ applyDocument(
18619
+ toggleTextTokenStyle(
18620
+ documentRef.current,
18621
+ editorFocus.fieldId,
18622
+ editorFocus.textTokenId,
18623
+ editorFocus.selectionStart,
18624
+ editorFocus.selectionEnd,
18625
+ styleName
18438
18626
  ),
18439
- debugEnabled ? /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "butex-document2-widget__dev", children: [
18440
- debugEnabled && documentNode.diagnostics.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_jsx_runtime17.Fragment, { children: [
18441
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("strong", { children: messages.importWarnings }),
18442
- documentNode.diagnostics.map((diagnostic) => /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("p", { className: "butex-document2-widget__dev-diagnostic", children: formatDocument2Diagnostic(diagnostic, messages) }, `${diagnostic.path}-${diagnostic.message}`))
18443
- ] }) : null,
18444
- /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "butex-document2-widget__dev-actions", children: [
18445
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("button", { type: "button", onClick: () => void copyDebugText(JSON.stringify(documentJson, null, 2)), children: "Copy JSON" }),
18446
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("button", { type: "button", onClick: () => void copyDebugText(latex), children: "Copy TeX" }),
18447
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("button", { type: "button", "aria-expanded": debugImportOpen, onClick: openDebugJsonImport, children: "Import JSON" })
18448
- ] }),
18449
- debugImportOpen ? /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "butex-document2-widget__dev-import", children: [
18627
+ "immediate"
18628
+ );
18629
+ }
18630
+ const showEditorPanel = !previewOnly && editorOpen;
18631
+ const showPreviewPanel = previewOnly || previewOpen;
18632
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
18633
+ "div",
18634
+ {
18635
+ ref: widgetRef,
18636
+ className: ["butex-document2-widget", className].filter(Boolean).join(" "),
18637
+ dir: uiLocaleDirection(uiLocale),
18638
+ lang: uiLocale,
18639
+ children: /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "butex-document2-widget__shell", children: [
18640
+ !previewOnly ? /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "butex-document2-widget__toolbar", children: [
18450
18641
  /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
18451
- "textarea",
18642
+ DocumentInsertToolbar,
18452
18643
  {
18453
- value: debugImportText,
18454
- "aria-label": "Import Document2 JSON",
18455
- spellCheck: false,
18456
- onChange: (event) => {
18457
- setDebugImportText(event.currentTarget.value);
18458
- setDebugImportError("");
18644
+ canUndo,
18645
+ canRedo,
18646
+ editableEquations,
18647
+ uiLocale,
18648
+ digitForm,
18649
+ selectionCount,
18650
+ canMoveSelectionUp,
18651
+ canMoveSelectionDown,
18652
+ canFormatText,
18653
+ activeTextStyles,
18654
+ onUndo: undoDocument,
18655
+ onRedo: redoDocument,
18656
+ onToggleTextStyle: toggleInlineTextStyle,
18657
+ onMoveSelectionUp: () => moveSelectedBlocks(-1),
18658
+ onMoveSelectionDown: () => moveSelectedBlocks(1),
18659
+ onDeleteSelection: deleteSelectedBlocks,
18660
+ onAddSection: () => {
18661
+ clearBlockSelection();
18662
+ applyDocument(addDocument2TextBlock(documentRef.current, "\\section", afterBlockId()), "immediate");
18663
+ },
18664
+ onAddSubsection: () => {
18665
+ clearBlockSelection();
18666
+ applyDocument(addDocument2TextBlock(documentRef.current, "\\subsection", afterBlockId()), "immediate");
18667
+ },
18668
+ onAddSubsubsection: () => {
18669
+ clearBlockSelection();
18670
+ applyDocument(addDocument2TextBlock(documentRef.current, "\\subsubsection", afterBlockId()), "immediate");
18671
+ },
18672
+ onAddParagraph: () => {
18673
+ clearBlockSelection();
18674
+ applyDocument(addDocument2TextBlock(documentRef.current, "\\paragraph", afterBlockId()), "immediate");
18675
+ },
18676
+ onAddInlineEquation: () => openNewEquation("inline"),
18677
+ onAddDisplayEquation: () => openNewEquation("display"),
18678
+ onAddTable: (rowCount, colCount) => {
18679
+ clearBlockSelection();
18680
+ applyDocument(addDocument2TableBlock(documentRef.current, "l".repeat(colCount), rowCount, colCount, afterBlockId()), "immediate");
18681
+ },
18682
+ onAddList: () => {
18683
+ clearBlockSelection();
18684
+ applyDocument(addDocument2ListBlock(documentRef.current, false, afterBlockId()), "immediate");
18685
+ },
18686
+ onAddEnumerate: () => {
18687
+ clearBlockSelection();
18688
+ applyDocument(addDocument2ListBlock(documentRef.current, true, afterBlockId()), "immediate");
18689
+ },
18690
+ onAddFigure: () => insertImageBlock(""),
18691
+ onInsertCitation: openCitePickerForInsert,
18692
+ onInsertInternalRef: openRefPickerForInsert,
18693
+ onInsertBibliography: () => {
18694
+ clearBlockSelection();
18695
+ applyDocument(ensureDocument2BibliographyBlock(documentRef.current, afterBlockId()), "immediate");
18696
+ },
18697
+ onManageReferences: () => setReferencesOpen(true),
18698
+ onManageLabels: () => setLabelsOpen(true),
18699
+ onOpenArticleMeta: () => focusArticleMetaPanel(articleMetaPanelRef.current),
18700
+ onDigitFormChange: (next) => {
18701
+ setDigitFormState(next);
18702
+ onDigitFormChange?.(next);
18459
18703
  }
18460
18704
  }
18461
18705
  ),
18706
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "butex-document2-widget__toolbar-group", role: "group", "aria-label": messages.panels, children: [
18707
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("button", { type: "button", "aria-pressed": editorOpen, onClick: toggleEditorPanel, children: editorOpen ? messages.hideEditor : messages.editor }),
18708
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("button", { type: "button", "aria-pressed": previewOpen, onClick: togglePreviewPanel, children: previewOpen ? messages.hidePreview : messages.preview })
18709
+ ] }),
18710
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "butex-document2-widget__toolbar-group", role: "group", "aria-label": messages.collapseBlocks, children: [
18711
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("button", { type: "button", onClick: collapseAllBlocks, children: messages.collapseAll }),
18712
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("button", { type: "button", onClick: openAllBlocks, children: messages.openAll })
18713
+ ] })
18714
+ ] }) : null,
18715
+ error ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("p", { className: "butex-document2-widget__error", children: error }) : null,
18716
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
18717
+ "div",
18718
+ {
18719
+ className: `butex-document2-widget__layout butex-document2-widget__layout--editor-${showEditorPanel ? "open" : "closed"} butex-document2-widget__layout--preview-${showPreviewPanel ? "open" : "closed"}`,
18720
+ children: [
18721
+ !previewOnly ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
18722
+ "section",
18723
+ {
18724
+ className: "butex-document2-widget__panel butex-document2-widget__editor-panel",
18725
+ "aria-label": messages.documentEditor,
18726
+ "aria-hidden": !showEditorPanel,
18727
+ children: /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "butex-document2-widget__blocks", children: [
18728
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
18729
+ ArticleMetaPanel,
18730
+ {
18731
+ meta: documentNode.meta ?? emptyDocument2Meta(),
18732
+ uiLocale,
18733
+ digitForm,
18734
+ panelRef: articleMetaPanelRef,
18735
+ onChange: (patch) => {
18736
+ clearBlockSelection();
18737
+ applyDocument(updateDocument2Meta(documentRef.current, patch), "text");
18738
+ }
18739
+ }
18740
+ ),
18741
+ documentNode.blocks.map((block, blockIndex) => /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
18742
+ BlockEditor,
18743
+ {
18744
+ block,
18745
+ references: documentNode.references,
18746
+ documentNode,
18747
+ documentDirection,
18748
+ digitForm,
18749
+ mathOutput,
18750
+ equationSide,
18751
+ editableEquations,
18752
+ editableCitations: !previewOnly,
18753
+ uiLocale,
18754
+ isCollapsed: collapsedBlockIds.has(block.id),
18755
+ selected: Boolean(blockSelection && blockIndex >= blockSelection.from && blockIndex <= blockSelection.to),
18756
+ onToggleSelect: toggleSelectBlock,
18757
+ onTextChange: (fieldId, tokenId, text) => {
18758
+ clearBlockSelection();
18759
+ applyDocument(updateTextToken(documentRef.current, fieldId, tokenId, text), "text");
18760
+ },
18761
+ onOpenMath: openMath,
18762
+ onDeleteMath: editableEquations ? deleteMathToken : void 0,
18763
+ onOpenCite: openCite,
18764
+ onDeleteCite: deleteCiteToken,
18765
+ onOpenRef: openRef,
18766
+ onDeleteRef: deleteRefToken,
18767
+ onToggleCollapse: toggleBlockCollapse,
18768
+ onBlockFocus: rememberBlockFocus,
18769
+ onFieldFocus: rememberFieldFocus,
18770
+ onMathFocus: rememberMathFocus,
18771
+ onFieldBlur,
18772
+ onImageSrcChange: (blockId, value) => {
18773
+ clearBlockSelection();
18774
+ applyDocument(updateDocument2ImageValue(documentRef.current, blockId, value), "text");
18775
+ },
18776
+ onImageAssetChange: (blockId, asset) => {
18777
+ clearBlockSelection();
18778
+ applyDocument(updateDocument2ImageAsset(documentRef.current, blockId, asset), "immediate");
18779
+ },
18780
+ onImageAssetClear: (blockId) => {
18781
+ clearBlockSelection();
18782
+ applyDocument(clearDocument2ImageAsset(documentRef.current, blockId), "immediate");
18783
+ },
18784
+ resolveImageUrl,
18785
+ onRequestImagePick,
18786
+ listImageAssets,
18787
+ renderImageBlockEditor,
18788
+ onFloatMetaChange: (blockId, kind, patch) => {
18789
+ clearBlockSelection();
18790
+ applyDocument(
18791
+ kind === "image" ? updateDocument2ImageMeta(documentRef.current, blockId, patch) : updateDocument2TableMeta(documentRef.current, blockId, patch),
18792
+ "text"
18793
+ );
18794
+ },
18795
+ onParagraphCenteredChange: (blockId, centered) => {
18796
+ clearBlockSelection();
18797
+ applyDocument(updateDocument2TextBlockCentered(documentRef.current, blockId, centered), "text");
18798
+ },
18799
+ onAddListItem: (listBlockId) => {
18800
+ clearBlockSelection();
18801
+ applyDocument(addDocument2ListItem(documentRef.current, listBlockId), "immediate");
18802
+ },
18803
+ onRemoveListItem: (listBlockId, itemId) => {
18804
+ clearBlockSelection();
18805
+ applyDocument(removeDocument2ListItem(documentRef.current, listBlockId, itemId), "immediate");
18806
+ },
18807
+ onManageReferences: () => setReferencesOpen(true)
18808
+ },
18809
+ block.id
18810
+ ))
18811
+ ] })
18812
+ }
18813
+ ) : null,
18814
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
18815
+ "section",
18816
+ {
18817
+ className: "butex-document2-widget__panel butex-document2-widget__preview-panel",
18818
+ "aria-label": messages.documentPreview,
18819
+ "aria-hidden": !showPreviewPanel,
18820
+ children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
18821
+ DocumentPreview,
18822
+ {
18823
+ blocks: preview.blocks,
18824
+ output: mathOutput,
18825
+ documentDirection,
18826
+ uiLocale,
18827
+ digitForm,
18828
+ resolveImageUrl
18829
+ }
18830
+ )
18831
+ }
18832
+ )
18833
+ ]
18834
+ }
18835
+ ),
18836
+ debugEnabled ? /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "butex-document2-widget__dev", children: [
18837
+ debugEnabled && documentNode.diagnostics.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_jsx_runtime17.Fragment, { children: [
18838
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("strong", { children: messages.importWarnings }),
18839
+ documentNode.diagnostics.map((diagnostic) => /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("p", { className: "butex-document2-widget__dev-diagnostic", children: formatDocument2Diagnostic(diagnostic, messages) }, `${diagnostic.path}-${diagnostic.message}`))
18840
+ ] }) : null,
18462
18841
  /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "butex-document2-widget__dev-actions", children: [
18463
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("button", { type: "button", onClick: applyDebugJsonImport, children: "Apply" }),
18842
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("button", { type: "button", onClick: () => void copyDebugText(JSON.stringify(documentJson, null, 2)), children: "Copy JSON" }),
18843
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("button", { type: "button", onClick: () => void copyDebugText(latex), children: "Copy TeX" }),
18844
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("button", { type: "button", "aria-expanded": debugImportOpen, onClick: openDebugJsonImport, children: "Import JSON" })
18845
+ ] }),
18846
+ debugImportOpen ? /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "butex-document2-widget__dev-import", children: [
18464
18847
  /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
18465
- "button",
18848
+ "textarea",
18466
18849
  {
18467
- type: "button",
18468
- onClick: () => {
18469
- setDebugImportOpen(false);
18850
+ value: debugImportText,
18851
+ "aria-label": "Import Document2 JSON",
18852
+ spellCheck: false,
18853
+ onChange: (event) => {
18854
+ setDebugImportText(event.currentTarget.value);
18470
18855
  setDebugImportError("");
18471
- },
18472
- children: "Cancel"
18856
+ }
18473
18857
  }
18474
- )
18475
- ] }),
18476
- debugImportError ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("p", { className: "butex-document2-widget__dev-error", children: debugImportError }) : null
18858
+ ),
18859
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "butex-document2-widget__dev-actions", children: [
18860
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("button", { type: "button", onClick: applyDebugJsonImport, children: "Apply" }),
18861
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
18862
+ "button",
18863
+ {
18864
+ type: "button",
18865
+ onClick: () => {
18866
+ setDebugImportOpen(false);
18867
+ setDebugImportError("");
18868
+ },
18869
+ children: "Cancel"
18870
+ }
18871
+ )
18872
+ ] }),
18873
+ debugImportError ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("p", { className: "butex-document2-widget__dev-error", children: debugImportError }) : null
18874
+ ] }) : null,
18875
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("strong", { children: "LaTeX" }),
18876
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("pre", { children: latex }),
18877
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("strong", { children: "JSON" }),
18878
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("pre", { children: JSON.stringify(documentJson, null, 2) }),
18879
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("strong", { children: "AST" }),
18880
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("pre", { children: JSON.stringify(documentNode, null, 2) })
18477
18881
  ] }) : null,
18478
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("strong", { children: "LaTeX" }),
18479
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("pre", { children: latex }),
18480
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("strong", { children: "JSON" }),
18481
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("pre", { children: JSON.stringify(documentJson, null, 2) }),
18482
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("strong", { children: "AST" }),
18483
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("pre", { children: JSON.stringify(documentNode, null, 2) })
18484
- ] }) : null,
18485
- !previewOnly && selectedMath ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
18486
- EquationDrawer,
18487
- {
18488
- debug: debugEnabled,
18489
- session: selectedMath.session,
18490
- reason: selectedMath.reason,
18491
- mathMode: selectedMath.mathMode,
18492
- canDelete: Boolean(selectedMath.tokenId),
18493
- equationSide,
18494
- uiLocale,
18495
- labelEnabled: selectedMath.labelEnabled,
18496
- label: selectedMath.label,
18497
- labels: documentLabels,
18498
- references: documentNode.references,
18499
- ownerId: selectedMath.tokenId ?? void 0,
18500
- onLabelEnabledChange: (enabled) => setSelectedMath((current) => current ? { ...current, labelEnabled: enabled } : current),
18501
- onLabelChange: (nextLabel) => setSelectedMath((current) => current ? { ...current, label: nextLabel } : current),
18502
- onMathModeChange: (mode) => setSelectedMath((current) => current ? { ...current, mathMode: mode } : current),
18503
- onClose: () => setSelectedMath(null),
18504
- onSave: saveEquation,
18505
- onDelete: deleteSelectedEquation
18506
- }
18507
- ) : null,
18508
- !previewOnly ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
18509
- CitePickerPopover,
18510
- {
18511
- open: citePicker !== null,
18512
- references: documentNode.references,
18513
- initialKeys: citePicker?.keys ?? [],
18514
- uiLocale,
18515
- digitForm,
18516
- onClose: () => setCitePicker(null),
18517
- onConfirm: confirmCiteKeys,
18518
- onManageReferences: () => {
18519
- setCitePicker(null);
18520
- setReferencesOpen(true);
18882
+ !previewOnly && selectedMath ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
18883
+ EquationDrawer,
18884
+ {
18885
+ debug: debugEnabled,
18886
+ session: selectedMath.session,
18887
+ reason: selectedMath.reason,
18888
+ mathMode: selectedMath.mathMode,
18889
+ canDelete: Boolean(selectedMath.tokenId),
18890
+ equationSide,
18891
+ uiLocale,
18892
+ labelEnabled: selectedMath.labelEnabled,
18893
+ label: selectedMath.label,
18894
+ labels: documentLabels,
18895
+ references: documentNode.references,
18896
+ ownerId: selectedMath.tokenId ?? void 0,
18897
+ onLabelEnabledChange: (enabled) => setSelectedMath((current) => current ? { ...current, labelEnabled: enabled } : current),
18898
+ onLabelChange: (nextLabel) => setSelectedMath((current) => current ? { ...current, label: nextLabel } : current),
18899
+ onMathModeChange: (mode) => setSelectedMath((current) => current ? { ...current, mathMode: mode } : current),
18900
+ onClose: () => setSelectedMath(null),
18901
+ onSave: saveEquation,
18902
+ onDelete: deleteSelectedEquation
18521
18903
  }
18522
- }
18523
- ) : null,
18524
- !previewOnly ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
18525
- RefPickerPopover,
18526
- {
18527
- open: refPicker !== null,
18528
- labels: documentLabels,
18529
- initialKeys: refPicker?.keys ?? [],
18530
- initialRefCommand: refPicker?.refCommand ?? "ref",
18531
- uiLocale,
18532
- digitForm,
18533
- onClose: () => setRefPicker(null),
18534
- onConfirm: confirmRefKeys,
18535
- onManageLabels: () => {
18536
- setRefPicker(null);
18537
- setLabelsOpen(true);
18904
+ ) : null,
18905
+ !previewOnly ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
18906
+ CitePickerPopover,
18907
+ {
18908
+ open: citePicker !== null,
18909
+ references: documentNode.references,
18910
+ initialKeys: citePicker?.keys ?? [],
18911
+ uiLocale,
18912
+ digitForm,
18913
+ onClose: () => setCitePicker(null),
18914
+ onConfirm: confirmCiteKeys,
18915
+ onManageReferences: () => {
18916
+ setCitePicker(null);
18917
+ setReferencesOpen(true);
18918
+ }
18538
18919
  }
18539
- }
18540
- ) : null,
18541
- !previewOnly ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
18542
- ReferencesPanel,
18543
- {
18544
- open: referencesOpen,
18545
- references: documentNode.references,
18546
- labels: documentLabels,
18547
- uiLocale,
18548
- digitForm,
18549
- onClose: () => setReferencesOpen(false),
18550
- onAdd: (partial) => applyDocument(addDocument2Reference(documentRef.current, partial), "immediate"),
18551
- onUpdate: (referenceId, patch) => applyDocument(updateDocument2Reference(documentRef.current, referenceId, patch), "immediate"),
18552
- onRemove: (referenceId) => applyDocument(removeDocument2Reference(documentRef.current, referenceId), "immediate"),
18553
- onMove: (referenceId, direction) => applyDocument(moveDocument2Reference(documentRef.current, referenceId, direction), "immediate")
18554
- }
18555
- ) : null,
18556
- !previewOnly ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
18557
- LabelsPanel,
18558
- {
18559
- open: labelsOpen,
18560
- labels: documentLabels,
18561
- references: documentNode.references,
18562
- uiLocale,
18563
- digitForm,
18564
- onClose: () => setLabelsOpen(false),
18565
- onUpdateFloat: (ownerId, kind, patch) => applyDocument(
18566
- kind === "fig" ? updateDocument2ImageMeta(documentRef.current, ownerId, patch) : updateDocument2TableMeta(documentRef.current, ownerId, patch),
18567
- "text"
18568
- ),
18569
- onUpdateEquationLabel: (tokenId, patch) => applyDocument(updateMathTokenLabel(documentRef.current, tokenId, patch), "text")
18570
- }
18571
- ) : null
18572
- ] })
18573
- }
18574
- );
18575
- }
18920
+ ) : null,
18921
+ !previewOnly ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
18922
+ RefPickerPopover,
18923
+ {
18924
+ open: refPicker !== null,
18925
+ labels: documentLabels,
18926
+ initialKeys: refPicker?.keys ?? [],
18927
+ initialRefCommand: refPicker?.refCommand ?? "ref",
18928
+ uiLocale,
18929
+ digitForm,
18930
+ onClose: () => setRefPicker(null),
18931
+ onConfirm: confirmRefKeys,
18932
+ onManageLabels: () => {
18933
+ setRefPicker(null);
18934
+ setLabelsOpen(true);
18935
+ }
18936
+ }
18937
+ ) : null,
18938
+ !previewOnly ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
18939
+ ReferencesPanel,
18940
+ {
18941
+ open: referencesOpen,
18942
+ references: documentNode.references,
18943
+ labels: documentLabels,
18944
+ uiLocale,
18945
+ digitForm,
18946
+ onClose: () => setReferencesOpen(false),
18947
+ onAdd: (partial) => applyDocument(addDocument2Reference(documentRef.current, partial), "immediate"),
18948
+ onUpdate: (referenceId, patch) => applyDocument(updateDocument2Reference(documentRef.current, referenceId, patch), "immediate"),
18949
+ onRemove: (referenceId) => applyDocument(removeDocument2Reference(documentRef.current, referenceId), "immediate"),
18950
+ onMove: (referenceId, direction) => applyDocument(moveDocument2Reference(documentRef.current, referenceId, direction), "immediate")
18951
+ }
18952
+ ) : null,
18953
+ !previewOnly ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
18954
+ LabelsPanel,
18955
+ {
18956
+ open: labelsOpen,
18957
+ labels: documentLabels,
18958
+ references: documentNode.references,
18959
+ uiLocale,
18960
+ digitForm,
18961
+ onClose: () => setLabelsOpen(false),
18962
+ onUpdateFloat: (ownerId, kind, patch) => applyDocument(
18963
+ kind === "fig" ? updateDocument2ImageMeta(documentRef.current, ownerId, patch) : updateDocument2TableMeta(documentRef.current, ownerId, patch),
18964
+ "text"
18965
+ ),
18966
+ onUpdateEquationLabel: (tokenId, patch) => applyDocument(updateMathTokenLabel(documentRef.current, tokenId, patch), "text")
18967
+ }
18968
+ ) : null
18969
+ ] })
18970
+ }
18971
+ );
18972
+ }
18973
+ );
18974
+ ButexDocumentEditor2.displayName = "ButexDocumentEditor2";
18576
18975
  // Annotate the CommonJS export names for ESM import in node:
18577
18976
  0 && (module.exports = {
18578
18977
  ButexDocumentEditor2,