@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.
@@ -3,8 +3,9 @@ import { useEffect, useId, useRef, useState } from "react";
3
3
 
4
4
  // src/document2/ids.ts
5
5
  var nextId = 1;
6
+ var instanceId = `${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 10)}`;
6
7
  function document2Id(prefix) {
7
- const id = `${prefix}_${String(nextId)}`;
8
+ const id = `${prefix}_${instanceId}_${String(nextId)}`;
8
9
  nextId += 1;
9
10
  return id;
10
11
  }
@@ -1169,6 +1170,19 @@ function asBlockJson(value) {
1169
1170
  }
1170
1171
  return value;
1171
1172
  }
1173
+ function blockIdFromJson(json, state) {
1174
+ const imported = typeof json.id === "string" ? json.id.trim() : "";
1175
+ if (imported.length > 0 && !state.used.has(imported)) {
1176
+ state.used.add(imported);
1177
+ return imported;
1178
+ }
1179
+ let generated = document2Id("block");
1180
+ while (state.used.has(generated)) {
1181
+ generated = document2Id("block");
1182
+ }
1183
+ state.used.add(generated);
1184
+ return generated;
1185
+ }
1172
1186
  function pushDiagnostic(diagnostics, options, path, message) {
1173
1187
  if (options.strict) {
1174
1188
  throw new Error(message);
@@ -1350,40 +1364,44 @@ function pushFormattedTextTokens(tokens, text, sourceStart, formats) {
1350
1364
  function closingForList(command) {
1351
1365
  return command === "\\begin{itemize}" ? "\\end{itemize}" : "\\end{enumerate}";
1352
1366
  }
1353
- function parseTextBlock(json, options, path, diagnostics) {
1367
+ function parseTextBlock(json, options, path, diagnostics, blockIds) {
1354
1368
  const value = requireString(json.value, `${json.command} requires string value`);
1355
1369
  return {
1356
- id: document2Id("block"),
1370
+ id: blockIdFromJson(json, blockIds),
1357
1371
  kind: "textBlock",
1358
1372
  command: json.command,
1359
1373
  field: createInlineField2(value, json.math_objects ?? [], options, `${path}.value`, diagnostics, json.command === "\\paragraph" ? json.formats ?? [] : []),
1360
1374
  ...json.centered === true ? { centered: true } : {}
1361
1375
  };
1362
1376
  }
1363
- function parseListItem(json, options, path, diagnostics) {
1377
+ function parseListItem(json, options, path, diagnostics, blockIds) {
1364
1378
  const value = requireString(json.value, "Document list item requires string value");
1365
1379
  const blocksJson = Array.isArray(json.blocks) ? json.blocks : [];
1366
1380
  return {
1367
1381
  id: document2Id("item"),
1368
1382
  field: createInlineField2(value, json.math_objects ?? [], options, `${path}.value`, diagnostics, json.formats ?? []),
1369
- blocks: blocksJson.map((block, index) => parseBlock(asBlockJson(block), options, `${path}.blocks[${String(index)}]`, diagnostics))
1383
+ blocks: blocksJson.map(
1384
+ (block, index) => parseBlock(asBlockJson(block), options, `${path}.blocks[${String(index)}]`, diagnostics, blockIds)
1385
+ )
1370
1386
  };
1371
1387
  }
1372
- function parseListBlock(json, options, path, diagnostics) {
1388
+ function parseListBlock(json, options, path, diagnostics, blockIds) {
1373
1389
  if (!Array.isArray(json.items)) {
1374
1390
  throw new Error(`${json.command} requires items array`);
1375
1391
  }
1376
1392
  const command = json.command;
1377
1393
  const closing = closingForList(command);
1378
1394
  return {
1379
- id: document2Id("block"),
1395
+ id: blockIdFromJson(json, blockIds),
1380
1396
  kind: "list",
1381
1397
  command,
1382
1398
  closing,
1383
- items: json.items.map((item, index) => parseListItem(item, options, `${path}.items[${String(index)}]`, diagnostics))
1399
+ items: json.items.map(
1400
+ (item, index) => parseListItem(item, options, `${path}.items[${String(index)}]`, diagnostics, blockIds)
1401
+ )
1384
1402
  };
1385
1403
  }
1386
- function parseTableBlock(json, options, path, diagnostics) {
1404
+ function parseTableBlock(json, options, path, diagnostics, blockIds) {
1387
1405
  if (!Array.isArray(json.rows)) {
1388
1406
  throw new Error("\\begin{tabular} requires rows array");
1389
1407
  }
@@ -1406,7 +1424,7 @@ function parseTableBlock(json, options, path, diagnostics) {
1406
1424
  pushDiagnostic(diagnostics, options, path, `math_objects count mismatch: detected ${String(mathObjectIndex)}, got ${String(mathObjects.length)}`);
1407
1425
  }
1408
1426
  return {
1409
- id: document2Id("block"),
1427
+ id: blockIdFromJson(json, blockIds),
1410
1428
  kind: "table",
1411
1429
  command: "\\begin{tabular}",
1412
1430
  closing: "\\end{tabular}",
@@ -1415,11 +1433,11 @@ function parseTableBlock(json, options, path, diagnostics) {
1415
1433
  ...parseFloatMetaFromJson(json)
1416
1434
  };
1417
1435
  }
1418
- function parseImageBlock(json) {
1436
+ function parseImageBlock(json, blockIds) {
1419
1437
  const assetId = typeof json.asset_id === "string" && json.asset_id.length > 0 ? json.asset_id : void 0;
1420
1438
  const value = assetId !== void 0 ? typeof json.value === "string" ? json.value : "" : requireString(json.value, "\\includegraphics requires string value");
1421
1439
  return {
1422
- id: document2Id("block"),
1440
+ id: blockIdFromJson(json, blockIds),
1423
1441
  kind: "image",
1424
1442
  command: "\\includegraphics",
1425
1443
  value,
@@ -1428,43 +1446,43 @@ function parseImageBlock(json) {
1428
1446
  ...parseFloatMetaFromJson(json)
1429
1447
  };
1430
1448
  }
1431
- function parseRawBlock(json) {
1449
+ function parseRawBlock(json, blockIds) {
1432
1450
  return {
1433
- id: document2Id("block"),
1451
+ id: blockIdFromJson(json, blockIds),
1434
1452
  kind: "raw",
1435
1453
  command: "\\raw",
1436
1454
  value: typeof json.value === "string" ? json.value : ""
1437
1455
  };
1438
1456
  }
1439
- function parseBibliographyBlock() {
1457
+ function parseBibliographyBlock(json, blockIds) {
1440
1458
  return {
1441
- id: document2Id("block"),
1459
+ id: blockIdFromJson(json, blockIds),
1442
1460
  kind: "bibliography",
1443
1461
  command: "\\begin{thebibliography}",
1444
1462
  closing: "\\end{thebibliography}"
1445
1463
  };
1446
1464
  }
1447
- function parseBlock(json, options, path, diagnostics) {
1465
+ function parseBlock(json, options, path, diagnostics, blockIds) {
1448
1466
  if (TEXT_COMMANDS.has(json.command)) {
1449
- return parseTextBlock(json, options, path, diagnostics);
1467
+ return parseTextBlock(json, options, path, diagnostics, blockIds);
1450
1468
  }
1451
1469
  if (LIST_COMMANDS.has(json.command)) {
1452
- return parseListBlock(json, options, path, diagnostics);
1470
+ return parseListBlock(json, options, path, diagnostics, blockIds);
1453
1471
  }
1454
1472
  if (json.command === "\\begin{tabular}") {
1455
- return parseTableBlock(json, options, path, diagnostics);
1473
+ return parseTableBlock(json, options, path, diagnostics, blockIds);
1456
1474
  }
1457
1475
  if (json.command === "\\includegraphics") {
1458
- return parseImageBlock(json);
1476
+ return parseImageBlock(json, blockIds);
1459
1477
  }
1460
1478
  if (json.command === "\\begin{thebibliography}" || json.command === "\\bibliography") {
1461
- return parseBibliographyBlock();
1479
+ return parseBibliographyBlock(json, blockIds);
1462
1480
  }
1463
1481
  if (json.command === "\\raw") {
1464
- return parseRawBlock(json);
1482
+ return parseRawBlock(json, blockIds);
1465
1483
  }
1466
1484
  return {
1467
- id: document2Id("block"),
1485
+ id: blockIdFromJson(json, blockIds),
1468
1486
  kind: "raw",
1469
1487
  command: "\\raw",
1470
1488
  value: typeof json.value === "string" ? json.value : json.command
@@ -1478,11 +1496,14 @@ function fromDocumentJson2(json, options = {}) {
1478
1496
  throw new Error("DocumentObject requires blocks array");
1479
1497
  }
1480
1498
  const diagnostics = [];
1499
+ const blockIds = { used: /* @__PURE__ */ new Set() };
1481
1500
  return {
1482
1501
  nodeType: "DocumentObject",
1483
1502
  meta: normalizeDocument2Meta(json.meta),
1484
1503
  references: parseReferences(json.references),
1485
- blocks: json.blocks.map((block, index) => parseBlock(asBlockJson(block), options, `$.blocks[${String(index)}]`, diagnostics)),
1504
+ blocks: json.blocks.map(
1505
+ (block, index) => parseBlock(asBlockJson(block), options, `$.blocks[${String(index)}]`, diagnostics, blockIds)
1506
+ ),
1486
1507
  diagnostics
1487
1508
  };
1488
1509
  }
@@ -8655,12 +8676,23 @@ function addDocument2TableBlock(document2, columns = "lll", rowCount = 3, colCou
8655
8676
  };
8656
8677
  return insertDocument2BlockAfter(document2, afterBlockId, block);
8657
8678
  }
8658
- function addDocument2ImageBlock(document2, src = "", afterBlockId) {
8679
+ function normalizeImageInput(srcOrAsset) {
8680
+ if (typeof srcOrAsset === "string") {
8681
+ return srcOrAsset.length > 0 ? { value: srcOrAsset, assetId: srcOrAsset } : { value: "" };
8682
+ }
8683
+ if (srcOrAsset && srcOrAsset.assetId.length > 0) {
8684
+ return { value: srcOrAsset.value ?? srcOrAsset.assetId, assetId: srcOrAsset.assetId };
8685
+ }
8686
+ return { value: "" };
8687
+ }
8688
+ function addDocument2ImageBlock(document2, srcOrAsset, afterBlockId) {
8689
+ const image = normalizeImageInput(srcOrAsset);
8659
8690
  const block = {
8660
8691
  id: document2Id("block"),
8661
8692
  kind: "image",
8662
8693
  command: "\\includegraphics",
8663
- value: src,
8694
+ value: image.value,
8695
+ ...image.assetId !== void 0 ? { assetId: image.assetId } : {},
8664
8696
  options: { width: "0.8\\columnwidth" },
8665
8697
  ...defaultFloatMeta()
8666
8698
  };
@@ -8687,6 +8719,55 @@ function updateDocument2ImageValue(document2, blockId, value) {
8687
8719
  visit(next.blocks);
8688
8720
  return next;
8689
8721
  }
8722
+ function updateDocument2ImageAsset(document2, blockId, asset) {
8723
+ const next = cloneDocument(document2);
8724
+ const image = normalizeImageInput(asset);
8725
+ function visit(blocks) {
8726
+ for (const block of blocks) {
8727
+ if (block.id === blockId && block.kind === "image") {
8728
+ block.value = image.value;
8729
+ if (image.assetId !== void 0) {
8730
+ block.assetId = image.assetId;
8731
+ } else {
8732
+ delete block.assetId;
8733
+ }
8734
+ return true;
8735
+ }
8736
+ if (block.kind === "list") {
8737
+ for (const item of block.items) {
8738
+ if (visit(item.blocks)) {
8739
+ return true;
8740
+ }
8741
+ }
8742
+ }
8743
+ }
8744
+ return false;
8745
+ }
8746
+ visit(next.blocks);
8747
+ return next;
8748
+ }
8749
+ function clearDocument2ImageAsset(document2, blockId) {
8750
+ const next = cloneDocument(document2);
8751
+ function visit(blocks) {
8752
+ for (const block of blocks) {
8753
+ if (block.id === blockId && block.kind === "image") {
8754
+ block.value = "";
8755
+ delete block.assetId;
8756
+ return true;
8757
+ }
8758
+ if (block.kind === "list") {
8759
+ for (const item of block.items) {
8760
+ if (visit(item.blocks)) {
8761
+ return true;
8762
+ }
8763
+ }
8764
+ }
8765
+ }
8766
+ return false;
8767
+ }
8768
+ visit(next.blocks);
8769
+ return next;
8770
+ }
8690
8771
  function updateDocument2ImageMeta(document2, blockId, patch) {
8691
8772
  const next = cloneDocument(document2);
8692
8773
  function visit(blocks) {
@@ -8982,6 +9063,159 @@ function updateDocument2Meta(document2, patch) {
8982
9063
  return next;
8983
9064
  }
8984
9065
 
9066
+ // src/document2/exportJson.ts
9067
+ function serializeField(field) {
9068
+ let value = "";
9069
+ let hasPersistedMath = false;
9070
+ const formats = [];
9071
+ const mathObjects = [];
9072
+ for (const token of field.tokens) {
9073
+ if (token.kind === "text") {
9074
+ const start = value.length;
9075
+ value += token.text;
9076
+ if (token.text.length > 0 && (token.style?.bold || token.style?.italic || token.style?.underline)) {
9077
+ formats.push({
9078
+ start,
9079
+ end: value.length,
9080
+ ...token.style.bold ? { bold: true } : {},
9081
+ ...token.style.italic ? { italic: true } : {},
9082
+ ...token.style.underline ? { underline: true } : {}
9083
+ });
9084
+ }
9085
+ continue;
9086
+ }
9087
+ if (token.kind === "cite") {
9088
+ value += citeTokenLatex(token.keys);
9089
+ continue;
9090
+ }
9091
+ if (token.kind === "ref") {
9092
+ value += refTokenLatex(token.keys, token.refCommand);
9093
+ continue;
9094
+ }
9095
+ value += token.source;
9096
+ if (!token.math || token.sourceOwner === "raw") {
9097
+ if (token.labelEnabled !== void 0 || token.label !== void 0) {
9098
+ hasPersistedMath = true;
9099
+ mathObjects.push({
9100
+ node_type: "RawMathObject",
9101
+ ...token.labelEnabled !== void 0 ? { label_enabled: token.labelEnabled } : {},
9102
+ ...token.label !== void 0 ? { label: token.label } : {}
9103
+ });
9104
+ } else {
9105
+ mathObjects.push(null);
9106
+ }
9107
+ continue;
9108
+ }
9109
+ hasPersistedMath = true;
9110
+ mathObjects.push({
9111
+ ...toMathObjectJson(token.math),
9112
+ ...token.sourceSide ? { source_side: token.sourceSide } : {},
9113
+ source_owner: token.sourceOwner,
9114
+ ...token.labelEnabled !== void 0 ? { label_enabled: token.labelEnabled } : {},
9115
+ ...token.label !== void 0 ? { label: token.label } : {}
9116
+ });
9117
+ }
9118
+ return { value, formats, mathObjects, hasPersistedMath };
9119
+ }
9120
+ function fieldJson(field) {
9121
+ const serialized = serializeField(field);
9122
+ return {
9123
+ value: serialized.value,
9124
+ ...serialized.formats.length > 0 ? { formats: serialized.formats } : {},
9125
+ ...serialized.hasPersistedMath ? { math_objects: serialized.mathObjects } : {}
9126
+ };
9127
+ }
9128
+ function listItemJson(item) {
9129
+ const field = fieldJson(item.field);
9130
+ return {
9131
+ value: field.value ?? "",
9132
+ ...field.formats ? { formats: field.formats } : {},
9133
+ ...field.math_objects ? { math_objects: field.math_objects } : {},
9134
+ ...item.blocks.length > 0 ? { blocks: item.blocks.map(blockJson) } : {}
9135
+ };
9136
+ }
9137
+ function blockJson(block) {
9138
+ if (block.kind === "textBlock") {
9139
+ return {
9140
+ id: block.id,
9141
+ command: block.command,
9142
+ ...fieldJson(block.field),
9143
+ ...block.command === "\\paragraph" ? { centered: block.centered === true } : {}
9144
+ };
9145
+ }
9146
+ if (block.kind === "list") {
9147
+ return {
9148
+ id: block.id,
9149
+ command: block.command,
9150
+ closing: block.closing,
9151
+ items: block.items.map(listItemJson)
9152
+ };
9153
+ }
9154
+ if (block.kind === "table") {
9155
+ const fields = block.rows.map((row) => row.map(serializeField));
9156
+ const hasPersistedMath = fields.some((row) => row.some((field) => field.hasPersistedMath));
9157
+ return {
9158
+ id: block.id,
9159
+ command: block.command,
9160
+ closing: block.closing,
9161
+ columns: block.columns,
9162
+ rows: fields.map((row) => row.map((field) => field.value)),
9163
+ ...fields.some((row) => row.some((field) => field.formats.length > 0)) ? { cell_formats: fields.map((row) => row.map((field) => field.formats)) } : {},
9164
+ ...hasPersistedMath ? { math_objects: fields.flatMap((row) => row.flatMap((field) => field.mathObjects)) } : {},
9165
+ centered: block.centered,
9166
+ caption_enabled: block.captionEnabled,
9167
+ caption: block.caption,
9168
+ label_enabled: block.labelEnabled,
9169
+ label: block.label
9170
+ };
9171
+ }
9172
+ if (block.kind === "image") {
9173
+ return {
9174
+ id: block.id,
9175
+ command: block.command,
9176
+ value: block.value,
9177
+ ...block.assetId !== void 0 ? { asset_id: block.assetId } : {},
9178
+ options: { ...block.options },
9179
+ centered: block.centered,
9180
+ caption_enabled: block.captionEnabled,
9181
+ caption: block.caption,
9182
+ label_enabled: block.labelEnabled,
9183
+ label: block.label
9184
+ };
9185
+ }
9186
+ if (block.kind === "bibliography") {
9187
+ return { id: block.id, command: block.command, closing: block.closing };
9188
+ }
9189
+ return { id: block.id, command: block.command, value: block.value };
9190
+ }
9191
+ function referenceJson(reference) {
9192
+ return {
9193
+ key: reference.key,
9194
+ authors: reference.authors,
9195
+ title: reference.title,
9196
+ year: reference.year,
9197
+ url: reference.url,
9198
+ venue: reference.venue,
9199
+ field_separator: reference.fieldSeparator
9200
+ };
9201
+ }
9202
+ function toDocumentJson2(document2) {
9203
+ if (document2.nodeType !== "DocumentObject" || !Array.isArray(document2.blocks)) {
9204
+ throw new Error("toDocumentJson2 requires a live Document2Node");
9205
+ }
9206
+ return {
9207
+ node_type: "DocumentObject",
9208
+ meta: {
9209
+ title: document2.meta.title,
9210
+ authors: document2.meta.authors,
9211
+ date: { ...document2.meta.date },
9212
+ abstract: document2.meta.abstract
9213
+ },
9214
+ references: document2.references.map(referenceJson),
9215
+ blocks: document2.blocks.map(blockJson)
9216
+ };
9217
+ }
9218
+
8985
9219
  // src/document2/keys.ts
8986
9220
  var DOCUMENT2_KEY_PATTERN = /^[\p{L}\p{M}0-9:._-]+$/u;
8987
9221
  function normalizeDocument2Key(key) {
@@ -9462,155 +9696,6 @@ ${body}
9462
9696
  `;
9463
9697
  }
9464
9698
 
9465
- // src/document2/exportJson.ts
9466
- function serializeField(field) {
9467
- let value = "";
9468
- let hasPersistedMath = false;
9469
- const formats = [];
9470
- const mathObjects = [];
9471
- for (const token of field.tokens) {
9472
- if (token.kind === "text") {
9473
- const start = value.length;
9474
- value += token.text;
9475
- if (token.text.length > 0 && (token.style?.bold || token.style?.italic || token.style?.underline)) {
9476
- formats.push({
9477
- start,
9478
- end: value.length,
9479
- ...token.style.bold ? { bold: true } : {},
9480
- ...token.style.italic ? { italic: true } : {},
9481
- ...token.style.underline ? { underline: true } : {}
9482
- });
9483
- }
9484
- continue;
9485
- }
9486
- if (token.kind === "cite") {
9487
- value += citeTokenLatex(token.keys);
9488
- continue;
9489
- }
9490
- if (token.kind === "ref") {
9491
- value += refTokenLatex(token.keys, token.refCommand);
9492
- continue;
9493
- }
9494
- value += token.source;
9495
- if (!token.math || token.sourceOwner === "raw") {
9496
- if (token.labelEnabled !== void 0 || token.label !== void 0) {
9497
- hasPersistedMath = true;
9498
- mathObjects.push({
9499
- node_type: "RawMathObject",
9500
- ...token.labelEnabled !== void 0 ? { label_enabled: token.labelEnabled } : {},
9501
- ...token.label !== void 0 ? { label: token.label } : {}
9502
- });
9503
- } else {
9504
- mathObjects.push(null);
9505
- }
9506
- continue;
9507
- }
9508
- hasPersistedMath = true;
9509
- mathObjects.push({
9510
- ...toMathObjectJson(token.math),
9511
- ...token.sourceSide ? { source_side: token.sourceSide } : {},
9512
- source_owner: token.sourceOwner,
9513
- ...token.labelEnabled !== void 0 ? { label_enabled: token.labelEnabled } : {},
9514
- ...token.label !== void 0 ? { label: token.label } : {}
9515
- });
9516
- }
9517
- return { value, formats, mathObjects, hasPersistedMath };
9518
- }
9519
- function fieldJson(field) {
9520
- const serialized = serializeField(field);
9521
- return {
9522
- value: serialized.value,
9523
- ...serialized.formats.length > 0 ? { formats: serialized.formats } : {},
9524
- ...serialized.hasPersistedMath ? { math_objects: serialized.mathObjects } : {}
9525
- };
9526
- }
9527
- function listItemJson(item) {
9528
- const field = fieldJson(item.field);
9529
- return {
9530
- value: field.value ?? "",
9531
- ...field.formats ? { formats: field.formats } : {},
9532
- ...field.math_objects ? { math_objects: field.math_objects } : {},
9533
- ...item.blocks.length > 0 ? { blocks: item.blocks.map(blockJson) } : {}
9534
- };
9535
- }
9536
- function blockJson(block) {
9537
- if (block.kind === "textBlock") {
9538
- return {
9539
- command: block.command,
9540
- ...fieldJson(block.field),
9541
- ...block.command === "\\paragraph" ? { centered: block.centered === true } : {}
9542
- };
9543
- }
9544
- if (block.kind === "list") {
9545
- return {
9546
- command: block.command,
9547
- closing: block.closing,
9548
- items: block.items.map(listItemJson)
9549
- };
9550
- }
9551
- if (block.kind === "table") {
9552
- const fields = block.rows.map((row) => row.map(serializeField));
9553
- const hasPersistedMath = fields.some((row) => row.some((field) => field.hasPersistedMath));
9554
- return {
9555
- command: block.command,
9556
- closing: block.closing,
9557
- columns: block.columns,
9558
- rows: fields.map((row) => row.map((field) => field.value)),
9559
- ...fields.some((row) => row.some((field) => field.formats.length > 0)) ? { cell_formats: fields.map((row) => row.map((field) => field.formats)) } : {},
9560
- ...hasPersistedMath ? { math_objects: fields.flatMap((row) => row.flatMap((field) => field.mathObjects)) } : {},
9561
- centered: block.centered,
9562
- caption_enabled: block.captionEnabled,
9563
- caption: block.caption,
9564
- label_enabled: block.labelEnabled,
9565
- label: block.label
9566
- };
9567
- }
9568
- if (block.kind === "image") {
9569
- return {
9570
- command: block.command,
9571
- value: block.value,
9572
- ...block.assetId !== void 0 ? { asset_id: block.assetId } : {},
9573
- options: { ...block.options },
9574
- centered: block.centered,
9575
- caption_enabled: block.captionEnabled,
9576
- caption: block.caption,
9577
- label_enabled: block.labelEnabled,
9578
- label: block.label
9579
- };
9580
- }
9581
- if (block.kind === "bibliography") {
9582
- return { command: block.command, closing: block.closing };
9583
- }
9584
- return { command: block.command, value: block.value };
9585
- }
9586
- function referenceJson(reference) {
9587
- return {
9588
- key: reference.key,
9589
- authors: reference.authors,
9590
- title: reference.title,
9591
- year: reference.year,
9592
- url: reference.url,
9593
- venue: reference.venue,
9594
- field_separator: reference.fieldSeparator
9595
- };
9596
- }
9597
- function toDocumentJson2(document2) {
9598
- if (document2.nodeType !== "DocumentObject" || !Array.isArray(document2.blocks)) {
9599
- throw new Error("toDocumentJson2 requires a live Document2Node");
9600
- }
9601
- return {
9602
- node_type: "DocumentObject",
9603
- meta: {
9604
- title: document2.meta.title,
9605
- authors: document2.meta.authors,
9606
- date: { ...document2.meta.date },
9607
- abstract: document2.meta.abstract
9608
- },
9609
- references: document2.references.map(referenceJson),
9610
- blocks: document2.blocks.map(blockJson)
9611
- };
9612
- }
9613
-
9614
9699
  // src/document2/history.ts
9615
9700
  var DEFAULT_DOCUMENT2_HISTORY_MAX_DEPTH = 100;
9616
9701
  function createDocument2History(maxDepth = DEFAULT_DOCUMENT2_HISTORY_MAX_DEPTH) {
@@ -9956,6 +10041,15 @@ var DOCUMENT2_MESSAGES = {
9956
10041
  loadDocumentError: "\u062A\u0639\u0630\u0631 \u062A\u062D\u0645\u064A\u0644 \u0627\u0644\u0645\u0633\u062A\u0646\u062F",
9957
10042
  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.",
9958
10043
  emptyImagePath: "\u0644\u0627 \u064A\u0648\u062C\u062F \u0645\u0633\u0627\u0631 \u0635\u0648\u0631\u0629",
10044
+ noImageSelected: "\u0644\u0627 \u0635\u0648\u0631\u0629 \u0645\u062D\u062F\u062F\u0629",
10045
+ chooseImage: "\u0627\u062E\u062A\u064A\u0627\u0631 \u0635\u0648\u0631\u0629",
10046
+ changeImage: "\u062A\u063A\u064A\u064A\u0631",
10047
+ removeImage: "\u0625\u0632\u0627\u0644\u0629",
10048
+ selectImageAsset: "\u0627\u062E\u062A\u064A\u0627\u0631 \u0635\u0648\u0631\u0629",
10049
+ noImageAssets: "\u0644\u0627 \u062A\u0648\u062C\u062F \u0635\u0648\u0631 \u0645\u062A\u0627\u062D\u0629",
10050
+ loadingImageAssets: "\u062C\u0627\u0631 \u062A\u062D\u0645\u064A\u0644 \u0627\u0644\u0635\u0648\u0631\u2026",
10051
+ advancedImagePath: "\u062E\u064A\u0627\u0631\u0627\u062A \u0645\u062A\u0642\u062F\u0645\u0629",
10052
+ imagePreviewAlt: "\u0645\u0639\u0627\u064A\u0646\u0629 \u0627\u0644\u0635\u0648\u0631\u0629",
9959
10053
  blockSelection: "\u062A\u062D\u062F\u064A\u062F \u0627\u0644\u0643\u062A\u0644",
9960
10054
  selectBlock: "\u062A\u062D\u062F\u064A\u062F \u0627\u0644\u0643\u062A\u0644\u0629",
9961
10055
  selectedBlockCount: "\u0645\u062D\u062F\u062F\u0629",
@@ -10100,6 +10194,15 @@ var DOCUMENT2_MESSAGES = {
10100
10194
  loadDocumentError: "Could not load document",
10101
10195
  runtimePersistenceError: "A Document2Node cannot be loaded after JSON.stringify. Persist Document2Json with toDocumentJson2 instead.",
10102
10196
  emptyImagePath: "Empty image path",
10197
+ noImageSelected: "No image selected",
10198
+ chooseImage: "Choose image",
10199
+ changeImage: "Change",
10200
+ removeImage: "Remove",
10201
+ selectImageAsset: "Select image",
10202
+ noImageAssets: "No images available",
10203
+ loadingImageAssets: "Loading images\u2026",
10204
+ advancedImagePath: "Advanced",
10205
+ imagePreviewAlt: "Image preview",
10103
10206
  blockSelection: "Block selection",
10104
10207
  selectBlock: "Select block",
10105
10208
  selectedBlockCount: "selected",
@@ -10380,7 +10483,7 @@ function focusArticleMetaPanel(panel) {
10380
10483
  }
10381
10484
 
10382
10485
  // src/react-document2/ButexDocumentEditor2.tsx
10383
- import { useCallback as useCallback2, useEffect as useEffect9, useMemo as useMemo2, useRef as useRef8, useState as useState12 } from "react";
10486
+ import { forwardRef as forwardRef2, useCallback as useCallback2, useEffect as useEffect10, useImperativeHandle as useImperativeHandle2, useMemo as useMemo2, useRef as useRef8, useState as useState12 } from "react";
10384
10487
 
10385
10488
  // src/debugJson.ts
10386
10489
  function lineColumnForPosition(text, position) {
@@ -10409,7 +10512,7 @@ function parseJsonDebugInput(text) {
10409
10512
  }
10410
10513
 
10411
10514
  // src/react-document2/BlockEditor.tsx
10412
- import { useState as useState3 } from "react";
10515
+ import { useEffect as useEffect3, useState as useState3 } from "react";
10413
10516
 
10414
10517
  // src/react-document2/InlineField.tsx
10415
10518
  import { useRef as useRef3 } from "react";
@@ -11401,7 +11504,7 @@ function uiLocaleDirection(locale) {
11401
11504
  }
11402
11505
 
11403
11506
  // src/react-document2/BlockEditor.tsx
11404
- import { jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
11507
+ import { Fragment, jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
11405
11508
  function blockChromeClass(block) {
11406
11509
  const root = "butex-document2-widget__block";
11407
11510
  if (block.kind === "textBlock") {
@@ -11588,6 +11691,167 @@ function FloatMetaEditor({
11588
11691
  ] }) : null
11589
11692
  ] });
11590
11693
  }
11694
+ function ImageAssetEditor({
11695
+ block,
11696
+ uiLocale,
11697
+ messages,
11698
+ resolveImageUrl,
11699
+ onRequestImagePick,
11700
+ listImageAssets,
11701
+ renderImageBlockEditor,
11702
+ onSelectAsset,
11703
+ onClearAsset,
11704
+ onImageSrcChange,
11705
+ onBlockFocus
11706
+ }) {
11707
+ const [assets, setAssets] = useState3([]);
11708
+ const [assetsLoaded, setAssetsLoaded] = useState3(false);
11709
+ const [assetsLoading, setAssetsLoading] = useState3(false);
11710
+ const [advancedOpen, setAdvancedOpen] = useState3(false);
11711
+ const hasImage = block.value.length > 0 || Boolean(block.assetId);
11712
+ const resolvedUrl = hasImage ? resolveImageUrl ? resolveImageUrl({ assetId: block.assetId, value: block.value }) : block.value : "";
11713
+ const current = block.assetId ? { assetId: block.assetId, value: block.value } : null;
11714
+ const canUsePicker = Boolean(onRequestImagePick);
11715
+ useEffect3(() => {
11716
+ let alive = true;
11717
+ if (!listImageAssets || onRequestImagePick) {
11718
+ setAssets([]);
11719
+ setAssetsLoaded(false);
11720
+ setAssetsLoading(false);
11721
+ return () => {
11722
+ alive = false;
11723
+ };
11724
+ }
11725
+ setAssetsLoading(true);
11726
+ Promise.resolve(listImageAssets()).then((nextAssets) => {
11727
+ if (!alive) {
11728
+ return;
11729
+ }
11730
+ setAssets(nextAssets);
11731
+ setAssetsLoaded(true);
11732
+ }).catch(() => {
11733
+ if (!alive) {
11734
+ return;
11735
+ }
11736
+ setAssets([]);
11737
+ setAssetsLoaded(true);
11738
+ }).finally(() => {
11739
+ if (alive) {
11740
+ setAssetsLoading(false);
11741
+ }
11742
+ });
11743
+ return () => {
11744
+ alive = false;
11745
+ };
11746
+ }, [listImageAssets, onRequestImagePick]);
11747
+ async function requestImagePick() {
11748
+ if (!onRequestImagePick) {
11749
+ return;
11750
+ }
11751
+ const nextAsset = await onRequestImagePick({ blockId: block.id, current });
11752
+ if (nextAsset) {
11753
+ onSelectAsset?.(block.id, nextAsset);
11754
+ }
11755
+ }
11756
+ function selectListedAsset(assetId) {
11757
+ const nextAsset = assets.find((asset) => asset.assetId === assetId);
11758
+ if (nextAsset) {
11759
+ onSelectAsset?.(block.id, nextAsset);
11760
+ }
11761
+ }
11762
+ const imageFieldId = `butex-d2-img-${block.id}`;
11763
+ const selectId = `butex-d2-img-asset-${block.id}`;
11764
+ const canSelectListedAsset = !canUsePicker && assets.length > 0;
11765
+ function activateImagePicker() {
11766
+ if (canUsePicker) {
11767
+ void requestImagePick();
11768
+ return;
11769
+ }
11770
+ if (canSelectListedAsset && typeof document !== "undefined") {
11771
+ document.getElementById(selectId)?.focus();
11772
+ }
11773
+ }
11774
+ const defaultUi = /* @__PURE__ */ jsxs5("div", { className: "butex-document2-widget__image-editor", children: [
11775
+ /* @__PURE__ */ jsx7("div", { className: `butex-document2-widget__image-asset ${hasImage ? "butex-document2-widget__image-asset--filled" : "butex-document2-widget__image-asset--empty"}`, children: hasImage ? /* @__PURE__ */ jsxs5(Fragment, { children: [
11776
+ resolvedUrl ? /* @__PURE__ */ jsx7("img", { className: "butex-document2-widget__image-thumb", src: resolvedUrl, alt: messages.imagePreviewAlt }) : null,
11777
+ /* @__PURE__ */ jsxs5("div", { className: "butex-document2-widget__image-asset-main", children: [
11778
+ /* @__PURE__ */ jsx7("span", { className: "butex-document2-widget__image-asset-title", dir: "ltr", children: block.assetId ?? block.value }),
11779
+ /* @__PURE__ */ jsxs5("div", { className: "butex-document2-widget__image-actions", children: [
11780
+ /* @__PURE__ */ jsx7("button", { type: "button", onClick: activateImagePicker, disabled: !canUsePicker && !canSelectListedAsset, children: messages.changeImage }),
11781
+ /* @__PURE__ */ jsx7("button", { type: "button", className: "butex-document2-widget__button--danger", onClick: () => onClearAsset?.(block.id), children: messages.removeImage })
11782
+ ] })
11783
+ ] })
11784
+ ] }) : /* @__PURE__ */ jsxs5("div", { className: "butex-document2-widget__image-empty-content", children: [
11785
+ /* @__PURE__ */ jsx7("strong", { children: messages.noImageSelected }),
11786
+ /* @__PURE__ */ jsx7("button", { type: "button", onClick: activateImagePicker, disabled: !canUsePicker && !canSelectListedAsset, children: messages.chooseImage })
11787
+ ] }) }),
11788
+ !canUsePicker ? /* @__PURE__ */ jsxs5("label", { className: "butex-document2-widget__image-select", htmlFor: selectId, children: [
11789
+ /* @__PURE__ */ jsx7("span", { children: messages.selectImageAsset }),
11790
+ /* @__PURE__ */ jsxs5(
11791
+ "select",
11792
+ {
11793
+ id: selectId,
11794
+ value: "",
11795
+ disabled: !canSelectListedAsset,
11796
+ onFocus: () => onBlockFocus?.(block.id),
11797
+ onChange: (event) => selectListedAsset(event.currentTarget.value),
11798
+ children: [
11799
+ /* @__PURE__ */ jsx7("option", { value: "", children: assetsLoading ? messages.loadingImageAssets : assetsLoaded && assets.length === 0 ? messages.noImageAssets : messages.selectImageAsset }),
11800
+ assets.map((asset) => /* @__PURE__ */ jsx7("option", { value: asset.assetId, children: asset.label ?? asset.value ?? asset.assetId }, asset.assetId))
11801
+ ]
11802
+ }
11803
+ )
11804
+ ] }) : null,
11805
+ /* @__PURE__ */ jsxs5(
11806
+ "details",
11807
+ {
11808
+ className: "butex-document2-widget__image-advanced",
11809
+ open: advancedOpen,
11810
+ onToggle: (event) => setAdvancedOpen(event.currentTarget.open),
11811
+ children: [
11812
+ /* @__PURE__ */ jsx7("summary", { children: messages.advancedImagePath }),
11813
+ /* @__PURE__ */ jsx7("label", { className: "butex-document2-widget__image-src-label", htmlFor: imageFieldId, children: messages.imagePath }),
11814
+ /* @__PURE__ */ jsx7(
11815
+ "textarea",
11816
+ {
11817
+ id: imageFieldId,
11818
+ className: "butex-document2-widget__inline-text",
11819
+ value: block.value,
11820
+ dir: "ltr",
11821
+ "aria-label": messages.imagePathLabel,
11822
+ rows: 1,
11823
+ ref: (element) => {
11824
+ if (element) {
11825
+ element.style.height = "auto";
11826
+ element.style.height = `${String(element.scrollHeight)}px`;
11827
+ }
11828
+ },
11829
+ onFocus: () => onBlockFocus?.(block.id),
11830
+ onChange: (event) => {
11831
+ onImageSrcChange?.(block.id, event.currentTarget.value);
11832
+ event.currentTarget.style.height = "auto";
11833
+ event.currentTarget.style.height = `${String(event.currentTarget.scrollHeight)}px`;
11834
+ }
11835
+ }
11836
+ )
11837
+ ]
11838
+ }
11839
+ )
11840
+ ] });
11841
+ if (renderImageBlockEditor) {
11842
+ return /* @__PURE__ */ jsx7(Fragment, { children: renderImageBlockEditor({
11843
+ blockId: block.id,
11844
+ assetId: block.assetId,
11845
+ value: block.value,
11846
+ resolvedUrl,
11847
+ uiLocale,
11848
+ onSelectAsset: (asset) => onSelectAsset?.(block.id, asset),
11849
+ onClearAsset: () => onClearAsset?.(block.id),
11850
+ defaultUi
11851
+ }) });
11852
+ }
11853
+ return /* @__PURE__ */ jsx7(Fragment, { children: defaultUi });
11854
+ }
11591
11855
  function blockSummary(block, referenceCount, messages) {
11592
11856
  if (block.kind === "textBlock") {
11593
11857
  return inlineFieldSummary(block.field);
@@ -11636,6 +11900,12 @@ function BlockEditor({
11636
11900
  onRefFocus,
11637
11901
  onFieldBlur,
11638
11902
  onImageSrcChange,
11903
+ onImageAssetChange,
11904
+ onImageAssetClear,
11905
+ resolveImageUrl,
11906
+ onRequestImagePick,
11907
+ listImageAssets,
11908
+ renderImageBlockEditor,
11639
11909
  onFloatMetaChange,
11640
11910
  onParagraphCenteredChange,
11641
11911
  onAddListItem,
@@ -11778,6 +12048,12 @@ function BlockEditor({
11778
12048
  onCiteFocus,
11779
12049
  onFieldBlur,
11780
12050
  onImageSrcChange,
12051
+ onImageAssetChange,
12052
+ onImageAssetClear,
12053
+ resolveImageUrl,
12054
+ onRequestImagePick,
12055
+ listImageAssets,
12056
+ renderImageBlockEditor,
11781
12057
  onAddListItem,
11782
12058
  onRemoveListItem,
11783
12059
  onManageReferences
@@ -11816,31 +12092,22 @@ function BlockEditor({
11816
12092
  ] });
11817
12093
  }
11818
12094
  if (block.kind === "image") {
11819
- const imageFieldId = `butex-d2-img-${block.id}`;
11820
12095
  return /* @__PURE__ */ jsxs5("section", { className: chromeClass, children: [
11821
12096
  header,
11822
- /* @__PURE__ */ jsx7("label", { className: "butex-document2-widget__image-src-label", htmlFor: imageFieldId, children: messages.imagePath }),
11823
12097
  /* @__PURE__ */ jsx7(
11824
- "textarea",
12098
+ ImageAssetEditor,
11825
12099
  {
11826
- id: imageFieldId,
11827
- className: "butex-document2-widget__inline-text",
11828
- value: block.value,
11829
- dir: "ltr",
11830
- "aria-label": messages.imagePathLabel,
11831
- rows: 1,
11832
- ref: (element) => {
11833
- if (element) {
11834
- element.style.height = "auto";
11835
- element.style.height = `${String(element.scrollHeight)}px`;
11836
- }
11837
- },
11838
- onFocus: () => onBlockFocus?.(block.id),
11839
- onChange: (event) => {
11840
- onImageSrcChange?.(block.id, event.currentTarget.value);
11841
- event.currentTarget.style.height = "auto";
11842
- event.currentTarget.style.height = `${String(event.currentTarget.scrollHeight)}px`;
11843
- }
12100
+ block,
12101
+ uiLocale,
12102
+ messages,
12103
+ resolveImageUrl,
12104
+ onRequestImagePick,
12105
+ listImageAssets,
12106
+ renderImageBlockEditor,
12107
+ onSelectAsset: onImageAssetChange,
12108
+ onClearAsset: onImageAssetClear,
12109
+ onImageSrcChange,
12110
+ onBlockFocus
11844
12111
  }
11845
12112
  ),
11846
12113
  /* @__PURE__ */ jsx7(
@@ -11885,7 +12152,7 @@ function BlockEditor({
11885
12152
  }
11886
12153
 
11887
12154
  // src/react-document2/CitePickerPopover.tsx
11888
- import { useEffect as useEffect3, useState as useState4 } from "react";
12155
+ import { useEffect as useEffect4, useState as useState4 } from "react";
11889
12156
  import { jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
11890
12157
  function CitePickerPopover({
11891
12158
  open,
@@ -11900,7 +12167,7 @@ function CitePickerPopover({
11900
12167
  const messages = document2Messages(uiLocale);
11901
12168
  const documentDirection = uiLocale === "ar" ? "rtl" : "ltr";
11902
12169
  const [selected, setSelected] = useState4(initialKeys);
11903
- useEffect3(() => {
12170
+ useEffect4(() => {
11904
12171
  if (open) {
11905
12172
  setSelected(initialKeys);
11906
12173
  }
@@ -11960,10 +12227,10 @@ function CitePickerPopover({
11960
12227
  }
11961
12228
 
11962
12229
  // src/react-document2/DocumentInsertToolbar.tsx
11963
- import { useEffect as useEffect5, useRef as useRef5, useState as useState6 } from "react";
12230
+ import { useEffect as useEffect6, useRef as useRef5, useState as useState6 } from "react";
11964
12231
 
11965
12232
  // src/react-document2/TableInsertPopover.tsx
11966
- import { useEffect as useEffect4, useRef as useRef4, useState as useState5 } from "react";
12233
+ import { useEffect as useEffect5, useRef as useRef4, useState as useState5 } from "react";
11967
12234
  import { jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
11968
12235
  function TableInsertPopover({ uiLocale = "ar", onConfirm }) {
11969
12236
  const messages = document2Messages(uiLocale);
@@ -11971,7 +12238,7 @@ function TableInsertPopover({ uiLocale = "ar", onConfirm }) {
11971
12238
  const [rows, setRows] = useState5("3");
11972
12239
  const [cols, setCols] = useState5("3");
11973
12240
  const rootRef = useRef4(null);
11974
- useEffect4(() => {
12241
+ useEffect5(() => {
11975
12242
  if (!open) {
11976
12243
  return;
11977
12244
  }
@@ -12069,7 +12336,7 @@ function DocumentInsertToolbar({
12069
12336
  const [referencesMenuOpen, setReferencesMenuOpen] = useState6(false);
12070
12337
  const referencesMenuRef = useRef5(null);
12071
12338
  const referencesTriggerRef = useRef5(null);
12072
- useEffect5(() => {
12339
+ useEffect6(() => {
12073
12340
  if (!referencesMenuOpen) {
12074
12341
  return;
12075
12342
  }
@@ -12329,9 +12596,9 @@ function DocumentInsertToolbar({
12329
12596
  }
12330
12597
 
12331
12598
  // src/react-document2/DocumentPreview.tsx
12332
- import { Fragment, jsx as jsx11, jsxs as jsxs9 } from "react/jsx-runtime";
12599
+ import { Fragment as Fragment2, jsx as jsx11, jsxs as jsxs9 } from "react/jsx-runtime";
12333
12600
  function PreviewInlines({ inlines, output }) {
12334
- return /* @__PURE__ */ jsx11(Fragment, { children: inlines.map((inline, index) => {
12601
+ return /* @__PURE__ */ jsx11(Fragment2, { children: inlines.map((inline, index) => {
12335
12602
  if (inline.kind === "text") {
12336
12603
  let content = /* @__PURE__ */ jsx11("span", { children: inline.text });
12337
12604
  if (inline.style?.bold) {
@@ -12454,7 +12721,7 @@ function PreviewBlock({
12454
12721
  " ",
12455
12722
  /* @__PURE__ */ jsxs9("span", { children: [
12456
12723
  bibliographyEntryBody(item, item.fieldSeparator, { digitForm }),
12457
- item.url ? /* @__PURE__ */ jsxs9(Fragment, { children: [
12724
+ item.url ? /* @__PURE__ */ jsxs9(Fragment2, { children: [
12458
12725
  " ",
12459
12726
  /* @__PURE__ */ jsx11("a", { href: absoluteHttpHref(item.url), target: "_blank", rel: "noreferrer", children: item.url })
12460
12727
  ] }) : null
@@ -12492,13 +12759,13 @@ function DocumentPreview({
12492
12759
  }
12493
12760
 
12494
12761
  // src/react-document2/EquationDrawer.tsx
12495
- import { useEffect as useEffect7, useRef as useRef7, useState as useState8 } from "react";
12762
+ import { useEffect as useEffect8, useRef as useRef7, useState as useState8 } from "react";
12496
12763
 
12497
12764
  // src/react/ButexEditor.tsx
12498
12765
  import {
12499
12766
  forwardRef,
12500
12767
  useCallback,
12501
- useEffect as useEffect6,
12768
+ useEffect as useEffect7,
12502
12769
  useImperativeHandle,
12503
12770
  useRef as useRef6,
12504
12771
  useState as useState7
@@ -13504,7 +13771,7 @@ function injectWidgetChromeCss(doc) {
13504
13771
  }
13505
13772
 
13506
13773
  // src/react/ButexEditor.tsx
13507
- import { Fragment as Fragment2, jsx as jsx12, jsxs as jsxs10 } from "react/jsx-runtime";
13774
+ import { Fragment as Fragment3, jsx as jsx12, jsxs as jsxs10 } from "react/jsx-runtime";
13508
13775
  var DIGIT_FORM_OPTIONS = [
13509
13776
  { id: "western", label: "456" },
13510
13777
  { id: "arabicIndic", label: "\u0664\u0665\u0666" },
@@ -13775,7 +14042,7 @@ ${arabic || currentMessages.empty}`;
13775
14042
  setDebugImportError(importError instanceof Error ? importError.message : String(importError));
13776
14043
  }
13777
14044
  }
13778
- useEffect6(() => {
14045
+ useEffect7(() => {
13779
14046
  injectWidgetChromeCss();
13780
14047
  injectBuTeXEditorStyles(typeof document !== "undefined" ? document : void 0);
13781
14048
  const surfaceEl = surfaceRef.current;
@@ -13832,7 +14099,7 @@ ${arabic || currentMessages.empty}`;
13832
14099
  runtimeRef.current = null;
13833
14100
  };
13834
14101
  }, []);
13835
- useEffect6(() => {
14102
+ useEffect7(() => {
13836
14103
  runtimeRef.current?.setUiLocale(uiLocale);
13837
14104
  const session = runtimeRef.current?.getSession();
13838
14105
  if (session) {
@@ -14198,7 +14465,7 @@ ${arabic || currentMessages.empty}`;
14198
14465
  ] }),
14199
14466
  /* @__PURE__ */ jsxs10("div", { className: "matrix-edit-actions", "aria-label": messages.editSelectedEnvironment, children: [
14200
14467
  envPaletteMode === "matrix" ? /* @__PURE__ */ jsx12("button", { type: "button", title: messages.applySelectedMatrixStyle, onMouseDown: (event) => event.preventDefault(), onClick: () => runtimeRef.current?.setMatrixEnvStyle(matrixStyle), children: messages.applyStyle }) : null,
14201
- envPaletteMode === "array" ? /* @__PURE__ */ jsxs10(Fragment2, { children: [
14468
+ envPaletteMode === "array" ? /* @__PURE__ */ jsxs10(Fragment3, { children: [
14202
14469
  /* @__PURE__ */ jsxs10("label", { children: [
14203
14470
  messages.column,
14204
14471
  /* @__PURE__ */ jsx12(
@@ -15062,10 +15329,10 @@ function EquationDrawer({
15062
15329
  labels,
15063
15330
  excludeOwnerId: ownerId || void 0
15064
15331
  }) : null;
15065
- useEffect7(() => {
15332
+ useEffect8(() => {
15066
15333
  setLabelOverride(null);
15067
15334
  }, [ownerId, label]);
15068
- useEffect7(() => {
15335
+ useEffect8(() => {
15069
15336
  const onKeyDown = (event) => {
15070
15337
  if (event.key === "Escape") {
15071
15338
  onClose();
@@ -15312,7 +15579,7 @@ function LabelsPanel({
15312
15579
 
15313
15580
  // src/react-document2/ReferencesPanel.tsx
15314
15581
  import { useMemo, useState as useState10 } from "react";
15315
- import { Fragment as Fragment3, jsx as jsx15, jsxs as jsxs13 } from "react/jsx-runtime";
15582
+ import { Fragment as Fragment4, jsx as jsx15, jsxs as jsxs13 } from "react/jsx-runtime";
15316
15583
  var EMPTY_DRAFT = {
15317
15584
  key: "",
15318
15585
  authors: "",
@@ -15403,7 +15670,7 @@ function ReferencePreview({
15403
15670
  "] "
15404
15671
  ] }) : null,
15405
15672
  body.length > 0 ? body : /* @__PURE__ */ jsx15("span", { className: "butex-document2-widget__cite-picker-empty", children: "\u2026" }),
15406
- url.trim().length > 0 ? /* @__PURE__ */ jsxs13(Fragment3, { children: [
15673
+ url.trim().length > 0 ? /* @__PURE__ */ jsxs13(Fragment4, { children: [
15407
15674
  " ",
15408
15675
  /* @__PURE__ */ jsx15("span", { className: "butex-document2-widget__modal-preview-url", children: url.trim() })
15409
15676
  ] }) : null
@@ -15607,7 +15874,7 @@ function ReferencesPanel({
15607
15874
  }
15608
15875
 
15609
15876
  // src/react-document2/RefPickerPopover.tsx
15610
- import { useEffect as useEffect8, useState as useState11 } from "react";
15877
+ import { useEffect as useEffect9, useState as useState11 } from "react";
15611
15878
  import { jsx as jsx16, jsxs as jsxs14 } from "react/jsx-runtime";
15612
15879
  function RefPickerPopover({
15613
15880
  open,
@@ -15624,7 +15891,7 @@ function RefPickerPopover({
15624
15891
  const documentDirection = uiLocale === "ar" ? "rtl" : "ltr";
15625
15892
  const [selected, setSelected] = useState11(initialKeys);
15626
15893
  const [refCommand, setRefCommand] = useState11(initialRefCommand);
15627
- useEffect8(() => {
15894
+ useEffect9(() => {
15628
15895
  if (open) {
15629
15896
  setSelected(initialKeys);
15630
15897
  setRefCommand(initialRefCommand);
@@ -16722,6 +16989,94 @@ var DOCUMENT2_WIDGET_CSS = `
16722
16989
  border-color: var(--butex-document2-dev-border);
16723
16990
  }
16724
16991
 
16992
+ .butex-document2-widget__image-editor {
16993
+ display: grid;
16994
+ gap: 10px;
16995
+ min-width: 0;
16996
+ }
16997
+
16998
+ .butex-document2-widget__image-asset {
16999
+ align-items: center;
17000
+ border: 1px solid var(--butex-document2-border);
17001
+ border-radius: 8px;
17002
+ display: flex;
17003
+ gap: 12px;
17004
+ min-height: 128px;
17005
+ min-width: 0;
17006
+ padding: 12px;
17007
+ }
17008
+
17009
+ .butex-document2-widget__image-asset--empty {
17010
+ border-style: dashed;
17011
+ justify-content: center;
17012
+ }
17013
+
17014
+ .butex-document2-widget__image-empty-content,
17015
+ .butex-document2-widget__image-asset-main,
17016
+ .butex-document2-widget__image-select,
17017
+ .butex-document2-widget__image-advanced {
17018
+ display: grid;
17019
+ gap: 8px;
17020
+ min-width: 0;
17021
+ }
17022
+
17023
+ .butex-document2-widget__image-empty-content {
17024
+ justify-items: center;
17025
+ text-align: center;
17026
+ }
17027
+
17028
+ .butex-document2-widget__image-thumb {
17029
+ aspect-ratio: 4 / 3;
17030
+ background: var(--butex-document2-bg);
17031
+ border: 1px solid var(--butex-document2-border);
17032
+ border-radius: 8px;
17033
+ flex: 0 0 128px;
17034
+ max-width: 38%;
17035
+ object-fit: cover;
17036
+ width: 128px;
17037
+ }
17038
+
17039
+ .butex-document2-widget__image-asset-title {
17040
+ color: var(--butex-document2-muted);
17041
+ display: block;
17042
+ overflow: hidden;
17043
+ text-align: start;
17044
+ text-overflow: ellipsis;
17045
+ white-space: nowrap;
17046
+ }
17047
+
17048
+ .butex-document2-widget__image-actions {
17049
+ display: flex;
17050
+ flex-wrap: wrap;
17051
+ gap: 8px;
17052
+ }
17053
+
17054
+ .butex-document2-widget__image-select span,
17055
+ .butex-document2-widget__image-src-label {
17056
+ color: var(--butex-document2-muted);
17057
+ font-size: 0.86rem;
17058
+ }
17059
+
17060
+ .butex-document2-widget__image-select select {
17061
+ background: var(--butex-document2-input-bg);
17062
+ border: 1px solid var(--butex-document2-border);
17063
+ border-radius: 8px;
17064
+ color: var(--butex-document2-fg);
17065
+ min-height: 36px;
17066
+ padding: 6px 8px;
17067
+ }
17068
+
17069
+ .butex-document2-widget__image-select select:disabled {
17070
+ cursor: not-allowed;
17071
+ opacity: 0.55;
17072
+ }
17073
+
17074
+ .butex-document2-widget__image-advanced summary {
17075
+ color: var(--butex-document2-muted);
17076
+ cursor: pointer;
17077
+ font-size: 0.86rem;
17078
+ }
17079
+
16725
17080
  .butex-document2-widget__dev pre {
16726
17081
  background: var(--butex-document2-dev-code-bg);
16727
17082
  color: var(--butex-document2-dev-code-fg);
@@ -17346,7 +17701,7 @@ function injectBuTeXDocument2Styles(doc) {
17346
17701
  }
17347
17702
 
17348
17703
  // src/react-document2/ButexDocumentEditor2.tsx
17349
- import { Fragment as Fragment4, jsx as jsx17, jsxs as jsxs15 } from "react/jsx-runtime";
17704
+ import { Fragment as Fragment5, jsx as jsx17, jsxs as jsxs15 } from "react/jsx-runtime";
17350
17705
  function mathDelimiters(mode) {
17351
17706
  return mode === "inline" ? { opening: "$", closing: "$" } : { opening: "\\[", closing: "\\]" };
17352
17707
  }
@@ -17600,951 +17955,995 @@ function resolveInitialDocument2(initialDocument, uiLocale = "ar", documentMeta)
17600
17955
  };
17601
17956
  }
17602
17957
  }
17603
- function ButexDocumentEditor2({
17604
- initialDocument,
17605
- documentMeta,
17606
- className,
17607
- debug = false,
17608
- documentDirection = "rtl",
17609
- equationSide = "arabic",
17610
- editableEquations = true,
17611
- previewOnly = false,
17612
- uiLocale = "ar",
17613
- mathOutput = "svg",
17614
- digitForm: digitFormProp,
17615
- onDigitFormChange,
17616
- resolveImageUrl,
17617
- onDocumentChange,
17618
- onDocumentJsonChange,
17619
- onLatexChange
17620
- }) {
17621
- const messages = document2Messages(uiLocale);
17622
- const initialState = useMemo2(
17623
- () => resolveInitialDocument2(initialDocument, uiLocale, documentMeta),
17624
- [initialDocument, documentMeta, uiLocale]
17625
- );
17626
- const [documentNode, setDocumentNode] = useState12(initialState.document);
17627
- const [error, setError] = useState12(initialState.error);
17628
- const [editorOpen, setEditorOpen] = useState12(true);
17629
- const [previewOpen, setPreviewOpen] = useState12(true);
17630
- const [selectedMath, setSelectedMath] = useState12(null);
17631
- const [citePicker, setCitePicker] = useState12(null);
17632
- const [referencesOpen, setReferencesOpen] = useState12(false);
17633
- const [labelsOpen, setLabelsOpen] = useState12(false);
17634
- const [refPicker, setRefPicker] = useState12(null);
17635
- const [digitFormState, setDigitFormState] = useState12(null);
17636
- const [debugImportOpen, setDebugImportOpen] = useState12(false);
17637
- const [debugImportText, setDebugImportText] = useState12("");
17638
- const [debugImportError, setDebugImportError] = useState12("");
17639
- const [editorFocus, setEditorFocus] = useState12(createEmptyDocument2EditorFocus());
17640
- const [collapsedBlockIds, setCollapsedBlockIds] = useState12(() => /* @__PURE__ */ new Set());
17641
- const [blockSelectionState, setBlockSelectionState] = useState12(emptyBlockSelection);
17642
- const [historyTick, setHistoryTick] = useState12(0);
17643
- const digitForm = digitFormProp ?? digitFormState ?? (documentDirection === "rtl" ? "arabicIndic" : "western");
17644
- function equationSessionWithDocumentDigits(session) {
17645
- return setCurrentDigitForm(session, digitForm);
17646
- }
17647
- const historyRef = useRef8(createDocument2History());
17648
- const documentRef = useRef8(documentNode);
17649
- const textSnapshotArmedRef = useRef8(false);
17650
- const textDebounceRef = useRef8(null);
17651
- const widgetRef = useRef8(null);
17652
- const articleMetaPanelRef = useRef8(null);
17653
- const pendingFocusRef = useRef8(null);
17654
- documentRef.current = documentNode;
17655
- const latex = document2Latex(documentNode, { digitForm });
17656
- const documentJson = useMemo2(() => toDocumentJson2(documentNode), [documentNode]);
17657
- const documentLabels = useMemo2(() => collectDocument2Labels(documentNode), [documentNode]);
17658
- const preview = useMemo2(
17659
- () => document2Preview(documentNode, mathOutput, equationSide, {
17660
- documentDirection,
17661
- digitForm,
17662
- uiLocale
17663
- }),
17664
- [documentNode, documentDirection, digitForm, equationSide, mathOutput, uiLocale]
17665
- );
17666
- const debugEnabled = useMemo2(() => {
17667
- if (debug) {
17668
- return true;
17669
- }
17670
- if (typeof window === "undefined") {
17671
- return false;
17672
- }
17673
- return new URLSearchParams(window.location.search).get("debug") === "1";
17674
- }, [debug]);
17675
- const canUndo = useMemo2(() => document2HistoryCanUndo(historyRef.current), [historyTick, documentNode]);
17676
- const canRedo = useMemo2(() => document2HistoryCanRedo(historyRef.current), [historyTick, documentNode]);
17677
- const blockSelection = normalizeBlockSelection(blockSelectionState.selection, documentNode.blocks.length);
17678
- const selectionCount = blockSelection ? blockSelection.to - blockSelection.from + 1 : 0;
17679
- const canMoveSelectionUp = Boolean(blockSelection && blockSelection.from > 0);
17680
- const canMoveSelectionDown = Boolean(blockSelection && blockSelection.to < documentNode.blocks.length - 1);
17681
- const formatTextToken = useMemo2(() => findFormatTextToken(documentNode, editorFocus), [documentNode, editorFocus]);
17682
- const canFormatText = formatTextToken !== null;
17683
- const activeTextStyles = formatTextToken?.style ?? {};
17684
- function clearBlockSelection() {
17685
- setBlockSelectionState(emptyBlockSelection());
17686
- }
17687
- function toggleSelectBlock(blockId, shiftKey) {
17688
- const index = documentRef.current.blocks.findIndex((block) => block.id === blockId);
17689
- if (index < 0) {
17690
- return;
17691
- }
17692
- const blockCount = documentRef.current.blocks.length;
17693
- setBlockSelectionState(
17694
- (current) => shiftKey ? extendBlockSelectionFromAnchor(current, index, blockCount) : toggleBlockInSelection(current, index, blockCount)
17958
+ var ButexDocumentEditor2 = forwardRef2(
17959
+ function ButexDocumentEditor22({
17960
+ initialDocument,
17961
+ documentMeta,
17962
+ className,
17963
+ debug = false,
17964
+ documentDirection = "rtl",
17965
+ equationSide = "arabic",
17966
+ editableEquations = true,
17967
+ previewOnly = false,
17968
+ uiLocale = "ar",
17969
+ mathOutput = "svg",
17970
+ digitForm: digitFormProp,
17971
+ onDigitFormChange,
17972
+ resolveImageUrl,
17973
+ onRequestImagePick,
17974
+ listImageAssets,
17975
+ renderImageBlockEditor,
17976
+ onDocumentChange,
17977
+ onDocumentJsonChange,
17978
+ onLatexChange
17979
+ }, ref) {
17980
+ const messages = document2Messages(uiLocale);
17981
+ const initialState = useMemo2(
17982
+ () => resolveInitialDocument2(initialDocument, uiLocale, documentMeta),
17983
+ [initialDocument, documentMeta, uiLocale]
17695
17984
  );
17696
- }
17697
- function moveSelectedBlocks(direction) {
17698
- const selection = normalizeBlockSelection(blockSelectionState.selection, documentRef.current.blocks.length);
17699
- if (!selection) {
17700
- return;
17701
- }
17702
- const next = moveDocument2BlockRange(documentRef.current, selection.from, selection.to, direction);
17703
- if (next === documentRef.current) {
17704
- return;
17705
- }
17706
- applyDocument(next, "immediate");
17707
- const remapped = remapSelectionAfterRangeMove(selection, direction);
17708
- setBlockSelectionState({
17709
- selection: normalizeBlockSelection(remapped, next.blocks.length),
17710
- anchor: blockSelectionState.anchor === null ? null : blockSelectionState.anchor + direction
17711
- });
17712
- }
17713
- function deleteSelectedBlocks() {
17714
- const selection = normalizeBlockSelection(blockSelectionState.selection, documentRef.current.blocks.length);
17715
- if (!selection) {
17716
- return;
17717
- }
17718
- applyDocument(removeDocument2BlockRange(documentRef.current, selection.from, selection.to), "immediate");
17719
- clearBlockSelection();
17720
- }
17721
- useEffect9(() => {
17722
- injectBuTeXDocument2Styles();
17723
- }, []);
17724
- useEffect9(() => {
17725
- if (!editableEquations || previewOnly) {
17726
- setSelectedMath(null);
17727
- }
17728
- }, [editableEquations, previewOnly]);
17729
- useEffect9(() => {
17730
- const next = resolveInitialDocument2(initialDocument, uiLocale, documentMeta);
17731
- setDocumentNode(next.document);
17732
- setError(next.error);
17733
- setSelectedMath(null);
17734
- setCollapsedBlockIds(/* @__PURE__ */ new Set());
17735
- historyRef.current = createDocument2History();
17736
- setHistoryTick((tick) => tick + 1);
17737
- }, [initialDocument, documentMeta, uiLocale]);
17738
- useEffect9(() => {
17739
- onDocumentChange?.(documentNode);
17740
- }, [documentNode, onDocumentChange]);
17741
- useEffect9(() => {
17742
- onDocumentJsonChange?.(documentJson);
17743
- }, [documentJson, onDocumentJsonChange]);
17744
- useEffect9(() => {
17745
- onLatexChange?.(latex);
17746
- }, [latex, onLatexChange]);
17747
- useEffect9(() => {
17748
- const pending = pendingFocusRef.current;
17749
- const root = widgetRef.current;
17750
- if (!pending || !root) {
17751
- return;
17985
+ const [documentNode, setDocumentNode] = useState12(initialState.document);
17986
+ const [error, setError] = useState12(initialState.error);
17987
+ const [editorOpen, setEditorOpen] = useState12(true);
17988
+ const [previewOpen, setPreviewOpen] = useState12(true);
17989
+ const [selectedMath, setSelectedMath] = useState12(null);
17990
+ const [citePicker, setCitePicker] = useState12(null);
17991
+ const [referencesOpen, setReferencesOpen] = useState12(false);
17992
+ const [labelsOpen, setLabelsOpen] = useState12(false);
17993
+ const [refPicker, setRefPicker] = useState12(null);
17994
+ const [digitFormState, setDigitFormState] = useState12(null);
17995
+ const [debugImportOpen, setDebugImportOpen] = useState12(false);
17996
+ const [debugImportText, setDebugImportText] = useState12("");
17997
+ const [debugImportError, setDebugImportError] = useState12("");
17998
+ const [editorFocus, setEditorFocus] = useState12(createEmptyDocument2EditorFocus());
17999
+ const [collapsedBlockIds, setCollapsedBlockIds] = useState12(() => /* @__PURE__ */ new Set());
18000
+ const [blockSelectionState, setBlockSelectionState] = useState12(emptyBlockSelection);
18001
+ const [historyTick, setHistoryTick] = useState12(0);
18002
+ const digitForm = digitFormProp ?? digitFormState ?? (documentDirection === "rtl" ? "arabicIndic" : "western");
18003
+ function equationSessionWithDocumentDigits(session) {
18004
+ return setCurrentDigitForm(session, digitForm);
18005
+ }
18006
+ const historyRef = useRef8(createDocument2History());
18007
+ const documentRef = useRef8(documentNode);
18008
+ const editorFocusRef = useRef8(editorFocus);
18009
+ const textSnapshotArmedRef = useRef8(false);
18010
+ const textDebounceRef = useRef8(null);
18011
+ const widgetRef = useRef8(null);
18012
+ const articleMetaPanelRef = useRef8(null);
18013
+ const pendingFocusRef = useRef8(null);
18014
+ documentRef.current = documentNode;
18015
+ editorFocusRef.current = editorFocus;
18016
+ const latex = document2Latex(documentNode, { digitForm });
18017
+ const documentJson = useMemo2(() => toDocumentJson2(documentNode), [documentNode]);
18018
+ const documentLabels = useMemo2(() => collectDocument2Labels(documentNode), [documentNode]);
18019
+ const preview = useMemo2(
18020
+ () => document2Preview(documentNode, mathOutput, equationSide, {
18021
+ documentDirection,
18022
+ digitForm,
18023
+ uiLocale
18024
+ }),
18025
+ [documentNode, documentDirection, digitForm, equationSide, mathOutput, uiLocale]
18026
+ );
18027
+ const debugEnabled = useMemo2(() => {
18028
+ if (debug) {
18029
+ return true;
18030
+ }
18031
+ if (typeof window === "undefined") {
18032
+ return false;
18033
+ }
18034
+ return new URLSearchParams(window.location.search).get("debug") === "1";
18035
+ }, [debug]);
18036
+ const canUndo = useMemo2(() => document2HistoryCanUndo(historyRef.current), [historyTick, documentNode]);
18037
+ const canRedo = useMemo2(() => document2HistoryCanRedo(historyRef.current), [historyTick, documentNode]);
18038
+ const blockSelection = normalizeBlockSelection(blockSelectionState.selection, documentNode.blocks.length);
18039
+ const selectionCount = blockSelection ? blockSelection.to - blockSelection.from + 1 : 0;
18040
+ const canMoveSelectionUp = Boolean(blockSelection && blockSelection.from > 0);
18041
+ const canMoveSelectionDown = Boolean(blockSelection && blockSelection.to < documentNode.blocks.length - 1);
18042
+ const formatTextToken = useMemo2(() => findFormatTextToken(documentNode, editorFocus), [documentNode, editorFocus]);
18043
+ const canFormatText = formatTextToken !== null;
18044
+ const activeTextStyles = formatTextToken?.style ?? {};
18045
+ function clearBlockSelection() {
18046
+ setBlockSelectionState(emptyBlockSelection());
18047
+ }
18048
+ function toggleSelectBlock(blockId, shiftKey) {
18049
+ const index = documentRef.current.blocks.findIndex((block) => block.id === blockId);
18050
+ if (index < 0) {
18051
+ return;
18052
+ }
18053
+ const blockCount = documentRef.current.blocks.length;
18054
+ setBlockSelectionState(
18055
+ (current) => shiftKey ? extendBlockSelectionFromAnchor(current, index, blockCount) : toggleBlockInSelection(current, index, blockCount)
18056
+ );
17752
18057
  }
17753
- pendingFocusRef.current = null;
17754
- requestAnimationFrame(() => {
17755
- if (pending.kind === "math") {
17756
- const mathButton = Array.from(root.querySelectorAll("[data-math-token-id]")).find(
17757
- (button) => button.dataset.mathTokenId === pending.tokenId
17758
- );
17759
- mathButton?.focus();
18058
+ function moveSelectedBlocks(direction) {
18059
+ const selection = normalizeBlockSelection(blockSelectionState.selection, documentRef.current.blocks.length);
18060
+ if (!selection) {
17760
18061
  return;
17761
18062
  }
17762
- if (pending.kind === "cite") {
17763
- const citeButton = Array.from(root.querySelectorAll("[data-cite-token-id]")).find(
17764
- (button) => button.dataset.citeTokenId === pending.tokenId
17765
- );
17766
- citeButton?.focus();
18063
+ const next = moveDocument2BlockRange(documentRef.current, selection.from, selection.to, direction);
18064
+ if (next === documentRef.current) {
17767
18065
  return;
17768
18066
  }
17769
- if (pending.kind === "ref") {
17770
- const refButton = Array.from(root.querySelectorAll("[data-ref-token-id]")).find(
17771
- (button) => button.dataset.refTokenId === pending.tokenId
17772
- );
17773
- refButton?.focus();
18067
+ applyDocument(next, "immediate");
18068
+ const remapped = remapSelectionAfterRangeMove(selection, direction);
18069
+ setBlockSelectionState({
18070
+ selection: normalizeBlockSelection(remapped, next.blocks.length),
18071
+ anchor: blockSelectionState.anchor === null ? null : blockSelectionState.anchor + direction
18072
+ });
18073
+ }
18074
+ function deleteSelectedBlocks() {
18075
+ const selection = normalizeBlockSelection(blockSelectionState.selection, documentRef.current.blocks.length);
18076
+ if (!selection) {
17774
18077
  return;
17775
18078
  }
17776
- const textarea = Array.from(root.querySelectorAll("textarea[data-field-id][data-text-token-id]")).find(
17777
- (element) => element.dataset.fieldId === pending.fieldId && element.dataset.textTokenId === pending.textTokenId
17778
- );
17779
- if (!textarea) {
18079
+ applyDocument(removeDocument2BlockRange(documentRef.current, selection.from, selection.to), "immediate");
18080
+ clearBlockSelection();
18081
+ }
18082
+ useEffect10(() => {
18083
+ injectBuTeXDocument2Styles();
18084
+ }, []);
18085
+ useEffect10(() => {
18086
+ if (!editableEquations || previewOnly) {
18087
+ setSelectedMath(null);
18088
+ }
18089
+ }, [editableEquations, previewOnly]);
18090
+ useEffect10(() => {
18091
+ const next = resolveInitialDocument2(initialDocument, uiLocale, documentMeta);
18092
+ setDocumentNode(next.document);
18093
+ setError(next.error);
18094
+ setSelectedMath(null);
18095
+ setCollapsedBlockIds(/* @__PURE__ */ new Set());
18096
+ historyRef.current = createDocument2History();
18097
+ setHistoryTick((tick) => tick + 1);
18098
+ }, [initialDocument, documentMeta, uiLocale]);
18099
+ useEffect10(() => {
18100
+ onDocumentChange?.(documentNode);
18101
+ }, [documentNode, onDocumentChange]);
18102
+ useEffect10(() => {
18103
+ onDocumentJsonChange?.(documentJson);
18104
+ }, [documentJson, onDocumentJsonChange]);
18105
+ useEffect10(() => {
18106
+ onLatexChange?.(latex);
18107
+ }, [latex, onLatexChange]);
18108
+ useEffect10(() => {
18109
+ const pending = pendingFocusRef.current;
18110
+ const root = widgetRef.current;
18111
+ if (!pending || !root) {
17780
18112
  return;
17781
18113
  }
17782
- const safeOffset = Math.max(0, Math.min(pending.caretOffset, textarea.value.length));
17783
- textarea.focus();
17784
- textarea.setSelectionRange(safeOffset, safeOffset);
17785
- });
17786
- }, [documentNode]);
17787
- const bumpHistoryUi = useCallback2(() => {
17788
- setHistoryTick((tick) => tick + 1);
17789
- }, []);
17790
- const applyDocument = useCallback2(
17791
- (next, mode = "immediate") => {
17792
- if (mode === "immediate") {
17793
- pushDocument2Snapshot(historyRef.current, documentRef.current);
17794
- bumpHistoryUi();
17795
- } else if (mode === "text") {
17796
- if (!textSnapshotArmedRef.current) {
18114
+ pendingFocusRef.current = null;
18115
+ requestAnimationFrame(() => {
18116
+ if (pending.kind === "math") {
18117
+ const mathButton = Array.from(root.querySelectorAll("[data-math-token-id]")).find(
18118
+ (button) => button.dataset.mathTokenId === pending.tokenId
18119
+ );
18120
+ mathButton?.focus();
18121
+ return;
18122
+ }
18123
+ if (pending.kind === "cite") {
18124
+ const citeButton = Array.from(root.querySelectorAll("[data-cite-token-id]")).find(
18125
+ (button) => button.dataset.citeTokenId === pending.tokenId
18126
+ );
18127
+ citeButton?.focus();
18128
+ return;
18129
+ }
18130
+ if (pending.kind === "ref") {
18131
+ const refButton = Array.from(root.querySelectorAll("[data-ref-token-id]")).find(
18132
+ (button) => button.dataset.refTokenId === pending.tokenId
18133
+ );
18134
+ refButton?.focus();
18135
+ return;
18136
+ }
18137
+ const textarea = Array.from(root.querySelectorAll("textarea[data-field-id][data-text-token-id]")).find(
18138
+ (element) => element.dataset.fieldId === pending.fieldId && element.dataset.textTokenId === pending.textTokenId
18139
+ );
18140
+ if (!textarea) {
18141
+ return;
18142
+ }
18143
+ const safeOffset = Math.max(0, Math.min(pending.caretOffset, textarea.value.length));
18144
+ textarea.focus();
18145
+ textarea.setSelectionRange(safeOffset, safeOffset);
18146
+ });
18147
+ }, [documentNode]);
18148
+ const bumpHistoryUi = useCallback2(() => {
18149
+ setHistoryTick((tick) => tick + 1);
18150
+ }, []);
18151
+ const applyDocument = useCallback2(
18152
+ (next, mode = "immediate") => {
18153
+ if (mode === "immediate") {
17797
18154
  pushDocument2Snapshot(historyRef.current, documentRef.current);
17798
- textSnapshotArmedRef.current = true;
17799
18155
  bumpHistoryUi();
18156
+ } else if (mode === "text") {
18157
+ if (!textSnapshotArmedRef.current) {
18158
+ pushDocument2Snapshot(historyRef.current, documentRef.current);
18159
+ textSnapshotArmedRef.current = true;
18160
+ bumpHistoryUi();
18161
+ }
18162
+ if (textDebounceRef.current !== null) {
18163
+ window.clearTimeout(textDebounceRef.current);
18164
+ }
18165
+ textDebounceRef.current = window.setTimeout(() => {
18166
+ textSnapshotArmedRef.current = false;
18167
+ }, 400);
17800
18168
  }
17801
- if (textDebounceRef.current !== null) {
17802
- window.clearTimeout(textDebounceRef.current);
18169
+ documentRef.current = next;
18170
+ setDocumentNode(next);
18171
+ },
18172
+ [bumpHistoryUi]
18173
+ );
18174
+ const afterBlockId = useCallback2(() => resolveInsertAfterBlockId(documentRef.current, editorFocusRef.current), []);
18175
+ const insertImageBlock = useCallback2(
18176
+ (srcOrAsset = "") => {
18177
+ clearBlockSelection();
18178
+ const after = resolveInsertAfterBlockId(documentRef.current, editorFocusRef.current);
18179
+ applyDocument(addDocument2ImageBlock(documentRef.current, srcOrAsset, after), "immediate");
18180
+ },
18181
+ [applyDocument]
18182
+ );
18183
+ useImperativeHandle2(
18184
+ ref,
18185
+ () => ({
18186
+ insertImageBlock,
18187
+ updateImageBlockValue(blockId, value) {
18188
+ applyDocument(updateDocument2ImageValue(documentRef.current, blockId, value), "immediate");
18189
+ },
18190
+ updateImageBlockAsset(blockId, asset) {
18191
+ applyDocument(updateDocument2ImageAsset(documentRef.current, blockId, asset), "immediate");
18192
+ },
18193
+ getDocumentJson() {
18194
+ return toDocumentJson2(documentRef.current);
17803
18195
  }
17804
- textDebounceRef.current = window.setTimeout(() => {
17805
- textSnapshotArmedRef.current = false;
17806
- }, 400);
18196
+ }),
18197
+ [applyDocument, insertImageBlock]
18198
+ );
18199
+ function undoDocument() {
18200
+ const restored = restoreDocument2Undo(historyRef.current, documentRef.current);
18201
+ if (!restored) {
18202
+ return;
17807
18203
  }
17808
- setDocumentNode(next);
17809
- },
17810
- [bumpHistoryUi]
17811
- );
17812
- const afterBlockId = useCallback2(() => resolveInsertAfterBlockId(documentRef.current, editorFocus), [editorFocus]);
17813
- function undoDocument() {
17814
- const restored = restoreDocument2Undo(historyRef.current, documentRef.current);
17815
- if (!restored) {
17816
- return;
17817
- }
17818
- textSnapshotArmedRef.current = false;
17819
- clearBlockSelection();
17820
- setDocumentNode(restored);
17821
- bumpHistoryUi();
17822
- }
17823
- function redoDocument() {
17824
- const restored = restoreDocument2Redo(historyRef.current, documentRef.current);
17825
- if (!restored) {
17826
- return;
18204
+ textSnapshotArmedRef.current = false;
18205
+ clearBlockSelection();
18206
+ documentRef.current = restored;
18207
+ setDocumentNode(restored);
18208
+ bumpHistoryUi();
17827
18209
  }
17828
- textSnapshotArmedRef.current = false;
17829
- clearBlockSelection();
17830
- setDocumentNode(restored);
17831
- bumpHistoryUi();
17832
- }
17833
- function openBlock(blockId) {
17834
- if (!blockId) {
17835
- return;
18210
+ function redoDocument() {
18211
+ const restored = restoreDocument2Redo(historyRef.current, documentRef.current);
18212
+ if (!restored) {
18213
+ return;
18214
+ }
18215
+ textSnapshotArmedRef.current = false;
18216
+ clearBlockSelection();
18217
+ documentRef.current = restored;
18218
+ setDocumentNode(restored);
18219
+ bumpHistoryUi();
17836
18220
  }
17837
- setCollapsedBlockIds((current) => {
17838
- if (!current.has(blockId)) {
17839
- return current;
18221
+ function openBlock(blockId) {
18222
+ if (!blockId) {
18223
+ return;
17840
18224
  }
17841
- const next = new Set(current);
17842
- next.delete(blockId);
17843
- return next;
17844
- });
17845
- }
17846
- function toggleBlockCollapse(blockId) {
17847
- setCollapsedBlockIds((current) => {
17848
- const next = new Set(current);
17849
- if (next.has(blockId)) {
18225
+ setCollapsedBlockIds((current) => {
18226
+ if (!current.has(blockId)) {
18227
+ return current;
18228
+ }
18229
+ const next = new Set(current);
17850
18230
  next.delete(blockId);
17851
- } else {
17852
- next.add(blockId);
17853
- }
17854
- return next;
17855
- });
17856
- }
17857
- function collapseAllBlocks() {
17858
- setCollapsedBlockIds(new Set(documentRef.current.blocks.map((block) => block.id)));
17859
- }
17860
- function openAllBlocks() {
17861
- setCollapsedBlockIds(/* @__PURE__ */ new Set());
17862
- }
17863
- async function copyDebugText(text) {
17864
- try {
17865
- await navigator.clipboard.writeText(text);
17866
- } catch {
18231
+ return next;
18232
+ });
17867
18233
  }
17868
- }
17869
- function openDebugJsonImport() {
17870
- setDebugImportText(JSON.stringify(documentJson, null, 2));
17871
- setDebugImportError("");
17872
- setDebugImportOpen(true);
17873
- }
17874
- function applyDebugJsonImport() {
17875
- const parsed = parseJsonDebugInput(debugImportText);
17876
- if (!parsed.ok) {
17877
- setDebugImportError(parsed.error);
17878
- return;
18234
+ function toggleBlockCollapse(blockId) {
18235
+ setCollapsedBlockIds((current) => {
18236
+ const next = new Set(current);
18237
+ if (next.has(blockId)) {
18238
+ next.delete(blockId);
18239
+ } else {
18240
+ next.add(blockId);
18241
+ }
18242
+ return next;
18243
+ });
17879
18244
  }
17880
- try {
17881
- const imported = fromDocumentJson2(parsed.value);
17882
- textSnapshotArmedRef.current = false;
17883
- if (textDebounceRef.current !== null) {
17884
- window.clearTimeout(textDebounceRef.current);
17885
- textDebounceRef.current = null;
17886
- }
17887
- setDocumentNode(imported);
17888
- setError(null);
17889
- setSelectedMath(null);
17890
- setCitePicker(null);
17891
- setRefPicker(null);
17892
- clearBlockSelection();
18245
+ function collapseAllBlocks() {
18246
+ setCollapsedBlockIds(new Set(documentRef.current.blocks.map((block) => block.id)));
18247
+ }
18248
+ function openAllBlocks() {
17893
18249
  setCollapsedBlockIds(/* @__PURE__ */ new Set());
17894
- setEditorFocus(createEmptyDocument2EditorFocus());
17895
- historyRef.current = createDocument2History();
17896
- bumpHistoryUi();
18250
+ }
18251
+ async function copyDebugText(text) {
18252
+ try {
18253
+ await navigator.clipboard.writeText(text);
18254
+ } catch {
18255
+ }
18256
+ }
18257
+ function openDebugJsonImport() {
18258
+ setDebugImportText(JSON.stringify(documentJson, null, 2));
17897
18259
  setDebugImportError("");
17898
- setDebugImportOpen(false);
17899
- } catch (importError) {
17900
- setDebugImportError(importError instanceof Error ? importError.message : String(importError));
18260
+ setDebugImportOpen(true);
17901
18261
  }
17902
- }
17903
- useEffect9(() => {
17904
- const root = widgetRef.current;
17905
- if (!root) {
17906
- return;
18262
+ function applyDebugJsonImport() {
18263
+ const parsed = parseJsonDebugInput(debugImportText);
18264
+ if (!parsed.ok) {
18265
+ setDebugImportError(parsed.error);
18266
+ return;
18267
+ }
18268
+ try {
18269
+ const imported = fromDocumentJson2(parsed.value);
18270
+ textSnapshotArmedRef.current = false;
18271
+ if (textDebounceRef.current !== null) {
18272
+ window.clearTimeout(textDebounceRef.current);
18273
+ textDebounceRef.current = null;
18274
+ }
18275
+ setDocumentNode(imported);
18276
+ setError(null);
18277
+ setSelectedMath(null);
18278
+ setCitePicker(null);
18279
+ setRefPicker(null);
18280
+ clearBlockSelection();
18281
+ setCollapsedBlockIds(/* @__PURE__ */ new Set());
18282
+ setEditorFocus(createEmptyDocument2EditorFocus());
18283
+ historyRef.current = createDocument2History();
18284
+ bumpHistoryUi();
18285
+ setDebugImportError("");
18286
+ setDebugImportOpen(false);
18287
+ } catch (importError) {
18288
+ setDebugImportError(importError instanceof Error ? importError.message : String(importError));
18289
+ }
17907
18290
  }
17908
- const onKeyDown = (event) => {
17909
- if (!editorOpen) {
18291
+ useEffect10(() => {
18292
+ const root = widgetRef.current;
18293
+ if (!root) {
17910
18294
  return;
17911
18295
  }
17912
- const mod = event.ctrlKey || event.metaKey;
17913
- if (!mod) {
18296
+ const onKeyDown = (event) => {
18297
+ if (!editorOpen) {
18298
+ return;
18299
+ }
18300
+ const mod = event.ctrlKey || event.metaKey;
18301
+ if (!mod) {
18302
+ return;
18303
+ }
18304
+ if (event.key.toLowerCase() === "z" && !event.shiftKey) {
18305
+ event.preventDefault();
18306
+ undoDocument();
18307
+ } else if (event.key.toLowerCase() === "z" && event.shiftKey || event.key.toLowerCase() === "y") {
18308
+ event.preventDefault();
18309
+ redoDocument();
18310
+ }
18311
+ };
18312
+ root.addEventListener("keydown", onKeyDown);
18313
+ return () => root.removeEventListener("keydown", onKeyDown);
18314
+ }, [editorOpen]);
18315
+ function rememberFieldFocus(blockId, fieldId, textTokenId, caretOffset, selectionStart, selectionEnd) {
18316
+ openBlock(blockId);
18317
+ clearBlockSelection();
18318
+ setEditorFocus({ blockId, fieldId, textTokenId, caretOffset, selectionStart, selectionEnd });
18319
+ }
18320
+ function rememberMathFocus(blockId, fieldId) {
18321
+ openBlock(blockId);
18322
+ clearBlockSelection();
18323
+ setEditorFocus({ blockId, fieldId, textTokenId: null, caretOffset: 0, selectionStart: 0, selectionEnd: 0 });
18324
+ }
18325
+ function rememberBlockFocus(blockId) {
18326
+ openBlock(blockId);
18327
+ setEditorFocus((current) => ({ ...current, blockId }));
18328
+ }
18329
+ function onFieldBlur() {
18330
+ textSnapshotArmedRef.current = false;
18331
+ }
18332
+ function openNewEquation(mode) {
18333
+ if (!editableEquations) {
17914
18334
  return;
17915
18335
  }
17916
- if (event.key.toLowerCase() === "z" && !event.shiftKey) {
17917
- event.preventDefault();
17918
- undoDocument();
17919
- } else if (event.key.toLowerCase() === "z" && event.shiftKey || event.key.toLowerCase() === "y") {
17920
- event.preventDefault();
17921
- redoDocument();
18336
+ const target = resolveInsertField(documentRef.current, editorFocus);
18337
+ if (target?.fieldId) {
18338
+ openBlock(topLevelBlockIdForField(documentRef.current, target.fieldId));
17922
18339
  }
17923
- };
17924
- root.addEventListener("keydown", onKeyDown);
17925
- return () => root.removeEventListener("keydown", onKeyDown);
17926
- }, [editorOpen]);
17927
- function rememberFieldFocus(blockId, fieldId, textTokenId, caretOffset, selectionStart, selectionEnd) {
17928
- openBlock(blockId);
17929
- clearBlockSelection();
17930
- setEditorFocus({ blockId, fieldId, textTokenId, caretOffset, selectionStart, selectionEnd });
17931
- }
17932
- function rememberMathFocus(blockId, fieldId) {
17933
- openBlock(blockId);
17934
- clearBlockSelection();
17935
- setEditorFocus({ blockId, fieldId, textTokenId: null, caretOffset: 0, selectionStart: 0, selectionEnd: 0 });
17936
- }
17937
- function rememberBlockFocus(blockId) {
17938
- openBlock(blockId);
17939
- setEditorFocus((current) => ({ ...current, blockId }));
17940
- }
17941
- function onFieldBlur() {
17942
- textSnapshotArmedRef.current = false;
17943
- }
17944
- function openNewEquation(mode) {
17945
- if (!editableEquations) {
17946
- return;
17947
- }
17948
- const target = resolveInsertField(documentRef.current, editorFocus);
17949
- if (target?.fieldId) {
17950
- openBlock(topLevelBlockIdForField(documentRef.current, target.fieldId));
17951
- }
17952
- setSelectedMath({
17953
- tokenId: null,
17954
- fieldId: target?.fieldId ?? null,
17955
- textTokenId: target?.textTokenId ?? null,
17956
- insertAt: target?.caretOffset ?? 0,
17957
- mathMode: mode,
17958
- session: equationSessionWithDocumentDigits(createEmptyEquationSession(equationSide)),
17959
- reason: null,
17960
- labelEnabled: false,
17961
- label: ""
17962
- });
17963
- }
17964
- function openMath(token) {
17965
- if (!editableEquations) {
17966
- return;
18340
+ setSelectedMath({
18341
+ tokenId: null,
18342
+ fieldId: target?.fieldId ?? null,
18343
+ textTokenId: target?.textTokenId ?? null,
18344
+ insertAt: target?.caretOffset ?? 0,
18345
+ mathMode: mode,
18346
+ session: equationSessionWithDocumentDigits(createEmptyEquationSession(equationSide)),
18347
+ reason: null,
18348
+ labelEnabled: false,
18349
+ label: ""
18350
+ });
17967
18351
  }
17968
- openBlock(topLevelBlockIdForToken(documentRef.current, token.id));
17969
- const result = mathTokenToEditorSession(token.math, equationSide);
17970
- const mode = token.display ? "display" : "inline";
17971
- if (!result.editable) {
18352
+ function openMath(token) {
18353
+ if (!editableEquations) {
18354
+ return;
18355
+ }
18356
+ openBlock(topLevelBlockIdForToken(documentRef.current, token.id));
18357
+ const result = mathTokenToEditorSession(token.math, equationSide);
18358
+ const mode = token.display ? "display" : "inline";
18359
+ if (!result.editable) {
18360
+ setSelectedMath({
18361
+ tokenId: token.id,
18362
+ fieldId: null,
18363
+ insertAt: 0,
18364
+ textTokenId: null,
18365
+ mathMode: mode,
18366
+ session: equationSessionWithDocumentDigits(createEmptyEquationSession(equationSide)),
18367
+ reason: result.reason,
18368
+ labelEnabled: Boolean(token.labelEnabled),
18369
+ label: token.label ?? ""
18370
+ });
18371
+ return;
18372
+ }
17972
18373
  setSelectedMath({
17973
18374
  tokenId: token.id,
17974
18375
  fieldId: null,
17975
18376
  insertAt: 0,
17976
18377
  textTokenId: null,
17977
18378
  mathMode: mode,
17978
- session: equationSessionWithDocumentDigits(createEmptyEquationSession(equationSide)),
17979
- reason: result.reason,
18379
+ session: equationSessionWithDocumentDigits(result.session),
18380
+ reason: null,
17980
18381
  labelEnabled: Boolean(token.labelEnabled),
17981
18382
  label: token.label ?? ""
17982
18383
  });
17983
- return;
17984
18384
  }
17985
- setSelectedMath({
17986
- tokenId: token.id,
17987
- fieldId: null,
17988
- insertAt: 0,
17989
- textTokenId: null,
17990
- mathMode: mode,
17991
- session: equationSessionWithDocumentDigits(result.session),
17992
- reason: null,
17993
- labelEnabled: Boolean(token.labelEnabled),
17994
- label: token.label ?? ""
17995
- });
17996
- }
17997
- function toggleEditorPanel() {
17998
- setEditorOpen((open) => {
17999
- if (open && !previewOpen) {
18000
- setPreviewOpen(true);
18385
+ function toggleEditorPanel() {
18386
+ setEditorOpen((open) => {
18387
+ if (open && !previewOpen) {
18388
+ setPreviewOpen(true);
18389
+ }
18390
+ return !open;
18391
+ });
18392
+ }
18393
+ function togglePreviewPanel() {
18394
+ setPreviewOpen((open) => {
18395
+ if (open && !editorOpen) {
18396
+ setEditorOpen(true);
18397
+ }
18398
+ return !open;
18399
+ });
18400
+ }
18401
+ function saveEquation(session) {
18402
+ if (!selectedMath) {
18403
+ return;
18001
18404
  }
18002
- return !open;
18003
- });
18004
- }
18005
- function togglePreviewPanel() {
18006
- setPreviewOpen((open) => {
18007
- if (open && !editorOpen) {
18008
- setEditorOpen(true);
18405
+ const delimiters = mathDelimiters(selectedMath.mathMode);
18406
+ if (selectedMath.tokenId) {
18407
+ let next2 = replaceMathTokenFromSession(
18408
+ documentRef.current,
18409
+ selectedMath.tokenId,
18410
+ session,
18411
+ delimiters.opening,
18412
+ delimiters.closing,
18413
+ equationSide
18414
+ );
18415
+ if (selectedMath.mathMode === "display") {
18416
+ next2 = updateMathTokenLabel(next2, selectedMath.tokenId, {
18417
+ labelEnabled: selectedMath.labelEnabled,
18418
+ label: selectedMath.label
18419
+ });
18420
+ } else {
18421
+ next2 = updateMathTokenLabel(next2, selectedMath.tokenId, { labelEnabled: false, label: "" });
18422
+ }
18423
+ pendingFocusRef.current = { kind: "math", tokenId: selectedMath.tokenId };
18424
+ applyDocument(next2, "immediate");
18425
+ setSelectedMath(null);
18426
+ return;
18009
18427
  }
18010
- return !open;
18011
- });
18012
- }
18013
- function saveEquation(session) {
18014
- if (!selectedMath) {
18015
- return;
18016
- }
18017
- const delimiters = mathDelimiters(selectedMath.mathMode);
18018
- if (selectedMath.tokenId) {
18019
- let next2 = replaceMathTokenFromSession(
18020
- documentRef.current,
18021
- selectedMath.tokenId,
18428
+ let current = documentRef.current;
18429
+ let fieldId = selectedMath.fieldId;
18430
+ let textTokenId = selectedMath.textTokenId;
18431
+ let insertAt = selectedMath.insertAt;
18432
+ if (!fieldId) {
18433
+ const anchor = afterBlockId();
18434
+ current = addDocument2TextBlock(current, "\\paragraph", anchor);
18435
+ const anchorIndex = anchor ? current.blocks.findIndex((block) => block.id === anchor) : -1;
18436
+ const newIndex = anchorIndex >= 0 ? anchorIndex + 1 : current.blocks.length - 1;
18437
+ const newBlock = current.blocks[newIndex];
18438
+ const field = newBlock?.kind === "textBlock" ? newBlock.field : null;
18439
+ if (!field) {
18440
+ setSelectedMath(null);
18441
+ return;
18442
+ }
18443
+ fieldId = field.id;
18444
+ textTokenId = field.tokens.find((token) => token.kind === "text")?.id ?? null;
18445
+ insertAt = 0;
18446
+ }
18447
+ let next = insertMathTokenAtCaret(
18448
+ current,
18449
+ fieldId,
18450
+ textTokenId,
18451
+ insertAt,
18022
18452
  session,
18023
18453
  delimiters.opening,
18024
18454
  delimiters.closing,
18025
18455
  equationSide
18026
18456
  );
18027
- if (selectedMath.mathMode === "display") {
18028
- next2 = updateMathTokenLabel(next2, selectedMath.tokenId, {
18029
- labelEnabled: selectedMath.labelEnabled,
18457
+ const trailingTextTokenId = trailingTextTokenAfterInsertedMath(current, next, fieldId, textTokenId, insertAt);
18458
+ const fieldAfter = findInlineFieldById(next, fieldId);
18459
+ const insertedMathId = trailingTextTokenId && fieldAfter ? fieldAfter.tokens[Math.max(
18460
+ 0,
18461
+ fieldAfter.tokens.findIndex((token) => token.id === trailingTextTokenId) - 1
18462
+ )]?.kind === "math" ? fieldAfter.tokens[Math.max(
18463
+ 0,
18464
+ fieldAfter.tokens.findIndex((token) => token.id === trailingTextTokenId) - 1
18465
+ )]?.id : null : fieldAfter?.tokens.filter((token) => token.kind === "math").at(-1)?.id ?? null;
18466
+ if (insertedMathId && selectedMath.mathMode === "display" && selectedMath.labelEnabled) {
18467
+ next = updateMathTokenLabel(next, insertedMathId, {
18468
+ labelEnabled: true,
18030
18469
  label: selectedMath.label
18031
18470
  });
18032
- } else {
18033
- next2 = updateMathTokenLabel(next2, selectedMath.tokenId, { labelEnabled: false, label: "" });
18034
18471
  }
18035
- pendingFocusRef.current = { kind: "math", tokenId: selectedMath.tokenId };
18036
- applyDocument(next2, "immediate");
18472
+ if (trailingTextTokenId) {
18473
+ pendingFocusRef.current = { kind: "text", fieldId, textTokenId: trailingTextTokenId, caretOffset: 0 };
18474
+ }
18475
+ applyDocument(next, "immediate");
18037
18476
  setSelectedMath(null);
18038
- return;
18039
18477
  }
18040
- let current = documentRef.current;
18041
- let fieldId = selectedMath.fieldId;
18042
- let textTokenId = selectedMath.textTokenId;
18043
- let insertAt = selectedMath.insertAt;
18044
- if (!fieldId) {
18045
- const anchor = afterBlockId();
18046
- current = addDocument2TextBlock(current, "\\paragraph", anchor);
18047
- const anchorIndex = anchor ? current.blocks.findIndex((block) => block.id === anchor) : -1;
18048
- const newIndex = anchorIndex >= 0 ? anchorIndex + 1 : current.blocks.length - 1;
18049
- const newBlock = current.blocks[newIndex];
18050
- const field = newBlock?.kind === "textBlock" ? newBlock.field : null;
18051
- if (!field) {
18052
- setSelectedMath(null);
18478
+ function deleteSelectedEquation() {
18479
+ if (!selectedMath?.tokenId) {
18053
18480
  return;
18054
18481
  }
18055
- fieldId = field.id;
18056
- textTokenId = field.tokens.find((token) => token.kind === "text")?.id ?? null;
18057
- insertAt = 0;
18058
- }
18059
- let next = insertMathTokenAtCaret(
18060
- current,
18061
- fieldId,
18062
- textTokenId,
18063
- insertAt,
18064
- session,
18065
- delimiters.opening,
18066
- delimiters.closing,
18067
- equationSide
18068
- );
18069
- const trailingTextTokenId = trailingTextTokenAfterInsertedMath(current, next, fieldId, textTokenId, insertAt);
18070
- const fieldAfter = findInlineFieldById(next, fieldId);
18071
- const insertedMathId = trailingTextTokenId && fieldAfter ? fieldAfter.tokens[Math.max(
18072
- 0,
18073
- fieldAfter.tokens.findIndex((token) => token.id === trailingTextTokenId) - 1
18074
- )]?.kind === "math" ? fieldAfter.tokens[Math.max(
18075
- 0,
18076
- fieldAfter.tokens.findIndex((token) => token.id === trailingTextTokenId) - 1
18077
- )]?.id : null : fieldAfter?.tokens.filter((token) => token.kind === "math").at(-1)?.id ?? null;
18078
- if (insertedMathId && selectedMath.mathMode === "display" && selectedMath.labelEnabled) {
18079
- next = updateMathTokenLabel(next, insertedMathId, {
18080
- labelEnabled: true,
18081
- label: selectedMath.label
18082
- });
18083
- }
18084
- if (trailingTextTokenId) {
18085
- pendingFocusRef.current = { kind: "text", fieldId, textTokenId: trailingTextTokenId, caretOffset: 0 };
18086
- }
18087
- applyDocument(next, "immediate");
18088
- setSelectedMath(null);
18089
- }
18090
- function deleteSelectedEquation() {
18091
- if (!selectedMath?.tokenId) {
18092
- return;
18482
+ const current = documentRef.current;
18483
+ const next = removeMathTokenById(current, selectedMath.tokenId);
18484
+ pendingFocusRef.current = focusAfterDeletedMath(current, next, selectedMath.tokenId);
18485
+ applyDocument(next, "immediate");
18486
+ setSelectedMath(null);
18093
18487
  }
18094
- const current = documentRef.current;
18095
- const next = removeMathTokenById(current, selectedMath.tokenId);
18096
- pendingFocusRef.current = focusAfterDeletedMath(current, next, selectedMath.tokenId);
18097
- applyDocument(next, "immediate");
18098
- setSelectedMath(null);
18099
- }
18100
- function deleteMathToken(tokenId) {
18101
- const current = documentRef.current;
18102
- const next = removeMathTokenById(current, tokenId);
18103
- pendingFocusRef.current = focusAfterDeletedMath(current, next, tokenId);
18104
- applyDocument(next, "immediate");
18105
- }
18106
- function openCitePickerForInsert() {
18107
- const target = resolveInsertField(documentRef.current, editorFocus);
18108
- setCitePicker({
18109
- tokenId: null,
18110
- fieldId: target?.fieldId ?? null,
18111
- textTokenId: target?.textTokenId ?? editorFocus.textTokenId,
18112
- caretOffset: target?.caretOffset ?? editorFocus.caretOffset,
18113
- keys: []
18114
- });
18115
- }
18116
- function openCite(token) {
18117
- setCitePicker({
18118
- tokenId: token.id,
18119
- fieldId: null,
18120
- textTokenId: null,
18121
- caretOffset: 0,
18122
- keys: [...token.keys]
18123
- });
18124
- }
18125
- function confirmCiteKeys(keys) {
18126
- if (!citePicker || keys.length === 0) {
18127
- setCitePicker(null);
18128
- return;
18488
+ function deleteMathToken(tokenId) {
18489
+ const current = documentRef.current;
18490
+ const next = removeMathTokenById(current, tokenId);
18491
+ pendingFocusRef.current = focusAfterDeletedMath(current, next, tokenId);
18492
+ applyDocument(next, "immediate");
18493
+ }
18494
+ function openCitePickerForInsert() {
18495
+ const target = resolveInsertField(documentRef.current, editorFocus);
18496
+ setCitePicker({
18497
+ tokenId: null,
18498
+ fieldId: target?.fieldId ?? null,
18499
+ textTokenId: target?.textTokenId ?? editorFocus.textTokenId,
18500
+ caretOffset: target?.caretOffset ?? editorFocus.caretOffset,
18501
+ keys: []
18502
+ });
18129
18503
  }
18130
- if (citePicker.tokenId) {
18131
- applyDocument(updateCiteTokenKeys(documentRef.current, citePicker.tokenId, keys), "immediate");
18132
- pendingFocusRef.current = { kind: "cite", tokenId: citePicker.tokenId };
18133
- setCitePicker(null);
18134
- return;
18504
+ function openCite(token) {
18505
+ setCitePicker({
18506
+ tokenId: token.id,
18507
+ fieldId: null,
18508
+ textTokenId: null,
18509
+ caretOffset: 0,
18510
+ keys: [...token.keys]
18511
+ });
18135
18512
  }
18136
- const target = resolveInsertField(documentRef.current, editorFocus);
18137
- const fieldId = citePicker.fieldId ?? target?.fieldId;
18138
- if (!fieldId) {
18513
+ function confirmCiteKeys(keys) {
18514
+ if (!citePicker || keys.length === 0) {
18515
+ setCitePicker(null);
18516
+ return;
18517
+ }
18518
+ if (citePicker.tokenId) {
18519
+ applyDocument(updateCiteTokenKeys(documentRef.current, citePicker.tokenId, keys), "immediate");
18520
+ pendingFocusRef.current = { kind: "cite", tokenId: citePicker.tokenId };
18521
+ setCitePicker(null);
18522
+ return;
18523
+ }
18524
+ const target = resolveInsertField(documentRef.current, editorFocus);
18525
+ const fieldId = citePicker.fieldId ?? target?.fieldId;
18526
+ if (!fieldId) {
18527
+ setCitePicker(null);
18528
+ return;
18529
+ }
18530
+ const textTokenId = citePicker.textTokenId ?? target?.textTokenId ?? null;
18531
+ const caretOffset = citePicker.caretOffset ?? target?.caretOffset ?? 0;
18532
+ const next = insertCiteTokenAtCaret(documentRef.current, fieldId, textTokenId, caretOffset, keys);
18533
+ applyDocument(next, "immediate");
18139
18534
  setCitePicker(null);
18140
- return;
18141
18535
  }
18142
- const textTokenId = citePicker.textTokenId ?? target?.textTokenId ?? null;
18143
- const caretOffset = citePicker.caretOffset ?? target?.caretOffset ?? 0;
18144
- const next = insertCiteTokenAtCaret(documentRef.current, fieldId, textTokenId, caretOffset, keys);
18145
- applyDocument(next, "immediate");
18146
- setCitePicker(null);
18147
- }
18148
- function deleteCiteToken(tokenId) {
18149
- applyDocument(removeCiteTokenById(documentRef.current, tokenId), "immediate");
18150
- }
18151
- function openRefPickerForInsert() {
18152
- const target = resolveInsertField(documentRef.current, editorFocus);
18153
- setRefPicker({
18154
- tokenId: null,
18155
- fieldId: target?.fieldId ?? null,
18156
- textTokenId: target?.textTokenId ?? editorFocus.textTokenId,
18157
- caretOffset: target?.caretOffset ?? editorFocus.caretOffset,
18158
- keys: [],
18159
- refCommand: "ref"
18160
- });
18161
- }
18162
- function openRef(token) {
18163
- setRefPicker({
18164
- tokenId: token.id,
18165
- fieldId: null,
18166
- textTokenId: null,
18167
- caretOffset: 0,
18168
- keys: [...token.keys],
18169
- refCommand: token.refCommand
18170
- });
18171
- }
18172
- function confirmRefKeys(keys, refCommand) {
18173
- if (!refPicker || keys.length === 0) {
18174
- setRefPicker(null);
18175
- return;
18536
+ function deleteCiteToken(tokenId) {
18537
+ applyDocument(removeCiteTokenById(documentRef.current, tokenId), "immediate");
18538
+ }
18539
+ function openRefPickerForInsert() {
18540
+ const target = resolveInsertField(documentRef.current, editorFocus);
18541
+ setRefPicker({
18542
+ tokenId: null,
18543
+ fieldId: target?.fieldId ?? null,
18544
+ textTokenId: target?.textTokenId ?? editorFocus.textTokenId,
18545
+ caretOffset: target?.caretOffset ?? editorFocus.caretOffset,
18546
+ keys: [],
18547
+ refCommand: "ref"
18548
+ });
18176
18549
  }
18177
- if (refPicker.tokenId) {
18178
- applyDocument(updateRefTokenKeys(documentRef.current, refPicker.tokenId, keys, refCommand), "immediate");
18179
- pendingFocusRef.current = { kind: "ref", tokenId: refPicker.tokenId };
18180
- setRefPicker(null);
18181
- return;
18550
+ function openRef(token) {
18551
+ setRefPicker({
18552
+ tokenId: token.id,
18553
+ fieldId: null,
18554
+ textTokenId: null,
18555
+ caretOffset: 0,
18556
+ keys: [...token.keys],
18557
+ refCommand: token.refCommand
18558
+ });
18182
18559
  }
18183
- const target = resolveInsertField(documentRef.current, editorFocus);
18184
- const fieldId = refPicker.fieldId ?? target?.fieldId;
18185
- if (!fieldId) {
18560
+ function confirmRefKeys(keys, refCommand) {
18561
+ if (!refPicker || keys.length === 0) {
18562
+ setRefPicker(null);
18563
+ return;
18564
+ }
18565
+ if (refPicker.tokenId) {
18566
+ applyDocument(updateRefTokenKeys(documentRef.current, refPicker.tokenId, keys, refCommand), "immediate");
18567
+ pendingFocusRef.current = { kind: "ref", tokenId: refPicker.tokenId };
18568
+ setRefPicker(null);
18569
+ return;
18570
+ }
18571
+ const target = resolveInsertField(documentRef.current, editorFocus);
18572
+ const fieldId = refPicker.fieldId ?? target?.fieldId;
18573
+ if (!fieldId) {
18574
+ setRefPicker(null);
18575
+ return;
18576
+ }
18577
+ const textTokenId = refPicker.textTokenId ?? target?.textTokenId ?? null;
18578
+ const caretOffset = refPicker.caretOffset ?? target?.caretOffset ?? 0;
18579
+ const next = insertRefTokenAtCaret(documentRef.current, fieldId, textTokenId, caretOffset, keys, refCommand);
18580
+ applyDocument(next, "immediate");
18186
18581
  setRefPicker(null);
18187
- return;
18188
18582
  }
18189
- const textTokenId = refPicker.textTokenId ?? target?.textTokenId ?? null;
18190
- const caretOffset = refPicker.caretOffset ?? target?.caretOffset ?? 0;
18191
- const next = insertRefTokenAtCaret(documentRef.current, fieldId, textTokenId, caretOffset, keys, refCommand);
18192
- applyDocument(next, "immediate");
18193
- setRefPicker(null);
18194
- }
18195
- function deleteRefToken(tokenId) {
18196
- applyDocument(removeRefTokenById(documentRef.current, tokenId), "immediate");
18197
- }
18198
- function toggleInlineTextStyle(styleName) {
18199
- if (!canFormatText || !editorFocus.fieldId || !editorFocus.textTokenId) {
18200
- return;
18583
+ function deleteRefToken(tokenId) {
18584
+ applyDocument(removeRefTokenById(documentRef.current, tokenId), "immediate");
18201
18585
  }
18202
- applyDocument(
18203
- toggleTextTokenStyle(
18204
- documentRef.current,
18205
- editorFocus.fieldId,
18206
- editorFocus.textTokenId,
18207
- editorFocus.selectionStart,
18208
- editorFocus.selectionEnd,
18209
- styleName
18210
- ),
18211
- "immediate"
18212
- );
18213
- }
18214
- const showEditorPanel = !previewOnly && editorOpen;
18215
- const showPreviewPanel = previewOnly || previewOpen;
18216
- return /* @__PURE__ */ jsx17(
18217
- "div",
18218
- {
18219
- ref: widgetRef,
18220
- className: ["butex-document2-widget", className].filter(Boolean).join(" "),
18221
- dir: uiLocaleDirection(uiLocale),
18222
- lang: uiLocale,
18223
- children: /* @__PURE__ */ jsxs15("div", { className: "butex-document2-widget__shell", children: [
18224
- !previewOnly ? /* @__PURE__ */ jsxs15("div", { className: "butex-document2-widget__toolbar", children: [
18225
- /* @__PURE__ */ jsx17(
18226
- DocumentInsertToolbar,
18227
- {
18228
- canUndo,
18229
- canRedo,
18230
- editableEquations,
18231
- uiLocale,
18232
- digitForm,
18233
- selectionCount,
18234
- canMoveSelectionUp,
18235
- canMoveSelectionDown,
18236
- canFormatText,
18237
- activeTextStyles,
18238
- onUndo: undoDocument,
18239
- onRedo: redoDocument,
18240
- onToggleTextStyle: toggleInlineTextStyle,
18241
- onMoveSelectionUp: () => moveSelectedBlocks(-1),
18242
- onMoveSelectionDown: () => moveSelectedBlocks(1),
18243
- onDeleteSelection: deleteSelectedBlocks,
18244
- onAddSection: () => {
18245
- clearBlockSelection();
18246
- applyDocument(addDocument2TextBlock(documentRef.current, "\\section", afterBlockId()), "immediate");
18247
- },
18248
- onAddSubsection: () => {
18249
- clearBlockSelection();
18250
- applyDocument(addDocument2TextBlock(documentRef.current, "\\subsection", afterBlockId()), "immediate");
18251
- },
18252
- onAddSubsubsection: () => {
18253
- clearBlockSelection();
18254
- applyDocument(addDocument2TextBlock(documentRef.current, "\\subsubsection", afterBlockId()), "immediate");
18255
- },
18256
- onAddParagraph: () => {
18257
- clearBlockSelection();
18258
- applyDocument(addDocument2TextBlock(documentRef.current, "\\paragraph", afterBlockId()), "immediate");
18259
- },
18260
- onAddInlineEquation: () => openNewEquation("inline"),
18261
- onAddDisplayEquation: () => openNewEquation("display"),
18262
- onAddTable: (rowCount, colCount) => {
18263
- clearBlockSelection();
18264
- applyDocument(addDocument2TableBlock(documentRef.current, "l".repeat(colCount), rowCount, colCount, afterBlockId()), "immediate");
18265
- },
18266
- onAddList: () => {
18267
- clearBlockSelection();
18268
- applyDocument(addDocument2ListBlock(documentRef.current, false, afterBlockId()), "immediate");
18269
- },
18270
- onAddEnumerate: () => {
18271
- clearBlockSelection();
18272
- applyDocument(addDocument2ListBlock(documentRef.current, true, afterBlockId()), "immediate");
18273
- },
18274
- onAddFigure: () => {
18275
- clearBlockSelection();
18276
- applyDocument(addDocument2ImageBlock(documentRef.current, "", afterBlockId()), "immediate");
18277
- },
18278
- onInsertCitation: openCitePickerForInsert,
18279
- onInsertInternalRef: openRefPickerForInsert,
18280
- onInsertBibliography: () => {
18281
- clearBlockSelection();
18282
- applyDocument(ensureDocument2BibliographyBlock(documentRef.current, afterBlockId()), "immediate");
18283
- },
18284
- onManageReferences: () => setReferencesOpen(true),
18285
- onManageLabels: () => setLabelsOpen(true),
18286
- onOpenArticleMeta: () => focusArticleMetaPanel(articleMetaPanelRef.current),
18287
- onDigitFormChange: (next) => {
18288
- setDigitFormState(next);
18289
- onDigitFormChange?.(next);
18290
- }
18291
- }
18292
- ),
18293
- /* @__PURE__ */ jsxs15("div", { className: "butex-document2-widget__toolbar-group", role: "group", "aria-label": messages.panels, children: [
18294
- /* @__PURE__ */ jsx17("button", { type: "button", "aria-pressed": editorOpen, onClick: toggleEditorPanel, children: editorOpen ? messages.hideEditor : messages.editor }),
18295
- /* @__PURE__ */ jsx17("button", { type: "button", "aria-pressed": previewOpen, onClick: togglePreviewPanel, children: previewOpen ? messages.hidePreview : messages.preview })
18296
- ] }),
18297
- /* @__PURE__ */ jsxs15("div", { className: "butex-document2-widget__toolbar-group", role: "group", "aria-label": messages.collapseBlocks, children: [
18298
- /* @__PURE__ */ jsx17("button", { type: "button", onClick: collapseAllBlocks, children: messages.collapseAll }),
18299
- /* @__PURE__ */ jsx17("button", { type: "button", onClick: openAllBlocks, children: messages.openAll })
18300
- ] })
18301
- ] }) : null,
18302
- error ? /* @__PURE__ */ jsx17("p", { className: "butex-document2-widget__error", children: error }) : null,
18303
- /* @__PURE__ */ jsxs15(
18304
- "div",
18305
- {
18306
- className: `butex-document2-widget__layout butex-document2-widget__layout--editor-${showEditorPanel ? "open" : "closed"} butex-document2-widget__layout--preview-${showPreviewPanel ? "open" : "closed"}`,
18307
- children: [
18308
- !previewOnly ? /* @__PURE__ */ jsx17(
18309
- "section",
18310
- {
18311
- className: "butex-document2-widget__panel butex-document2-widget__editor-panel",
18312
- "aria-label": messages.documentEditor,
18313
- "aria-hidden": !showEditorPanel,
18314
- children: /* @__PURE__ */ jsxs15("div", { className: "butex-document2-widget__blocks", children: [
18315
- /* @__PURE__ */ jsx17(
18316
- ArticleMetaPanel,
18317
- {
18318
- meta: documentNode.meta ?? emptyDocument2Meta(),
18319
- uiLocale,
18320
- digitForm,
18321
- panelRef: articleMetaPanelRef,
18322
- onChange: (patch) => {
18323
- clearBlockSelection();
18324
- applyDocument(updateDocument2Meta(documentRef.current, patch), "text");
18325
- }
18326
- }
18327
- ),
18328
- documentNode.blocks.map((block, blockIndex) => /* @__PURE__ */ jsx17(
18329
- BlockEditor,
18330
- {
18331
- block,
18332
- references: documentNode.references,
18333
- documentNode,
18334
- documentDirection,
18335
- digitForm,
18336
- mathOutput,
18337
- equationSide,
18338
- editableEquations,
18339
- editableCitations: !previewOnly,
18340
- uiLocale,
18341
- isCollapsed: collapsedBlockIds.has(block.id),
18342
- selected: Boolean(blockSelection && blockIndex >= blockSelection.from && blockIndex <= blockSelection.to),
18343
- onToggleSelect: toggleSelectBlock,
18344
- onTextChange: (fieldId, tokenId, text) => {
18345
- clearBlockSelection();
18346
- applyDocument(updateTextToken(documentRef.current, fieldId, tokenId, text), "text");
18347
- },
18348
- onOpenMath: openMath,
18349
- onDeleteMath: editableEquations ? deleteMathToken : void 0,
18350
- onOpenCite: openCite,
18351
- onDeleteCite: deleteCiteToken,
18352
- onOpenRef: openRef,
18353
- onDeleteRef: deleteRefToken,
18354
- onToggleCollapse: toggleBlockCollapse,
18355
- onBlockFocus: rememberBlockFocus,
18356
- onFieldFocus: rememberFieldFocus,
18357
- onMathFocus: rememberMathFocus,
18358
- onFieldBlur,
18359
- onImageSrcChange: (blockId, value) => {
18360
- clearBlockSelection();
18361
- applyDocument(updateDocument2ImageValue(documentRef.current, blockId, value), "text");
18362
- },
18363
- onFloatMetaChange: (blockId, kind, patch) => {
18364
- clearBlockSelection();
18365
- applyDocument(
18366
- kind === "image" ? updateDocument2ImageMeta(documentRef.current, blockId, patch) : updateDocument2TableMeta(documentRef.current, blockId, patch),
18367
- "text"
18368
- );
18369
- },
18370
- onParagraphCenteredChange: (blockId, centered) => {
18371
- clearBlockSelection();
18372
- applyDocument(updateDocument2TextBlockCentered(documentRef.current, blockId, centered), "text");
18373
- },
18374
- onAddListItem: (listBlockId) => {
18375
- clearBlockSelection();
18376
- applyDocument(addDocument2ListItem(documentRef.current, listBlockId), "immediate");
18377
- },
18378
- onRemoveListItem: (listBlockId, itemId) => {
18379
- clearBlockSelection();
18380
- applyDocument(removeDocument2ListItem(documentRef.current, listBlockId, itemId), "immediate");
18381
- },
18382
- onManageReferences: () => setReferencesOpen(true)
18383
- },
18384
- block.id
18385
- ))
18386
- ] })
18387
- }
18388
- ) : null,
18389
- /* @__PURE__ */ jsx17(
18390
- "section",
18391
- {
18392
- className: "butex-document2-widget__panel butex-document2-widget__preview-panel",
18393
- "aria-label": messages.documentPreview,
18394
- "aria-hidden": !showPreviewPanel,
18395
- children: /* @__PURE__ */ jsx17(
18396
- DocumentPreview,
18397
- {
18398
- blocks: preview.blocks,
18399
- output: mathOutput,
18400
- documentDirection,
18401
- uiLocale,
18402
- digitForm,
18403
- resolveImageUrl
18404
- }
18405
- )
18406
- }
18407
- )
18408
- ]
18409
- }
18586
+ function toggleInlineTextStyle(styleName) {
18587
+ if (!canFormatText || !editorFocus.fieldId || !editorFocus.textTokenId) {
18588
+ return;
18589
+ }
18590
+ applyDocument(
18591
+ toggleTextTokenStyle(
18592
+ documentRef.current,
18593
+ editorFocus.fieldId,
18594
+ editorFocus.textTokenId,
18595
+ editorFocus.selectionStart,
18596
+ editorFocus.selectionEnd,
18597
+ styleName
18410
18598
  ),
18411
- debugEnabled ? /* @__PURE__ */ jsxs15("div", { className: "butex-document2-widget__dev", children: [
18412
- debugEnabled && documentNode.diagnostics.length > 0 ? /* @__PURE__ */ jsxs15(Fragment4, { children: [
18413
- /* @__PURE__ */ jsx17("strong", { children: messages.importWarnings }),
18414
- documentNode.diagnostics.map((diagnostic) => /* @__PURE__ */ jsx17("p", { className: "butex-document2-widget__dev-diagnostic", children: formatDocument2Diagnostic(diagnostic, messages) }, `${diagnostic.path}-${diagnostic.message}`))
18415
- ] }) : null,
18416
- /* @__PURE__ */ jsxs15("div", { className: "butex-document2-widget__dev-actions", children: [
18417
- /* @__PURE__ */ jsx17("button", { type: "button", onClick: () => void copyDebugText(JSON.stringify(documentJson, null, 2)), children: "Copy JSON" }),
18418
- /* @__PURE__ */ jsx17("button", { type: "button", onClick: () => void copyDebugText(latex), children: "Copy TeX" }),
18419
- /* @__PURE__ */ jsx17("button", { type: "button", "aria-expanded": debugImportOpen, onClick: openDebugJsonImport, children: "Import JSON" })
18420
- ] }),
18421
- debugImportOpen ? /* @__PURE__ */ jsxs15("div", { className: "butex-document2-widget__dev-import", children: [
18599
+ "immediate"
18600
+ );
18601
+ }
18602
+ const showEditorPanel = !previewOnly && editorOpen;
18603
+ const showPreviewPanel = previewOnly || previewOpen;
18604
+ return /* @__PURE__ */ jsx17(
18605
+ "div",
18606
+ {
18607
+ ref: widgetRef,
18608
+ className: ["butex-document2-widget", className].filter(Boolean).join(" "),
18609
+ dir: uiLocaleDirection(uiLocale),
18610
+ lang: uiLocale,
18611
+ children: /* @__PURE__ */ jsxs15("div", { className: "butex-document2-widget__shell", children: [
18612
+ !previewOnly ? /* @__PURE__ */ jsxs15("div", { className: "butex-document2-widget__toolbar", children: [
18422
18613
  /* @__PURE__ */ jsx17(
18423
- "textarea",
18614
+ DocumentInsertToolbar,
18424
18615
  {
18425
- value: debugImportText,
18426
- "aria-label": "Import Document2 JSON",
18427
- spellCheck: false,
18428
- onChange: (event) => {
18429
- setDebugImportText(event.currentTarget.value);
18430
- setDebugImportError("");
18616
+ canUndo,
18617
+ canRedo,
18618
+ editableEquations,
18619
+ uiLocale,
18620
+ digitForm,
18621
+ selectionCount,
18622
+ canMoveSelectionUp,
18623
+ canMoveSelectionDown,
18624
+ canFormatText,
18625
+ activeTextStyles,
18626
+ onUndo: undoDocument,
18627
+ onRedo: redoDocument,
18628
+ onToggleTextStyle: toggleInlineTextStyle,
18629
+ onMoveSelectionUp: () => moveSelectedBlocks(-1),
18630
+ onMoveSelectionDown: () => moveSelectedBlocks(1),
18631
+ onDeleteSelection: deleteSelectedBlocks,
18632
+ onAddSection: () => {
18633
+ clearBlockSelection();
18634
+ applyDocument(addDocument2TextBlock(documentRef.current, "\\section", afterBlockId()), "immediate");
18635
+ },
18636
+ onAddSubsection: () => {
18637
+ clearBlockSelection();
18638
+ applyDocument(addDocument2TextBlock(documentRef.current, "\\subsection", afterBlockId()), "immediate");
18639
+ },
18640
+ onAddSubsubsection: () => {
18641
+ clearBlockSelection();
18642
+ applyDocument(addDocument2TextBlock(documentRef.current, "\\subsubsection", afterBlockId()), "immediate");
18643
+ },
18644
+ onAddParagraph: () => {
18645
+ clearBlockSelection();
18646
+ applyDocument(addDocument2TextBlock(documentRef.current, "\\paragraph", afterBlockId()), "immediate");
18647
+ },
18648
+ onAddInlineEquation: () => openNewEquation("inline"),
18649
+ onAddDisplayEquation: () => openNewEquation("display"),
18650
+ onAddTable: (rowCount, colCount) => {
18651
+ clearBlockSelection();
18652
+ applyDocument(addDocument2TableBlock(documentRef.current, "l".repeat(colCount), rowCount, colCount, afterBlockId()), "immediate");
18653
+ },
18654
+ onAddList: () => {
18655
+ clearBlockSelection();
18656
+ applyDocument(addDocument2ListBlock(documentRef.current, false, afterBlockId()), "immediate");
18657
+ },
18658
+ onAddEnumerate: () => {
18659
+ clearBlockSelection();
18660
+ applyDocument(addDocument2ListBlock(documentRef.current, true, afterBlockId()), "immediate");
18661
+ },
18662
+ onAddFigure: () => insertImageBlock(""),
18663
+ onInsertCitation: openCitePickerForInsert,
18664
+ onInsertInternalRef: openRefPickerForInsert,
18665
+ onInsertBibliography: () => {
18666
+ clearBlockSelection();
18667
+ applyDocument(ensureDocument2BibliographyBlock(documentRef.current, afterBlockId()), "immediate");
18668
+ },
18669
+ onManageReferences: () => setReferencesOpen(true),
18670
+ onManageLabels: () => setLabelsOpen(true),
18671
+ onOpenArticleMeta: () => focusArticleMetaPanel(articleMetaPanelRef.current),
18672
+ onDigitFormChange: (next) => {
18673
+ setDigitFormState(next);
18674
+ onDigitFormChange?.(next);
18431
18675
  }
18432
18676
  }
18433
18677
  ),
18678
+ /* @__PURE__ */ jsxs15("div", { className: "butex-document2-widget__toolbar-group", role: "group", "aria-label": messages.panels, children: [
18679
+ /* @__PURE__ */ jsx17("button", { type: "button", "aria-pressed": editorOpen, onClick: toggleEditorPanel, children: editorOpen ? messages.hideEditor : messages.editor }),
18680
+ /* @__PURE__ */ jsx17("button", { type: "button", "aria-pressed": previewOpen, onClick: togglePreviewPanel, children: previewOpen ? messages.hidePreview : messages.preview })
18681
+ ] }),
18682
+ /* @__PURE__ */ jsxs15("div", { className: "butex-document2-widget__toolbar-group", role: "group", "aria-label": messages.collapseBlocks, children: [
18683
+ /* @__PURE__ */ jsx17("button", { type: "button", onClick: collapseAllBlocks, children: messages.collapseAll }),
18684
+ /* @__PURE__ */ jsx17("button", { type: "button", onClick: openAllBlocks, children: messages.openAll })
18685
+ ] })
18686
+ ] }) : null,
18687
+ error ? /* @__PURE__ */ jsx17("p", { className: "butex-document2-widget__error", children: error }) : null,
18688
+ /* @__PURE__ */ jsxs15(
18689
+ "div",
18690
+ {
18691
+ className: `butex-document2-widget__layout butex-document2-widget__layout--editor-${showEditorPanel ? "open" : "closed"} butex-document2-widget__layout--preview-${showPreviewPanel ? "open" : "closed"}`,
18692
+ children: [
18693
+ !previewOnly ? /* @__PURE__ */ jsx17(
18694
+ "section",
18695
+ {
18696
+ className: "butex-document2-widget__panel butex-document2-widget__editor-panel",
18697
+ "aria-label": messages.documentEditor,
18698
+ "aria-hidden": !showEditorPanel,
18699
+ children: /* @__PURE__ */ jsxs15("div", { className: "butex-document2-widget__blocks", children: [
18700
+ /* @__PURE__ */ jsx17(
18701
+ ArticleMetaPanel,
18702
+ {
18703
+ meta: documentNode.meta ?? emptyDocument2Meta(),
18704
+ uiLocale,
18705
+ digitForm,
18706
+ panelRef: articleMetaPanelRef,
18707
+ onChange: (patch) => {
18708
+ clearBlockSelection();
18709
+ applyDocument(updateDocument2Meta(documentRef.current, patch), "text");
18710
+ }
18711
+ }
18712
+ ),
18713
+ documentNode.blocks.map((block, blockIndex) => /* @__PURE__ */ jsx17(
18714
+ BlockEditor,
18715
+ {
18716
+ block,
18717
+ references: documentNode.references,
18718
+ documentNode,
18719
+ documentDirection,
18720
+ digitForm,
18721
+ mathOutput,
18722
+ equationSide,
18723
+ editableEquations,
18724
+ editableCitations: !previewOnly,
18725
+ uiLocale,
18726
+ isCollapsed: collapsedBlockIds.has(block.id),
18727
+ selected: Boolean(blockSelection && blockIndex >= blockSelection.from && blockIndex <= blockSelection.to),
18728
+ onToggleSelect: toggleSelectBlock,
18729
+ onTextChange: (fieldId, tokenId, text) => {
18730
+ clearBlockSelection();
18731
+ applyDocument(updateTextToken(documentRef.current, fieldId, tokenId, text), "text");
18732
+ },
18733
+ onOpenMath: openMath,
18734
+ onDeleteMath: editableEquations ? deleteMathToken : void 0,
18735
+ onOpenCite: openCite,
18736
+ onDeleteCite: deleteCiteToken,
18737
+ onOpenRef: openRef,
18738
+ onDeleteRef: deleteRefToken,
18739
+ onToggleCollapse: toggleBlockCollapse,
18740
+ onBlockFocus: rememberBlockFocus,
18741
+ onFieldFocus: rememberFieldFocus,
18742
+ onMathFocus: rememberMathFocus,
18743
+ onFieldBlur,
18744
+ onImageSrcChange: (blockId, value) => {
18745
+ clearBlockSelection();
18746
+ applyDocument(updateDocument2ImageValue(documentRef.current, blockId, value), "text");
18747
+ },
18748
+ onImageAssetChange: (blockId, asset) => {
18749
+ clearBlockSelection();
18750
+ applyDocument(updateDocument2ImageAsset(documentRef.current, blockId, asset), "immediate");
18751
+ },
18752
+ onImageAssetClear: (blockId) => {
18753
+ clearBlockSelection();
18754
+ applyDocument(clearDocument2ImageAsset(documentRef.current, blockId), "immediate");
18755
+ },
18756
+ resolveImageUrl,
18757
+ onRequestImagePick,
18758
+ listImageAssets,
18759
+ renderImageBlockEditor,
18760
+ onFloatMetaChange: (blockId, kind, patch) => {
18761
+ clearBlockSelection();
18762
+ applyDocument(
18763
+ kind === "image" ? updateDocument2ImageMeta(documentRef.current, blockId, patch) : updateDocument2TableMeta(documentRef.current, blockId, patch),
18764
+ "text"
18765
+ );
18766
+ },
18767
+ onParagraphCenteredChange: (blockId, centered) => {
18768
+ clearBlockSelection();
18769
+ applyDocument(updateDocument2TextBlockCentered(documentRef.current, blockId, centered), "text");
18770
+ },
18771
+ onAddListItem: (listBlockId) => {
18772
+ clearBlockSelection();
18773
+ applyDocument(addDocument2ListItem(documentRef.current, listBlockId), "immediate");
18774
+ },
18775
+ onRemoveListItem: (listBlockId, itemId) => {
18776
+ clearBlockSelection();
18777
+ applyDocument(removeDocument2ListItem(documentRef.current, listBlockId, itemId), "immediate");
18778
+ },
18779
+ onManageReferences: () => setReferencesOpen(true)
18780
+ },
18781
+ block.id
18782
+ ))
18783
+ ] })
18784
+ }
18785
+ ) : null,
18786
+ /* @__PURE__ */ jsx17(
18787
+ "section",
18788
+ {
18789
+ className: "butex-document2-widget__panel butex-document2-widget__preview-panel",
18790
+ "aria-label": messages.documentPreview,
18791
+ "aria-hidden": !showPreviewPanel,
18792
+ children: /* @__PURE__ */ jsx17(
18793
+ DocumentPreview,
18794
+ {
18795
+ blocks: preview.blocks,
18796
+ output: mathOutput,
18797
+ documentDirection,
18798
+ uiLocale,
18799
+ digitForm,
18800
+ resolveImageUrl
18801
+ }
18802
+ )
18803
+ }
18804
+ )
18805
+ ]
18806
+ }
18807
+ ),
18808
+ debugEnabled ? /* @__PURE__ */ jsxs15("div", { className: "butex-document2-widget__dev", children: [
18809
+ debugEnabled && documentNode.diagnostics.length > 0 ? /* @__PURE__ */ jsxs15(Fragment5, { children: [
18810
+ /* @__PURE__ */ jsx17("strong", { children: messages.importWarnings }),
18811
+ documentNode.diagnostics.map((diagnostic) => /* @__PURE__ */ jsx17("p", { className: "butex-document2-widget__dev-diagnostic", children: formatDocument2Diagnostic(diagnostic, messages) }, `${diagnostic.path}-${diagnostic.message}`))
18812
+ ] }) : null,
18434
18813
  /* @__PURE__ */ jsxs15("div", { className: "butex-document2-widget__dev-actions", children: [
18435
- /* @__PURE__ */ jsx17("button", { type: "button", onClick: applyDebugJsonImport, children: "Apply" }),
18814
+ /* @__PURE__ */ jsx17("button", { type: "button", onClick: () => void copyDebugText(JSON.stringify(documentJson, null, 2)), children: "Copy JSON" }),
18815
+ /* @__PURE__ */ jsx17("button", { type: "button", onClick: () => void copyDebugText(latex), children: "Copy TeX" }),
18816
+ /* @__PURE__ */ jsx17("button", { type: "button", "aria-expanded": debugImportOpen, onClick: openDebugJsonImport, children: "Import JSON" })
18817
+ ] }),
18818
+ debugImportOpen ? /* @__PURE__ */ jsxs15("div", { className: "butex-document2-widget__dev-import", children: [
18436
18819
  /* @__PURE__ */ jsx17(
18437
- "button",
18820
+ "textarea",
18438
18821
  {
18439
- type: "button",
18440
- onClick: () => {
18441
- setDebugImportOpen(false);
18822
+ value: debugImportText,
18823
+ "aria-label": "Import Document2 JSON",
18824
+ spellCheck: false,
18825
+ onChange: (event) => {
18826
+ setDebugImportText(event.currentTarget.value);
18442
18827
  setDebugImportError("");
18443
- },
18444
- children: "Cancel"
18828
+ }
18445
18829
  }
18446
- )
18447
- ] }),
18448
- debugImportError ? /* @__PURE__ */ jsx17("p", { className: "butex-document2-widget__dev-error", children: debugImportError }) : null
18830
+ ),
18831
+ /* @__PURE__ */ jsxs15("div", { className: "butex-document2-widget__dev-actions", children: [
18832
+ /* @__PURE__ */ jsx17("button", { type: "button", onClick: applyDebugJsonImport, children: "Apply" }),
18833
+ /* @__PURE__ */ jsx17(
18834
+ "button",
18835
+ {
18836
+ type: "button",
18837
+ onClick: () => {
18838
+ setDebugImportOpen(false);
18839
+ setDebugImportError("");
18840
+ },
18841
+ children: "Cancel"
18842
+ }
18843
+ )
18844
+ ] }),
18845
+ debugImportError ? /* @__PURE__ */ jsx17("p", { className: "butex-document2-widget__dev-error", children: debugImportError }) : null
18846
+ ] }) : null,
18847
+ /* @__PURE__ */ jsx17("strong", { children: "LaTeX" }),
18848
+ /* @__PURE__ */ jsx17("pre", { children: latex }),
18849
+ /* @__PURE__ */ jsx17("strong", { children: "JSON" }),
18850
+ /* @__PURE__ */ jsx17("pre", { children: JSON.stringify(documentJson, null, 2) }),
18851
+ /* @__PURE__ */ jsx17("strong", { children: "AST" }),
18852
+ /* @__PURE__ */ jsx17("pre", { children: JSON.stringify(documentNode, null, 2) })
18449
18853
  ] }) : null,
18450
- /* @__PURE__ */ jsx17("strong", { children: "LaTeX" }),
18451
- /* @__PURE__ */ jsx17("pre", { children: latex }),
18452
- /* @__PURE__ */ jsx17("strong", { children: "JSON" }),
18453
- /* @__PURE__ */ jsx17("pre", { children: JSON.stringify(documentJson, null, 2) }),
18454
- /* @__PURE__ */ jsx17("strong", { children: "AST" }),
18455
- /* @__PURE__ */ jsx17("pre", { children: JSON.stringify(documentNode, null, 2) })
18456
- ] }) : null,
18457
- !previewOnly && selectedMath ? /* @__PURE__ */ jsx17(
18458
- EquationDrawer,
18459
- {
18460
- debug: debugEnabled,
18461
- session: selectedMath.session,
18462
- reason: selectedMath.reason,
18463
- mathMode: selectedMath.mathMode,
18464
- canDelete: Boolean(selectedMath.tokenId),
18465
- equationSide,
18466
- uiLocale,
18467
- labelEnabled: selectedMath.labelEnabled,
18468
- label: selectedMath.label,
18469
- labels: documentLabels,
18470
- references: documentNode.references,
18471
- ownerId: selectedMath.tokenId ?? void 0,
18472
- onLabelEnabledChange: (enabled) => setSelectedMath((current) => current ? { ...current, labelEnabled: enabled } : current),
18473
- onLabelChange: (nextLabel) => setSelectedMath((current) => current ? { ...current, label: nextLabel } : current),
18474
- onMathModeChange: (mode) => setSelectedMath((current) => current ? { ...current, mathMode: mode } : current),
18475
- onClose: () => setSelectedMath(null),
18476
- onSave: saveEquation,
18477
- onDelete: deleteSelectedEquation
18478
- }
18479
- ) : null,
18480
- !previewOnly ? /* @__PURE__ */ jsx17(
18481
- CitePickerPopover,
18482
- {
18483
- open: citePicker !== null,
18484
- references: documentNode.references,
18485
- initialKeys: citePicker?.keys ?? [],
18486
- uiLocale,
18487
- digitForm,
18488
- onClose: () => setCitePicker(null),
18489
- onConfirm: confirmCiteKeys,
18490
- onManageReferences: () => {
18491
- setCitePicker(null);
18492
- setReferencesOpen(true);
18854
+ !previewOnly && selectedMath ? /* @__PURE__ */ jsx17(
18855
+ EquationDrawer,
18856
+ {
18857
+ debug: debugEnabled,
18858
+ session: selectedMath.session,
18859
+ reason: selectedMath.reason,
18860
+ mathMode: selectedMath.mathMode,
18861
+ canDelete: Boolean(selectedMath.tokenId),
18862
+ equationSide,
18863
+ uiLocale,
18864
+ labelEnabled: selectedMath.labelEnabled,
18865
+ label: selectedMath.label,
18866
+ labels: documentLabels,
18867
+ references: documentNode.references,
18868
+ ownerId: selectedMath.tokenId ?? void 0,
18869
+ onLabelEnabledChange: (enabled) => setSelectedMath((current) => current ? { ...current, labelEnabled: enabled } : current),
18870
+ onLabelChange: (nextLabel) => setSelectedMath((current) => current ? { ...current, label: nextLabel } : current),
18871
+ onMathModeChange: (mode) => setSelectedMath((current) => current ? { ...current, mathMode: mode } : current),
18872
+ onClose: () => setSelectedMath(null),
18873
+ onSave: saveEquation,
18874
+ onDelete: deleteSelectedEquation
18493
18875
  }
18494
- }
18495
- ) : null,
18496
- !previewOnly ? /* @__PURE__ */ jsx17(
18497
- RefPickerPopover,
18498
- {
18499
- open: refPicker !== null,
18500
- labels: documentLabels,
18501
- initialKeys: refPicker?.keys ?? [],
18502
- initialRefCommand: refPicker?.refCommand ?? "ref",
18503
- uiLocale,
18504
- digitForm,
18505
- onClose: () => setRefPicker(null),
18506
- onConfirm: confirmRefKeys,
18507
- onManageLabels: () => {
18508
- setRefPicker(null);
18509
- setLabelsOpen(true);
18876
+ ) : null,
18877
+ !previewOnly ? /* @__PURE__ */ jsx17(
18878
+ CitePickerPopover,
18879
+ {
18880
+ open: citePicker !== null,
18881
+ references: documentNode.references,
18882
+ initialKeys: citePicker?.keys ?? [],
18883
+ uiLocale,
18884
+ digitForm,
18885
+ onClose: () => setCitePicker(null),
18886
+ onConfirm: confirmCiteKeys,
18887
+ onManageReferences: () => {
18888
+ setCitePicker(null);
18889
+ setReferencesOpen(true);
18890
+ }
18510
18891
  }
18511
- }
18512
- ) : null,
18513
- !previewOnly ? /* @__PURE__ */ jsx17(
18514
- ReferencesPanel,
18515
- {
18516
- open: referencesOpen,
18517
- references: documentNode.references,
18518
- labels: documentLabels,
18519
- uiLocale,
18520
- digitForm,
18521
- onClose: () => setReferencesOpen(false),
18522
- onAdd: (partial) => applyDocument(addDocument2Reference(documentRef.current, partial), "immediate"),
18523
- onUpdate: (referenceId, patch) => applyDocument(updateDocument2Reference(documentRef.current, referenceId, patch), "immediate"),
18524
- onRemove: (referenceId) => applyDocument(removeDocument2Reference(documentRef.current, referenceId), "immediate"),
18525
- onMove: (referenceId, direction) => applyDocument(moveDocument2Reference(documentRef.current, referenceId, direction), "immediate")
18526
- }
18527
- ) : null,
18528
- !previewOnly ? /* @__PURE__ */ jsx17(
18529
- LabelsPanel,
18530
- {
18531
- open: labelsOpen,
18532
- labels: documentLabels,
18533
- references: documentNode.references,
18534
- uiLocale,
18535
- digitForm,
18536
- onClose: () => setLabelsOpen(false),
18537
- onUpdateFloat: (ownerId, kind, patch) => applyDocument(
18538
- kind === "fig" ? updateDocument2ImageMeta(documentRef.current, ownerId, patch) : updateDocument2TableMeta(documentRef.current, ownerId, patch),
18539
- "text"
18540
- ),
18541
- onUpdateEquationLabel: (tokenId, patch) => applyDocument(updateMathTokenLabel(documentRef.current, tokenId, patch), "text")
18542
- }
18543
- ) : null
18544
- ] })
18545
- }
18546
- );
18547
- }
18892
+ ) : null,
18893
+ !previewOnly ? /* @__PURE__ */ jsx17(
18894
+ RefPickerPopover,
18895
+ {
18896
+ open: refPicker !== null,
18897
+ labels: documentLabels,
18898
+ initialKeys: refPicker?.keys ?? [],
18899
+ initialRefCommand: refPicker?.refCommand ?? "ref",
18900
+ uiLocale,
18901
+ digitForm,
18902
+ onClose: () => setRefPicker(null),
18903
+ onConfirm: confirmRefKeys,
18904
+ onManageLabels: () => {
18905
+ setRefPicker(null);
18906
+ setLabelsOpen(true);
18907
+ }
18908
+ }
18909
+ ) : null,
18910
+ !previewOnly ? /* @__PURE__ */ jsx17(
18911
+ ReferencesPanel,
18912
+ {
18913
+ open: referencesOpen,
18914
+ references: documentNode.references,
18915
+ labels: documentLabels,
18916
+ uiLocale,
18917
+ digitForm,
18918
+ onClose: () => setReferencesOpen(false),
18919
+ onAdd: (partial) => applyDocument(addDocument2Reference(documentRef.current, partial), "immediate"),
18920
+ onUpdate: (referenceId, patch) => applyDocument(updateDocument2Reference(documentRef.current, referenceId, patch), "immediate"),
18921
+ onRemove: (referenceId) => applyDocument(removeDocument2Reference(documentRef.current, referenceId), "immediate"),
18922
+ onMove: (referenceId, direction) => applyDocument(moveDocument2Reference(documentRef.current, referenceId, direction), "immediate")
18923
+ }
18924
+ ) : null,
18925
+ !previewOnly ? /* @__PURE__ */ jsx17(
18926
+ LabelsPanel,
18927
+ {
18928
+ open: labelsOpen,
18929
+ labels: documentLabels,
18930
+ references: documentNode.references,
18931
+ uiLocale,
18932
+ digitForm,
18933
+ onClose: () => setLabelsOpen(false),
18934
+ onUpdateFloat: (ownerId, kind, patch) => applyDocument(
18935
+ kind === "fig" ? updateDocument2ImageMeta(documentRef.current, ownerId, patch) : updateDocument2TableMeta(documentRef.current, ownerId, patch),
18936
+ "text"
18937
+ ),
18938
+ onUpdateEquationLabel: (tokenId, patch) => applyDocument(updateMathTokenLabel(documentRef.current, tokenId, patch), "text")
18939
+ }
18940
+ ) : null
18941
+ ] })
18942
+ }
18943
+ );
18944
+ }
18945
+ );
18946
+ ButexDocumentEditor2.displayName = "ButexDocumentEditor2";
18548
18947
  export {
18549
18948
  ButexDocumentEditor2,
18550
18949
  DOCUMENT2_WIDGET_CSS,