@drghaliasri/butex 5.6.1 → 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 { forwardRef as forwardRef2, useCallback as useCallback2, useEffect as useEffect9, useImperativeHandle as useImperativeHandle2, 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
  }
@@ -17615,6 +17970,9 @@ var ButexDocumentEditor2 = forwardRef2(
17615
17970
  digitForm: digitFormProp,
17616
17971
  onDigitFormChange,
17617
17972
  resolveImageUrl,
17973
+ onRequestImagePick,
17974
+ listImageAssets,
17975
+ renderImageBlockEditor,
17618
17976
  onDocumentChange,
17619
17977
  onDocumentJsonChange,
17620
17978
  onLatexChange
@@ -17721,15 +18079,15 @@ var ButexDocumentEditor2 = forwardRef2(
17721
18079
  applyDocument(removeDocument2BlockRange(documentRef.current, selection.from, selection.to), "immediate");
17722
18080
  clearBlockSelection();
17723
18081
  }
17724
- useEffect9(() => {
18082
+ useEffect10(() => {
17725
18083
  injectBuTeXDocument2Styles();
17726
18084
  }, []);
17727
- useEffect9(() => {
18085
+ useEffect10(() => {
17728
18086
  if (!editableEquations || previewOnly) {
17729
18087
  setSelectedMath(null);
17730
18088
  }
17731
18089
  }, [editableEquations, previewOnly]);
17732
- useEffect9(() => {
18090
+ useEffect10(() => {
17733
18091
  const next = resolveInitialDocument2(initialDocument, uiLocale, documentMeta);
17734
18092
  setDocumentNode(next.document);
17735
18093
  setError(next.error);
@@ -17738,16 +18096,16 @@ var ButexDocumentEditor2 = forwardRef2(
17738
18096
  historyRef.current = createDocument2History();
17739
18097
  setHistoryTick((tick) => tick + 1);
17740
18098
  }, [initialDocument, documentMeta, uiLocale]);
17741
- useEffect9(() => {
18099
+ useEffect10(() => {
17742
18100
  onDocumentChange?.(documentNode);
17743
18101
  }, [documentNode, onDocumentChange]);
17744
- useEffect9(() => {
18102
+ useEffect10(() => {
17745
18103
  onDocumentJsonChange?.(documentJson);
17746
18104
  }, [documentJson, onDocumentJsonChange]);
17747
- useEffect9(() => {
18105
+ useEffect10(() => {
17748
18106
  onLatexChange?.(latex);
17749
18107
  }, [latex, onLatexChange]);
17750
- useEffect9(() => {
18108
+ useEffect10(() => {
17751
18109
  const pending = pendingFocusRef.current;
17752
18110
  const root = widgetRef.current;
17753
18111
  if (!pending || !root) {
@@ -17815,10 +18173,10 @@ var ButexDocumentEditor2 = forwardRef2(
17815
18173
  );
17816
18174
  const afterBlockId = useCallback2(() => resolveInsertAfterBlockId(documentRef.current, editorFocusRef.current), []);
17817
18175
  const insertImageBlock = useCallback2(
17818
- (srcOrAssetId = "") => {
18176
+ (srcOrAsset = "") => {
17819
18177
  clearBlockSelection();
17820
18178
  const after = resolveInsertAfterBlockId(documentRef.current, editorFocusRef.current);
17821
- applyDocument(addDocument2ImageBlock(documentRef.current, srcOrAssetId, after), "immediate");
18179
+ applyDocument(addDocument2ImageBlock(documentRef.current, srcOrAsset, after), "immediate");
17822
18180
  },
17823
18181
  [applyDocument]
17824
18182
  );
@@ -17829,6 +18187,9 @@ var ButexDocumentEditor2 = forwardRef2(
17829
18187
  updateImageBlockValue(blockId, value) {
17830
18188
  applyDocument(updateDocument2ImageValue(documentRef.current, blockId, value), "immediate");
17831
18189
  },
18190
+ updateImageBlockAsset(blockId, asset) {
18191
+ applyDocument(updateDocument2ImageAsset(documentRef.current, blockId, asset), "immediate");
18192
+ },
17832
18193
  getDocumentJson() {
17833
18194
  return toDocumentJson2(documentRef.current);
17834
18195
  }
@@ -17927,7 +18288,7 @@ var ButexDocumentEditor2 = forwardRef2(
17927
18288
  setDebugImportError(importError instanceof Error ? importError.message : String(importError));
17928
18289
  }
17929
18290
  }
17930
- useEffect9(() => {
18291
+ useEffect10(() => {
17931
18292
  const root = widgetRef.current;
17932
18293
  if (!root) {
17933
18294
  return;
@@ -18384,6 +18745,18 @@ var ButexDocumentEditor2 = forwardRef2(
18384
18745
  clearBlockSelection();
18385
18746
  applyDocument(updateDocument2ImageValue(documentRef.current, blockId, value), "text");
18386
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,
18387
18760
  onFloatMetaChange: (blockId, kind, patch) => {
18388
18761
  clearBlockSelection();
18389
18762
  applyDocument(
@@ -18433,7 +18806,7 @@ var ButexDocumentEditor2 = forwardRef2(
18433
18806
  }
18434
18807
  ),
18435
18808
  debugEnabled ? /* @__PURE__ */ jsxs15("div", { className: "butex-document2-widget__dev", children: [
18436
- debugEnabled && documentNode.diagnostics.length > 0 ? /* @__PURE__ */ jsxs15(Fragment4, { children: [
18809
+ debugEnabled && documentNode.diagnostics.length > 0 ? /* @__PURE__ */ jsxs15(Fragment5, { children: [
18437
18810
  /* @__PURE__ */ jsx17("strong", { children: messages.importWarnings }),
18438
18811
  documentNode.diagnostics.map((diagnostic) => /* @__PURE__ */ jsx17("p", { className: "butex-document2-widget__dev-diagnostic", children: formatDocument2Diagnostic(diagnostic, messages) }, `${diagnostic.path}-${diagnostic.message}`))
18439
18812
  ] }) : null,