@drghaliasri/butex 5.3.1 → 5.3.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -268,6 +268,10 @@ function cloneDocument2Meta(meta) {
268
268
  }
269
269
 
270
270
  // src/document2/citations.ts
271
+ var DEFAULT_REFERENCE_FIELD_SEPARATOR = "\u060C";
272
+ function normalizeReferenceFieldSeparator(value) {
273
+ return value === "," ? "," : DEFAULT_REFERENCE_FIELD_SEPARATOR;
274
+ }
271
275
  function referenceNumberMap(references) {
272
276
  const map = /* @__PURE__ */ new Map();
273
277
  references.forEach((reference, index) => {
@@ -313,7 +317,8 @@ function referenceFromJson(json) {
313
317
  title: typeof json.title === "string" ? json.title : "",
314
318
  year: typeof json.year === "string" ? json.year : "",
315
319
  url: typeof json.url === "string" ? json.url : "",
316
- venue: typeof json.venue === "string" ? json.venue : ""
320
+ venue: typeof json.venue === "string" ? json.venue : "",
321
+ fieldSeparator: normalizeReferenceFieldSeparator(json.field_separator)
317
322
  };
318
323
  }
319
324
  function createEmptyReference2(partial = {}) {
@@ -323,15 +328,33 @@ function createEmptyReference2(partial = {}) {
323
328
  title: partial.title,
324
329
  year: partial.year,
325
330
  url: partial.url,
326
- venue: partial.venue
331
+ venue: partial.venue,
332
+ field_separator: partial.field_separator
327
333
  });
328
334
  }
335
+ function bibliographyEntryBody(reference, separator = reference.fieldSeparator, options = {}) {
336
+ const year = options.digitForm !== void 0 ? formatDigits(reference.year, options.digitForm) : reference.year;
337
+ const parts = [reference.authors, reference.title, reference.venue, year].filter((part) => part.trim().length > 0);
338
+ return parts.join(`${separator} `);
339
+ }
329
340
  function bibliographyEntryLatex(reference) {
330
- const parts = [reference.authors, reference.title, reference.venue, reference.year].filter((part) => part.trim().length > 0);
331
- const body = parts.join(", ");
341
+ const body = bibliographyEntryBody(reference);
332
342
  const url = reference.url.trim().length > 0 ? ` \\url{${reference.url}}` : "";
333
343
  return `\\bibitem{${reference.key}} ${body}${url}`.trim();
334
344
  }
345
+ function absoluteHttpHref(url) {
346
+ const trimmed = url.trim();
347
+ if (trimmed.length === 0) {
348
+ return "";
349
+ }
350
+ if (/^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(trimmed)) {
351
+ return trimmed;
352
+ }
353
+ if (trimmed.startsWith("//")) {
354
+ return `https:${trimmed}`;
355
+ }
356
+ return `https://${trimmed}`;
357
+ }
335
358
 
336
359
  // src/ast/commands/index.ts
337
360
  function renderCommandCore(context) {
@@ -816,6 +839,23 @@ function labelNumberMap(document2) {
816
839
  }
817
840
  return map;
818
841
  }
842
+ function formatFloatCaptionTitle(kind, number, options = {}) {
843
+ const locale = options.uiLocale ?? "ar";
844
+ const kindWord = kind === "fig" ? locale === "ar" ? "\u0627\u0644\u0634\u0643\u0644" : "Figure" : kind === "tab" ? locale === "ar" ? "\u0627\u0644\u062C\u062F\u0648\u0644" : "Table" : locale === "ar" ? "\u0627\u0644\u0645\u0639\u0627\u062F\u0644\u0629" : "Equation";
845
+ const digits = formatBibliographyNumber(number, {
846
+ documentDirection: options.documentDirection ?? (locale === "ar" ? "rtl" : "ltr"),
847
+ digitForm: options.digitForm ?? (locale === "ar" ? "arabicIndic" : "western")
848
+ });
849
+ return `${kindWord} ${digits}`;
850
+ }
851
+ function formatFloatCaptionPreview(title, caption, uiLocale = "ar") {
852
+ const trimmed = caption?.trim() ?? "";
853
+ if (trimmed.length === 0) {
854
+ return title;
855
+ }
856
+ const separator = uiLocale === "ar" ? ": " : ". ";
857
+ return `${title}${separator}${trimmed}`;
858
+ }
819
859
  function formatRefLabel(keys, document2, refCommand, options = {}) {
820
860
  const documentDirection = options.documentDirection ?? "rtl";
821
861
  const digitForm = options.digitForm ?? (documentDirection === "rtl" ? "arabicIndic" : "western");
@@ -1086,7 +1126,8 @@ function parseReferences(json) {
1086
1126
  title: typeof entry.title === "string" ? entry.title : void 0,
1087
1127
  year: typeof entry.year === "string" ? entry.year : void 0,
1088
1128
  url: typeof entry.url === "string" ? entry.url : void 0,
1089
- venue: typeof entry.venue === "string" ? entry.venue : void 0
1129
+ venue: typeof entry.venue === "string" ? entry.venue : void 0,
1130
+ field_separator: entry.field_separator === "," || entry.field_separator === "\u060C" ? entry.field_separator : void 0
1090
1131
  })
1091
1132
  );
1092
1133
  }
@@ -1732,7 +1773,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
1732
1773
  id: "neq",
1733
1774
  category: "operators1",
1734
1775
  Tex: "\\neq",
1735
- mirror: false,
1776
+ mirror: true,
1736
1777
  Label: "\u2260",
1737
1778
  title: "\u0644\u0627 \u064A\u0633\u0627\u0648\u064A",
1738
1779
  svg_path: null
@@ -1742,6 +1783,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
1742
1783
  category: "operators1",
1743
1784
  Tex: "\\approx",
1744
1785
  mirror: false,
1786
+ displayMirror: true,
1745
1787
  Label: "\u2248",
1746
1788
  title: "\u062A\u0642\u0631\u064A\u0628\u0627 \u064A\u0633\u0627\u0648\u064A",
1747
1789
  svg_path: null
@@ -1751,6 +1793,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
1751
1793
  category: "operators1",
1752
1794
  Tex: "\\sim",
1753
1795
  mirror: false,
1796
+ displayMirror: true,
1754
1797
  Label: "\u223C",
1755
1798
  title: "\u0645\u0634\u0627\u0628\u0647",
1756
1799
  svg_path: null
@@ -1769,6 +1812,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
1769
1812
  category: "operators1",
1770
1813
  Tex: "\\leq",
1771
1814
  mirror: true,
1815
+ displayMirror: false,
1772
1816
  Label: "\u2264",
1773
1817
  title: "\u0623\u0635\u063A\u0631 \u0645\u0646 \u0623\u0648 \u064A\u0633\u0627\u0648\u064A",
1774
1818
  svg_path: null
@@ -1778,6 +1822,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
1778
1822
  category: "operators1",
1779
1823
  Tex: "\\geq",
1780
1824
  mirror: true,
1825
+ displayMirror: false,
1781
1826
  Label: "\u2265",
1782
1827
  title: "\u0623\u0643\u0628\u0631 \u0645\u0646 \u0623\u0648 \u064A\u0633\u0627\u0648\u064A",
1783
1828
  svg_path: null
@@ -1787,6 +1832,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
1787
1832
  category: "operators1",
1788
1833
  Tex: "\\coloneqq",
1789
1834
  mirror: true,
1835
+ displayMirror: false,
1790
1836
  Label: "\u2254",
1791
1837
  title: "\u064A\u0639\u0631\u0641 \u0628\u0623\u0646\u0647 \u064A\u0633\u0627\u0648\u064A",
1792
1838
  svg_path: null
@@ -1796,6 +1842,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
1796
1842
  category: "operators1",
1797
1843
  Tex: "\\eqqcolon",
1798
1844
  mirror: true,
1845
+ displayMirror: false,
1799
1846
  Label: "\u2255",
1800
1847
  title: "\u064A\u0633\u0627\u0648\u064A \u0628\u0627\u0644\u062A\u0639\u0631\u064A\u0641",
1801
1848
  svg_path: null
@@ -1806,6 +1853,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
1806
1853
  category: "operators2",
1807
1854
  Tex: "\\propto",
1808
1855
  mirror: true,
1856
+ displayMirror: false,
1809
1857
  Label: "\u221D",
1810
1858
  title: "\u064A\u062A\u0646\u0627\u0633\u0628 \u0645\u0639",
1811
1859
  svg_path: null
@@ -1815,6 +1863,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
1815
1863
  category: "operators2",
1816
1864
  Tex: "\\in",
1817
1865
  mirror: true,
1866
+ displayMirror: false,
1818
1867
  Label: "\u2208",
1819
1868
  title: "\u064A\u0646\u062A\u0645\u064A \u0625\u0644\u0649",
1820
1869
  svg_path: null
@@ -1824,6 +1873,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
1824
1873
  category: "operators2",
1825
1874
  Tex: "\\notin",
1826
1875
  mirror: false,
1876
+ displayMirror: true,
1827
1877
  Label: "\u2209",
1828
1878
  title: "\u0644\u0627 \u064A\u0646\u062A\u0645\u064A \u0625\u0644\u0649",
1829
1879
  svg_path: null
@@ -1833,6 +1883,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
1833
1883
  category: "operators2",
1834
1884
  Tex: "\\subset",
1835
1885
  mirror: true,
1886
+ displayMirror: false,
1836
1887
  Label: "\u2282",
1837
1888
  title: "\u0645\u062C\u0645\u0648\u0639\u0629 \u062C\u0632\u0626\u064A\u0629 \u0645\u0646",
1838
1889
  svg_path: null
@@ -1842,6 +1893,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
1842
1893
  category: "operators2",
1843
1894
  Tex: "\\supset",
1844
1895
  mirror: true,
1896
+ displayMirror: false,
1845
1897
  Label: "\u2283",
1846
1898
  title: "\u0645\u062C\u0645\u0648\u0639\u0629 \u0634\u0627\u0645\u0644\u0629 \u0644\u0640",
1847
1899
  svg_path: null
@@ -1851,6 +1903,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
1851
1903
  category: "operators2",
1852
1904
  Tex: "\\subseteq",
1853
1905
  mirror: true,
1906
+ displayMirror: false,
1854
1907
  Label: "\u2286",
1855
1908
  title: "\u0645\u062C\u0645\u0648\u0639\u0629 \u062C\u0632\u0626\u064A\u0629 \u0623\u0648 \u062A\u0633\u0627\u0648\u064A",
1856
1909
  svg_path: null
@@ -1860,6 +1913,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
1860
1913
  category: "operators2",
1861
1914
  Tex: "\\supseteq",
1862
1915
  mirror: true,
1916
+ displayMirror: false,
1863
1917
  Label: "\u2287",
1864
1918
  title: "\u0645\u062C\u0645\u0648\u0639\u0629 \u0634\u0627\u0645\u0644\u0629 \u0623\u0648 \u062A\u0633\u0627\u0648\u064A",
1865
1919
  svg_path: null
@@ -1869,6 +1923,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
1869
1923
  category: "operators2",
1870
1924
  Tex: "\\cap",
1871
1925
  mirror: false,
1926
+ displayMirror: true,
1872
1927
  Label: "\u2229",
1873
1928
  title: "\u062A\u0642\u0627\u0637\u0639",
1874
1929
  svg_path: null
@@ -1878,6 +1933,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
1878
1933
  category: "operators2",
1879
1934
  Tex: "\\cup",
1880
1935
  mirror: false,
1936
+ displayMirror: true,
1881
1937
  Label: "\u222A",
1882
1938
  title: "\u0627\u062A\u062D\u0627\u062F",
1883
1939
  svg_path: null
@@ -1887,6 +1943,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
1887
1943
  category: "operators2",
1888
1944
  Tex: "\\emptyset",
1889
1945
  mirror: false,
1946
+ displayMirror: true,
1890
1947
  Label: "\u2205",
1891
1948
  title: "\u0627\u0644\u0645\u062C\u0645\u0648\u0639\u0629 \u0627\u0644\u062E\u0627\u0644\u064A\u0629",
1892
1949
  svg_path: null
@@ -1896,6 +1953,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
1896
1953
  category: "operators2",
1897
1954
  Tex: "\\nsubseteq",
1898
1955
  mirror: true,
1956
+ displayMirror: false,
1899
1957
  Label: "\u2288",
1900
1958
  title: "\u0644\u064A\u0633\u062A \u0645\u062C\u0645\u0648\u0639\u0629 \u062C\u0632\u0626\u064A\u0629 \u0623\u0648 \u062A\u0633\u0627\u0648\u064A",
1901
1959
  svg_path: null
@@ -1905,6 +1963,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
1905
1963
  category: "operators2",
1906
1964
  Tex: "\\nsupseteq",
1907
1965
  mirror: true,
1966
+ displayMirror: false,
1908
1967
  Label: "\u2289",
1909
1968
  title: "\u0644\u064A\u0633\u062A \u0645\u062C\u0645\u0648\u0639\u0629 \u0634\u0627\u0645\u0644\u0629 \u0623\u0648 \u062A\u0633\u0627\u0648\u064A",
1910
1969
  svg_path: null
@@ -2116,6 +2175,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
2116
2175
  category: "integrals",
2117
2176
  Tex: "\\iint",
2118
2177
  mirror: true,
2178
+ displayMirror: false,
2119
2179
  Label: "\u222C",
2120
2180
  title: "\u062A\u0643\u0627\u0645\u0644 \u0645\u0632\u062F\u0648\u062C",
2121
2181
  svg_path: null
@@ -2125,6 +2185,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
2125
2185
  category: "integrals",
2126
2186
  Tex: "\\iiint",
2127
2187
  mirror: true,
2188
+ displayMirror: false,
2128
2189
  Label: "\u222D",
2129
2190
  title: "\u062A\u0643\u0627\u0645\u0644 \u062B\u0644\u0627\u062B\u064A",
2130
2191
  svg_path: null
@@ -2134,6 +2195,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
2134
2195
  category: "integrals",
2135
2196
  Tex: "\\iiiint",
2136
2197
  mirror: true,
2198
+ displayMirror: false,
2137
2199
  Label: "\u2A0C",
2138
2200
  title: "\u062A\u0643\u0627\u0645\u0644 \u0631\u0628\u0627\u0639\u064A",
2139
2201
  svg_path: null
@@ -2143,6 +2205,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
2143
2205
  category: "integrals",
2144
2206
  Tex: "\\oint",
2145
2207
  mirror: true,
2208
+ displayMirror: false,
2146
2209
  Label: "\u222E",
2147
2210
  title: "\u062A\u0643\u0627\u0645\u0644 \u0645\u063A\u0644\u0642",
2148
2211
  svg_path: null
@@ -2152,6 +2215,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
2152
2215
  category: "integrals",
2153
2216
  Tex: "\\oiint",
2154
2217
  mirror: true,
2218
+ displayMirror: false,
2155
2219
  Label: "\u222F",
2156
2220
  title: "\u062A\u0643\u0627\u0645\u0644 \u0633\u0637\u062D\u064A \u0645\u063A\u0644\u0642",
2157
2221
  svg_path: null
@@ -2161,6 +2225,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
2161
2225
  category: "integrals",
2162
2226
  Tex: "\\oiiint",
2163
2227
  mirror: true,
2228
+ displayMirror: false,
2164
2229
  Label: "\u2230",
2165
2230
  title: "\u062A\u0643\u0627\u0645\u0644 \u062D\u062C\u0645\u064A \u0645\u063A\u0644\u0642",
2166
2231
  svg_path: null
@@ -7518,6 +7583,9 @@ function updateDocument2Reference(document2, referenceId, patch) {
7518
7583
  if (typeof patch.venue === "string") {
7519
7584
  reference.venue = patch.venue;
7520
7585
  }
7586
+ if (patch.field_separator === "," || patch.field_separator === "\u060C") {
7587
+ reference.fieldSeparator = patch.field_separator;
7588
+ }
7521
7589
  return next;
7522
7590
  }
7523
7591
  function removeDocument2Reference(document2, referenceId) {
@@ -7551,6 +7619,48 @@ function updateDocument2Meta(document2, patch) {
7551
7619
  return next;
7552
7620
  }
7553
7621
 
7622
+ // src/document2/keys.ts
7623
+ var DOCUMENT2_KEY_PATTERN = /^[\p{L}\p{M}0-9:._-]+$/u;
7624
+ function normalizeDocument2Key(key) {
7625
+ return key.normalize("NFC").trim();
7626
+ }
7627
+ function isDuplicateDocument2Key(key, namespace) {
7628
+ const normalized = normalizeDocument2Key(key);
7629
+ if (normalized.length === 0) {
7630
+ return false;
7631
+ }
7632
+ for (const reference of namespace.references ?? []) {
7633
+ if (namespace.excludeReferenceId && reference.id === namespace.excludeReferenceId) {
7634
+ continue;
7635
+ }
7636
+ if (normalizeDocument2Key(reference.key) === normalized) {
7637
+ return true;
7638
+ }
7639
+ }
7640
+ for (const label of namespace.labels ?? []) {
7641
+ if (namespace.excludeOwnerId && label.ownerId === namespace.excludeOwnerId) {
7642
+ continue;
7643
+ }
7644
+ if (normalizeDocument2Key(label.key) === normalized) {
7645
+ return true;
7646
+ }
7647
+ }
7648
+ return false;
7649
+ }
7650
+ function document2KeyError(key, namespace = {}) {
7651
+ const normalized = normalizeDocument2Key(key);
7652
+ if (normalized.length === 0) {
7653
+ return "empty";
7654
+ }
7655
+ if (!DOCUMENT2_KEY_PATTERN.test(normalized)) {
7656
+ return "invalid";
7657
+ }
7658
+ if (isDuplicateDocument2Key(normalized, namespace)) {
7659
+ return "duplicate";
7660
+ }
7661
+ return null;
7662
+ }
7663
+
7554
7664
  // src/document2/arabicPreamble.ts
7555
7665
  function arabicXeLatexPreamblePkg(twocolumn = false) {
7556
7666
  const classOptions = twocolumn ? "12pt,a4paper,notitlepage,twocolumn" : "12pt,a4paper,notitlepage";
@@ -8101,13 +8211,11 @@ function floatNumberLabel(document2, key, previewOptions) {
8101
8211
  if (!hit || hit.kind !== "fig" && hit.kind !== "tab") {
8102
8212
  return void 0;
8103
8213
  }
8104
- const locale = previewOptions.uiLocale ?? "ar";
8105
- const kindWord = hit.kind === "fig" ? locale === "ar" ? "\u0627\u0644\u0634\u0643\u0644" : "Figure" : locale === "ar" ? "\u0627\u0644\u062C\u062F\u0648\u0644" : "Table";
8106
- const digits = formatBibliographyNumber(hit.number, {
8214
+ return formatFloatCaptionTitle(hit.kind, hit.number, {
8215
+ uiLocale: previewOptions.uiLocale ?? "ar",
8107
8216
  documentDirection: previewOptions.documentDirection,
8108
8217
  digitForm: previewOptions.digitForm
8109
8218
  });
8110
- return `${kindWord} ${digits}`;
8111
8219
  }
8112
8220
  function blockPreview(block, islands, output, equationSide, document2, previewOptions) {
8113
8221
  if (block.kind === "textBlock") {
@@ -8162,7 +8270,8 @@ function blockPreview(block, islands, output, equationSide, document2, previewOp
8162
8270
  title: reference.title,
8163
8271
  year: reference.year,
8164
8272
  url: reference.url,
8165
- venue: reference.venue
8273
+ venue: reference.venue,
8274
+ fieldSeparator: reference.fieldSeparator
8166
8275
  }))
8167
8276
  };
8168
8277
  }
@@ -8313,6 +8422,15 @@ var DOCUMENT2_MESSAGES = {
8313
8422
  labelKindFig: "\u0634\u0643\u0644",
8314
8423
  labelKindTab: "\u062C\u062F\u0648\u0644",
8315
8424
  labelKindEq: "\u0645\u0639\u0627\u062F\u0644\u0629",
8425
+ captionPreview: "\u0645\u0639\u0627\u064A\u0646\u0629 \u0627\u0644\u062A\u0639\u0644\u064A\u0642",
8426
+ referencePreview: "\u0645\u0639\u0627\u064A\u0646\u0629 \u0627\u0644\u0645\u0631\u062C\u0639",
8427
+ referenceFieldSeparator: "\u0641\u0627\u0635\u0644 \u0627\u0644\u062D\u0642\u0648\u0644",
8428
+ referenceCommaLtr: "\u0641\u0627\u0635\u0644\u0629 \u0625\u0646\u062C\u0644\u064A\u0632\u064A\u0629 ,",
8429
+ referenceCommaRtl: "\u0641\u0627\u0635\u0644\u0629 \u0639\u0631\u0628\u064A\u0629 \u060C",
8430
+ keyEmpty: "\u0627\u0644\u0645\u0641\u062A\u0627\u062D \u0645\u0637\u0644\u0648\u0628",
8431
+ keyInvalid: "\u0627\u0644\u0645\u0641\u062A\u0627\u062D \u063A\u064A\u0631 \u0635\u0627\u0644\u062D: \u0628\u062F\u0648\u0646 \u0645\u0633\u0627\u0641\u0627\u062A\u060C \u0648\u064A\u064F\u0633\u0645\u062D \u0628\u0627\u0644\u062D\u0631\u0648\u0641 \u0648\u0627\u0644\u0623\u0631\u0642\u0627\u0645 \u0648`:._-` \u0641\u0642\u0637",
8432
+ keyDuplicate: "\u0647\u0630\u0627 \u0627\u0644\u0645\u0641\u062A\u0627\u062D \u0645\u0633\u062A\u062E\u062F\u0645 \u0645\u0633\u0628\u0642\u0627\u064B",
8433
+ keyRulesHint: "\u062D\u0631\u0648\u0641 (\u0639\u0631\u0628\u064A\u0629 \u0623\u0648 \u0644\u0627\u062A\u064A\u0646\u064A\u0629) \u0648\u0623\u0631\u0642\u0627\u0645 \u0648`:._-` \u0628\u062F\u0648\u0646 \u0645\u0633\u0627\u0641\u0627\u062A",
8316
8434
  citePickerTitle: "\u0627\u062E\u062A\u064A\u0627\u0631 \u0627\u0644\u0645\u0631\u0627\u062C\u0639",
8317
8435
  noReferencesYet: "\u0644\u0627 \u062A\u0648\u062C\u062F \u0645\u0631\u0627\u062C\u0639 \u0628\u0639\u062F. \u0623\u0636\u0641 \u0645\u0631\u062C\u0639\u0627\u064B \u0623\u0648\u0644\u0627\u064B.",
8318
8436
  editCitation: "\u062A\u062D\u0631\u064A\u0631 \u0627\u0644\u0627\u0642\u062A\u0628\u0627\u0633",
@@ -8443,6 +8561,15 @@ var DOCUMENT2_MESSAGES = {
8443
8561
  labelKindFig: "Figure",
8444
8562
  labelKindTab: "Table",
8445
8563
  labelKindEq: "Equation",
8564
+ captionPreview: "Caption preview",
8565
+ referencePreview: "Reference preview",
8566
+ referenceFieldSeparator: "Field separator",
8567
+ referenceCommaLtr: "Latin comma ,",
8568
+ referenceCommaRtl: "Arabic comma \u060C",
8569
+ keyEmpty: "Key is required",
8570
+ keyInvalid: "Invalid key: letters, digits, and `:._-` only; no spaces",
8571
+ keyDuplicate: "This key is already in use",
8572
+ keyRulesHint: "Letters (Arabic or Latin), digits, and `:._-`; no spaces",
8446
8573
  citePickerTitle: "Select references",
8447
8574
  noReferencesYet: "No references yet. Add a reference first.",
8448
8575
  editCitation: "Edit citation",
@@ -8680,7 +8807,10 @@ function focusArticleMetaPanel(panel) {
8680
8807
  }
8681
8808
 
8682
8809
  // src/react-document2/ButexDocumentEditor2.tsx
8683
- import { useCallback as useCallback2, useEffect as useEffect8, useMemo, useRef as useRef7, useState as useState9 } from "react";
8810
+ import { useCallback as useCallback2, useEffect as useEffect8, useMemo as useMemo2, useRef as useRef7, useState as useState12 } from "react";
8811
+
8812
+ // src/react-document2/BlockEditor.tsx
8813
+ import { useState as useState3 } from "react";
8684
8814
 
8685
8815
  // src/react-document2/InlineField.tsx
8686
8816
  import { useRef as useRef3 } from "react";
@@ -8761,7 +8891,10 @@ import { useEffect as useEffect2, useRef as useRef2, useState as useState2 } fro
8761
8891
  // src/mathjax/svgPatcher.ts
8762
8892
  var SVG_ARABSQRT_SELECTOR = 'mjx-container[jax="SVG"] svg .mjx-rtl-mirror[data-mjx-rtl-root="true"]';
8763
8893
  var ARABSQRT_SELECTOR = '.mjx-rtl-mirror[data-mjx-rtl-root="true"]';
8894
+ var SVG_PREVIEW_MIRROR_SELECTOR = 'mjx-container[jax="SVG"] svg .butex-preview-mirror';
8895
+ var PREVIEW_MIRROR_SELECTOR = ".butex-preview-mirror";
8764
8896
  var PATCHED_ATTR = "data-butex-svg-arabsqrt";
8897
+ var MIRROR_PATCHED_ATTR = "data-butex-svg-mirror";
8765
8898
  var ROOT_INDEX_SCALE = 0.72;
8766
8899
  var ROOT_INDEX_GAP_FACTOR = 0.08;
8767
8900
  var ROOT_INDEX_MIN_GAP = 2;
@@ -8786,11 +8919,12 @@ function directSvgElementChildren(element) {
8786
8919
  return Array.from(element.children).filter((child) => child instanceof SVGElement);
8787
8920
  }
8788
8921
  function boxFor(element) {
8789
- if (!(element instanceof SVGGraphicsElement) || typeof element.getBBox !== "function") {
8922
+ const candidate = element;
8923
+ if (typeof candidate.getBBox !== "function") {
8790
8924
  return null;
8791
8925
  }
8792
8926
  try {
8793
- const box = element.getBBox();
8927
+ const box = candidate.getBBox();
8794
8928
  return {
8795
8929
  x: box.x,
8796
8930
  y: box.y,
@@ -9034,6 +9168,31 @@ function patchArabSqrtRoot(root) {
9034
9168
  root.setAttribute(PATCHED_ATTR, "patched");
9035
9169
  rootNode.setAttribute(PATCHED_ATTR, "patched");
9036
9170
  }
9171
+ function patchPreviewMirror(element) {
9172
+ if (element.getAttribute(MIRROR_PATCHED_ATTR) === "patched") {
9173
+ return;
9174
+ }
9175
+ const box = boxFor(element);
9176
+ if (!box || box.width <= 0) {
9177
+ return;
9178
+ }
9179
+ const [tx, ty] = readTranslate(element);
9180
+ const mirrorX = tx + 2 * box.x + box.width;
9181
+ element.setAttribute(
9182
+ "transform",
9183
+ `translate(${roundSvg(mirrorX)},${roundSvg(ty)}) scale(-1,1)`
9184
+ );
9185
+ element.setAttribute(MIRROR_PATCHED_ATTR, "patched");
9186
+ }
9187
+ function collectPreviewMirrors(root) {
9188
+ let mirrors = Array.from(root.querySelectorAll(SVG_PREVIEW_MIRROR_SELECTOR));
9189
+ if (!mirrors.length) {
9190
+ mirrors = Array.from(root.querySelectorAll(PREVIEW_MIRROR_SELECTOR)).filter((candidate) => {
9191
+ return candidate.closest('mjx-container[jax="SVG"]') != null;
9192
+ });
9193
+ }
9194
+ return mirrors;
9195
+ }
9037
9196
  function patchBuTeXSvgMath(root) {
9038
9197
  if (typeof root.querySelectorAll !== "function") {
9039
9198
  return;
@@ -9045,6 +9204,7 @@ function patchBuTeXSvgMath(root) {
9045
9204
  });
9046
9205
  }
9047
9206
  roots.sort((a, b) => rootDepth(b) - rootDepth(a)).forEach((arabSqrtRoot) => patchArabSqrtRoot(arabSqrtRoot));
9207
+ collectPreviewMirrors(root).forEach((mirror) => patchPreviewMirror(mirror));
9048
9208
  }
9049
9209
 
9050
9210
  // src/mathjax/renderIsland.ts
@@ -9717,6 +9877,18 @@ function inlineFieldSummary(field) {
9717
9877
  }).join("")
9718
9878
  );
9719
9879
  }
9880
+ function keyErrorMessage(error, messages) {
9881
+ if (error === "empty") {
9882
+ return messages.keyEmpty;
9883
+ }
9884
+ if (error === "invalid") {
9885
+ return messages.keyInvalid;
9886
+ }
9887
+ if (error === "duplicate") {
9888
+ return messages.keyDuplicate;
9889
+ }
9890
+ return null;
9891
+ }
9720
9892
  function FloatMetaEditor({
9721
9893
  captionEnabled,
9722
9894
  caption,
@@ -9725,8 +9897,18 @@ function FloatMetaEditor({
9725
9897
  labelPlaceholder,
9726
9898
  labelDir,
9727
9899
  messages,
9900
+ ownerId,
9901
+ references,
9902
+ labels,
9728
9903
  onChange
9729
9904
  }) {
9905
+ const [labelOverride, setLabelOverride] = useState3(null);
9906
+ const displayedLabel = labelOverride ?? label;
9907
+ const labelError = labelEnabled ? document2KeyError(displayedLabel, {
9908
+ references,
9909
+ labels,
9910
+ excludeOwnerId: ownerId
9911
+ }) : null;
9730
9912
  return /* @__PURE__ */ jsxs5("div", { className: "butex-document2-widget__float-meta", children: [
9731
9913
  /* @__PURE__ */ jsxs5("label", { className: "butex-document2-widget__float-meta-toggle", children: [
9732
9914
  /* @__PURE__ */ jsx7(
@@ -9776,15 +9958,27 @@ function FloatMetaEditor({
9776
9958
  /* @__PURE__ */ jsx7(
9777
9959
  "input",
9778
9960
  {
9779
- value: label,
9961
+ value: displayedLabel,
9780
9962
  dir: labelDir,
9781
9963
  placeholder: labelPlaceholder,
9964
+ "aria-invalid": labelError !== null ? true : void 0,
9782
9965
  onChange: (event) => {
9783
9966
  const value = event.currentTarget.value;
9967
+ setLabelOverride(value);
9968
+ const error = document2KeyError(value, {
9969
+ references,
9970
+ labels,
9971
+ excludeOwnerId: ownerId
9972
+ });
9973
+ if (error !== null) {
9974
+ return;
9975
+ }
9784
9976
  onChange({ label: value });
9977
+ setLabelOverride(null);
9785
9978
  }
9786
9979
  }
9787
- )
9980
+ ),
9981
+ labelError !== null ? /* @__PURE__ */ jsx7("p", { className: "butex-document2-widget__field-error", children: labelError === "invalid" ? messages.keyRulesHint : keyErrorMessage(labelError, messages) }) : null
9788
9982
  ] }) : null
9789
9983
  ] });
9790
9984
  }
@@ -9843,6 +10037,8 @@ function BlockEditor({
9843
10037
  onManageReferences
9844
10038
  }) {
9845
10039
  const messages = document2Messages(uiLocale);
10040
+ const labels = documentNode ? collectDocument2Labels(documentNode) : [];
10041
+ const referenceList = references ?? [];
9846
10042
  const showChrome = depth === 0;
9847
10043
  const chromeClass = [
9848
10044
  blockChromeClass(block),
@@ -10005,6 +10201,9 @@ function BlockEditor({
10005
10201
  labelPlaceholder: messages.tableLabelPlaceholder,
10006
10202
  labelDir: uiLocaleDirection(uiLocale),
10007
10203
  messages,
10204
+ ownerId: block.id,
10205
+ references: referenceList,
10206
+ labels,
10008
10207
  onChange: (patch) => onFloatMetaChange?.(block.id, "table", patch)
10009
10208
  }
10010
10209
  )
@@ -10048,6 +10247,9 @@ function BlockEditor({
10048
10247
  labelPlaceholder: messages.figureLabelPlaceholder,
10049
10248
  labelDir: uiLocaleDirection(uiLocale),
10050
10249
  messages,
10250
+ ownerId: block.id,
10251
+ references: referenceList,
10252
+ labels,
10051
10253
  onChange: (patch) => onFloatMetaChange?.(block.id, "image", patch)
10052
10254
  }
10053
10255
  )
@@ -10056,14 +10258,16 @@ function BlockEditor({
10056
10258
  if (block.kind === "bibliography") {
10057
10259
  return /* @__PURE__ */ jsxs5("section", { className: chromeClass, children: [
10058
10260
  header,
10059
- /* @__PURE__ */ jsx7("ol", { className: "butex-document2-widget__bibliography-editor", dir: documentDirection, children: references.map((reference, index) => /* @__PURE__ */ jsxs5("li", { children: [
10261
+ /* @__PURE__ */ jsx7("ol", { className: "butex-document2-widget__bibliography-editor", dir: documentDirection, children: referenceList.map((reference, index) => /* @__PURE__ */ jsxs5("li", { children: [
10060
10262
  /* @__PURE__ */ jsxs5("strong", { children: [
10061
10263
  "[",
10062
- index + 1,
10264
+ formatBibliographyNumber(index + 1, { documentDirection, digitForm }),
10063
10265
  "] ",
10064
10266
  reference.key
10065
10267
  ] }),
10066
- /* @__PURE__ */ jsx7("span", { children: [reference.authors, reference.title, reference.venue, reference.year].filter(Boolean).join(" \u2014 ") })
10268
+ /* @__PURE__ */ jsx7("span", { children: bibliographyEntryBody(reference, reference.fieldSeparator, {
10269
+ digitForm: digitForm ?? (documentDirection === "rtl" ? "arabicIndic" : "western")
10270
+ }) })
10067
10271
  ] }, reference.id)) }),
10068
10272
  onManageReferences ? /* @__PURE__ */ jsx7("button", { type: "button", onClick: onManageReferences, children: messages.manageReferences }) : null
10069
10273
  ] });
@@ -10075,19 +10279,21 @@ function BlockEditor({
10075
10279
  }
10076
10280
 
10077
10281
  // src/react-document2/CitePickerPopover.tsx
10078
- import { useEffect as useEffect3, useState as useState3 } from "react";
10282
+ import { useEffect as useEffect3, useState as useState4 } from "react";
10079
10283
  import { jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
10080
10284
  function CitePickerPopover({
10081
10285
  open,
10082
10286
  references,
10083
10287
  initialKeys = [],
10084
10288
  uiLocale = "ar",
10289
+ digitForm = "arabicIndic",
10085
10290
  onClose,
10086
10291
  onConfirm,
10087
10292
  onManageReferences
10088
10293
  }) {
10089
10294
  const messages = document2Messages(uiLocale);
10090
- const [selected, setSelected] = useState3(initialKeys);
10295
+ const documentDirection = uiLocale === "ar" ? "rtl" : "ltr";
10296
+ const [selected, setSelected] = useState4(initialKeys);
10091
10297
  useEffect3(() => {
10092
10298
  if (open) {
10093
10299
  setSelected(initialKeys);
@@ -10114,12 +10320,13 @@ function CitePickerPopover({
10114
10320
  references.length === 0 ? /* @__PURE__ */ jsx8("p", { className: "butex-document2-widget__cite-picker-empty", children: messages.noReferencesYet }) : /* @__PURE__ */ jsx8("ul", { className: "butex-document2-widget__cite-picker-list", children: references.map((reference, index) => {
10115
10321
  const checked = selected.includes(reference.key);
10116
10322
  const subtitle = [reference.authors, reference.title].filter(Boolean).join(" \u2014 ");
10323
+ const numberLabel = formatBibliographyNumber(index + 1, { documentDirection, digitForm });
10117
10324
  return /* @__PURE__ */ jsx8("li", { children: /* @__PURE__ */ jsxs6("label", { className: "butex-document2-widget__cite-picker-item", children: [
10118
10325
  /* @__PURE__ */ jsx8("input", { type: "checkbox", checked, onChange: () => toggleKey(reference.key) }),
10119
10326
  /* @__PURE__ */ jsxs6("span", { children: [
10120
10327
  /* @__PURE__ */ jsxs6("strong", { children: [
10121
10328
  "[",
10122
- index + 1,
10329
+ numberLabel,
10123
10330
  "] ",
10124
10331
  reference.key
10125
10332
  ] }),
@@ -10147,16 +10354,16 @@ function CitePickerPopover({
10147
10354
  }
10148
10355
 
10149
10356
  // src/react-document2/DocumentInsertToolbar.tsx
10150
- import { useState as useState5 } from "react";
10357
+ import { useState as useState6 } from "react";
10151
10358
 
10152
10359
  // src/react-document2/TableInsertPopover.tsx
10153
- import { useEffect as useEffect4, useRef as useRef4, useState as useState4 } from "react";
10360
+ import { useEffect as useEffect4, useRef as useRef4, useState as useState5 } from "react";
10154
10361
  import { jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
10155
10362
  function TableInsertPopover({ uiLocale = "ar", onConfirm }) {
10156
10363
  const messages = document2Messages(uiLocale);
10157
- const [open, setOpen] = useState4(false);
10158
- const [rows, setRows] = useState4("3");
10159
- const [cols, setCols] = useState4("3");
10364
+ const [open, setOpen] = useState5(false);
10365
+ const [rows, setRows] = useState5("3");
10366
+ const [cols, setCols] = useState5("3");
10160
10367
  const rootRef = useRef4(null);
10161
10368
  useEffect4(() => {
10162
10369
  if (!open) {
@@ -10249,7 +10456,7 @@ function DocumentInsertToolbar({
10249
10456
  onDigitFormChange
10250
10457
  }) {
10251
10458
  const messages = document2Messages(uiLocale);
10252
- const [digitMenuOpen, setDigitMenuOpen] = useState5(false);
10459
+ const [digitMenuOpen, setDigitMenuOpen] = useState6(false);
10253
10460
  function digitTitle(id) {
10254
10461
  if (id === "arabicIndic") {
10255
10462
  return messages.arabicIndicDigits;
@@ -10420,7 +10627,8 @@ function PreviewBlock({
10420
10627
  output,
10421
10628
  resolveImageUrl,
10422
10629
  uiLocale,
10423
- documentDirection
10630
+ documentDirection,
10631
+ digitForm
10424
10632
  }) {
10425
10633
  const messages = document2Messages(uiLocale);
10426
10634
  const floatCaptionSeparator = uiLocale === "ar" ? ": " : ". ";
@@ -10479,7 +10687,8 @@ function PreviewBlock({
10479
10687
  output,
10480
10688
  resolveImageUrl,
10481
10689
  uiLocale,
10482
- documentDirection
10690
+ documentDirection,
10691
+ digitForm
10483
10692
  },
10484
10693
  child.id
10485
10694
  ))
@@ -10509,10 +10718,10 @@ function PreviewBlock({
10509
10718
  ] }),
10510
10719
  " ",
10511
10720
  /* @__PURE__ */ jsxs9("span", { children: [
10512
- [item.authors, item.title, item.venue, item.year].filter(Boolean).join(", "),
10721
+ bibliographyEntryBody(item, item.fieldSeparator, { digitForm }),
10513
10722
  item.url ? /* @__PURE__ */ jsxs9(Fragment, { children: [
10514
10723
  " ",
10515
- /* @__PURE__ */ jsx11("a", { href: item.url, target: "_blank", rel: "noreferrer", children: item.url })
10724
+ /* @__PURE__ */ jsx11("a", { href: absoluteHttpHref(item.url), target: "_blank", rel: "noreferrer", children: item.url })
10516
10725
  ] }) : null
10517
10726
  ] })
10518
10727
  ] }, item.id)) });
@@ -10524,9 +10733,11 @@ function DocumentPreview({
10524
10733
  output,
10525
10734
  documentDirection = "rtl",
10526
10735
  uiLocale = "ar",
10736
+ digitForm,
10527
10737
  resolveImageUrl
10528
10738
  }) {
10529
10739
  const messages = document2Messages(uiLocale);
10740
+ const resolvedDigitForm = digitForm ?? (documentDirection === "rtl" ? "arabicIndic" : "western");
10530
10741
  const hasBody = blocks.some(isBodyPreviewBlock);
10531
10742
  return /* @__PURE__ */ jsxs9("div", { className: "butex-document2-widget__preview", dir: documentDirection, children: [
10532
10743
  blocks.map((block) => /* @__PURE__ */ jsx11(
@@ -10536,7 +10747,8 @@ function DocumentPreview({
10536
10747
  output,
10537
10748
  resolveImageUrl,
10538
10749
  uiLocale,
10539
- documentDirection
10750
+ documentDirection,
10751
+ digitForm: resolvedDigitForm
10540
10752
  },
10541
10753
  block.id
10542
10754
  )),
@@ -10545,7 +10757,7 @@ function DocumentPreview({
10545
10757
  }
10546
10758
 
10547
10759
  // src/react-document2/EquationDrawer.tsx
10548
- import { useEffect as useEffect6, useRef as useRef6 } from "react";
10760
+ import { useEffect as useEffect6, useRef as useRef6, useState as useState8 } from "react";
10549
10761
 
10550
10762
  // src/react/ButexEditor.tsx
10551
10763
  import {
@@ -10554,7 +10766,7 @@ import {
10554
10766
  useEffect as useEffect5,
10555
10767
  useImperativeHandle,
10556
10768
  useRef as useRef5,
10557
- useState as useState6
10769
+ useState as useState7
10558
10770
  } from "react";
10559
10771
 
10560
10772
  // src/react/widgetChromeCss.ts
@@ -11626,32 +11838,32 @@ var ButexEditor = forwardRef(
11626
11838
  const onSessionChangeRef = useRef5(onSessionChange);
11627
11839
  onSessionChangeRef.current = onSessionChange;
11628
11840
  const debugBodyHiddenRef = useRef5(false);
11629
- const [debugBodyHidden, setDebugBodyHidden] = useState6(false);
11841
+ const [debugBodyHidden, setDebugBodyHidden] = useState7(false);
11630
11842
  debugBodyHiddenRef.current = debugBodyHidden;
11631
- const [digitMenuOpen, setDigitMenuOpen] = useState6(false);
11632
- const [characterFontMenuOpen, setCharacterFontMenuOpen] = useState6(false);
11633
- const [delimiterMenuOpen, setDelimiterMenuOpen] = useState6(false);
11634
- const [matrixMenuOpen, setMatrixMenuOpen] = useState6(false);
11635
- const [spacingMenuOpen, setSpacingMenuOpen] = useState6(false);
11636
- const [functionMenuOpen, setFunctionMenuOpen] = useState6(false);
11637
- const [limitsSeriesMenuOpen, setLimitsSeriesMenuOpen] = useState6(false);
11638
- const [function2MenuOpen, setFunction2MenuOpen] = useState6(false);
11639
- const [derivativeMenuOpen, setDerivativeMenuOpen] = useState6(false);
11640
- const [groupMenuOpen, setGroupMenuOpen] = useState6(false);
11641
- const [operatorMenuOpen, setOperatorMenuOpen] = useState6(false);
11642
- const [operator2MenuOpen, setOperator2MenuOpen] = useState6(false);
11643
- const [operator3MenuOpen, setOperator3MenuOpen] = useState6(false);
11644
- const [dotMenuOpen, setDotMenuOpen] = useState6(false);
11645
- const [integralMenuOpen, setIntegralMenuOpen] = useState6(false);
11646
- const [selectedDigitForm2, setSelectedDigitForm] = useState6("western");
11647
- const [selectedCharacterFont, setSelectedCharacterFont] = useState6("default");
11648
- const [activeEditorSide, setActiveEditorSide] = useState6("arabic");
11649
- const [envPaletteMode, setEnvPaletteMode] = useState6("matrix");
11650
- const [matrixStyle, setMatrixStyle] = useState6("pmatrix");
11651
- const [matrixRows, setMatrixRows] = useState6(2);
11652
- const [matrixColumns, setMatrixColumns] = useState6(2);
11653
- const [matrixHover, setMatrixHover] = useState6(null);
11654
- const [arrayAlignmentColumn, setArrayAlignmentColumn] = useState6(1);
11843
+ const [digitMenuOpen, setDigitMenuOpen] = useState7(false);
11844
+ const [characterFontMenuOpen, setCharacterFontMenuOpen] = useState7(false);
11845
+ const [delimiterMenuOpen, setDelimiterMenuOpen] = useState7(false);
11846
+ const [matrixMenuOpen, setMatrixMenuOpen] = useState7(false);
11847
+ const [spacingMenuOpen, setSpacingMenuOpen] = useState7(false);
11848
+ const [functionMenuOpen, setFunctionMenuOpen] = useState7(false);
11849
+ const [limitsSeriesMenuOpen, setLimitsSeriesMenuOpen] = useState7(false);
11850
+ const [function2MenuOpen, setFunction2MenuOpen] = useState7(false);
11851
+ const [derivativeMenuOpen, setDerivativeMenuOpen] = useState7(false);
11852
+ const [groupMenuOpen, setGroupMenuOpen] = useState7(false);
11853
+ const [operatorMenuOpen, setOperatorMenuOpen] = useState7(false);
11854
+ const [operator2MenuOpen, setOperator2MenuOpen] = useState7(false);
11855
+ const [operator3MenuOpen, setOperator3MenuOpen] = useState7(false);
11856
+ const [dotMenuOpen, setDotMenuOpen] = useState7(false);
11857
+ const [integralMenuOpen, setIntegralMenuOpen] = useState7(false);
11858
+ const [selectedDigitForm2, setSelectedDigitForm] = useState7("western");
11859
+ const [selectedCharacterFont, setSelectedCharacterFont] = useState7("default");
11860
+ const [activeEditorSide, setActiveEditorSide] = useState7("arabic");
11861
+ const [envPaletteMode, setEnvPaletteMode] = useState7("matrix");
11862
+ const [matrixStyle, setMatrixStyle] = useState7("pmatrix");
11863
+ const [matrixRows, setMatrixRows] = useState7(2);
11864
+ const [matrixColumns, setMatrixColumns] = useState7(2);
11865
+ const [matrixHover, setMatrixHover] = useState7(null);
11866
+ const [arrayAlignmentColumn, setArrayAlignmentColumn] = useState7(1);
11655
11867
  useImperativeHandle(ref, () => ({
11656
11868
  getRuntime: () => runtimeRef.current
11657
11869
  }));
@@ -12836,6 +13048,18 @@ ButexEditor.displayName = "ButexEditor";
12836
13048
 
12837
13049
  // src/react-document2/EquationDrawer.tsx
12838
13050
  import { jsx as jsx13, jsxs as jsxs11 } from "react/jsx-runtime";
13051
+ function keyErrorMessage2(error, messages) {
13052
+ if (error === "empty") {
13053
+ return messages.keyEmpty;
13054
+ }
13055
+ if (error === "invalid") {
13056
+ return messages.keyInvalid;
13057
+ }
13058
+ if (error === "duplicate") {
13059
+ return messages.keyDuplicate;
13060
+ }
13061
+ return null;
13062
+ }
12839
13063
  function EquationDrawer({
12840
13064
  session,
12841
13065
  reason,
@@ -12845,6 +13069,9 @@ function EquationDrawer({
12845
13069
  uiLocale = "ar",
12846
13070
  labelEnabled = false,
12847
13071
  label = "",
13072
+ labels = [],
13073
+ references = [],
13074
+ ownerId = "",
12848
13075
  onLabelEnabledChange,
12849
13076
  onLabelChange,
12850
13077
  onMathModeChange,
@@ -12854,6 +13081,16 @@ function EquationDrawer({
12854
13081
  }) {
12855
13082
  const messages = document2Messages(uiLocale);
12856
13083
  const latestSession = useRef6(session);
13084
+ const [labelOverride, setLabelOverride] = useState8(null);
13085
+ const displayedLabel = labelOverride ?? label;
13086
+ const labelError = labelEnabled && mathMode === "display" ? document2KeyError(displayedLabel, {
13087
+ references,
13088
+ labels,
13089
+ excludeOwnerId: ownerId || void 0
13090
+ }) : null;
13091
+ useEffect6(() => {
13092
+ setLabelOverride(null);
13093
+ }, [ownerId, label]);
12857
13094
  useEffect6(() => {
12858
13095
  const onKeyDown = (event) => {
12859
13096
  if (event.key === "Escape") {
@@ -12914,12 +13151,27 @@ function EquationDrawer({
12914
13151
  /* @__PURE__ */ jsx13(
12915
13152
  "input",
12916
13153
  {
12917
- value: label,
13154
+ value: displayedLabel,
12918
13155
  dir: uiLocaleDirection(uiLocale),
12919
13156
  placeholder: messages.equationLabelPlaceholder,
12920
- onChange: (event) => onLabelChange?.(event.currentTarget.value)
13157
+ "aria-invalid": labelError !== null ? true : void 0,
13158
+ onChange: (event) => {
13159
+ const value = event.currentTarget.value;
13160
+ setLabelOverride(value);
13161
+ const error = document2KeyError(value, {
13162
+ references,
13163
+ labels,
13164
+ excludeOwnerId: ownerId || void 0
13165
+ });
13166
+ if (error !== null) {
13167
+ return;
13168
+ }
13169
+ onLabelChange?.(value);
13170
+ setLabelOverride(null);
13171
+ }
12921
13172
  }
12922
- )
13173
+ ),
13174
+ labelError !== null ? /* @__PURE__ */ jsx13("p", { className: "butex-document2-widget__field-error", children: labelError === "invalid" ? messages.keyRulesHint : keyErrorMessage2(labelError, messages) }) : null
12923
13175
  ] }) : null
12924
13176
  ] }) : null,
12925
13177
  reason ? /* @__PURE__ */ jsx13("p", { className: "butex-document2-widget__error", children: uiLocale === "en" ? messages.equationUnavailable : reason }) : null,
@@ -12937,7 +13189,15 @@ function EquationDrawer({
12937
13189
  ),
12938
13190
  /* @__PURE__ */ jsxs11("div", { className: "butex-document2-widget__row", children: [
12939
13191
  canDelete && onDelete ? /* @__PURE__ */ jsx13("button", { type: "button", className: "butex-document2-widget__button--danger", onClick: onDelete, children: messages.deleteEquation }) : null,
12940
- /* @__PURE__ */ jsx13("button", { type: "button", onClick: () => onSave({ ...latestSession.current, activeSide: equationSide }), children: messages.saveEquation })
13192
+ /* @__PURE__ */ jsx13(
13193
+ "button",
13194
+ {
13195
+ type: "button",
13196
+ disabled: labelEnabled && mathMode === "display" && labelError !== null,
13197
+ onClick: () => onSave({ ...latestSession.current, activeSide: equationSide }),
13198
+ children: messages.saveEquation
13199
+ }
13200
+ )
12941
13201
  ] })
12942
13202
  ]
12943
13203
  }
@@ -12946,92 +13206,243 @@ function EquationDrawer({
12946
13206
  }
12947
13207
 
12948
13208
  // src/react-document2/LabelsPanel.tsx
13209
+ import { useState as useState9 } from "react";
12949
13210
  import { jsx as jsx14, jsxs as jsxs12 } from "react/jsx-runtime";
12950
- function kindLabel(kind, uiLocale) {
12951
- const messages = document2Messages(uiLocale);
12952
- if (kind === "fig") {
12953
- return messages.labelKindFig;
13211
+ function keyErrorMessage3(error, messages) {
13212
+ if (error === "empty") {
13213
+ return messages.keyEmpty;
12954
13214
  }
12955
- if (kind === "tab") {
12956
- return messages.labelKindTab;
13215
+ if (error === "invalid") {
13216
+ return messages.keyInvalid;
12957
13217
  }
12958
- return messages.labelKindEq;
13218
+ if (error === "duplicate") {
13219
+ return messages.keyDuplicate;
13220
+ }
13221
+ return null;
12959
13222
  }
12960
13223
  function LabelsPanel({
12961
13224
  open,
12962
13225
  labels,
13226
+ references = [],
12963
13227
  uiLocale = "ar",
13228
+ digitForm = "arabicIndic",
12964
13229
  onClose,
12965
13230
  onUpdateFloat,
12966
13231
  onUpdateEquationLabel
12967
13232
  }) {
12968
13233
  const messages = document2Messages(uiLocale);
13234
+ const documentDirection = uiLocale === "ar" ? "rtl" : "ltr";
13235
+ const [keyOverrides, setKeyOverrides] = useState9({});
12969
13236
  if (!open) {
12970
13237
  return null;
12971
13238
  }
12972
- return /* @__PURE__ */ jsx14("div", { className: "butex-document2-widget__refs-backdrop", role: "presentation", onClick: onClose, children: /* @__PURE__ */ jsxs12("div", { className: "butex-document2-widget__refs-panel", role: "dialog", "aria-label": messages.labelsTitle, onClick: (event) => event.stopPropagation(), children: [
12973
- /* @__PURE__ */ jsxs12("header", { className: "butex-document2-widget__refs-header", children: [
12974
- /* @__PURE__ */ jsx14("strong", { children: messages.labelsTitle }),
12975
- /* @__PURE__ */ jsx14("button", { type: "button", className: "butex-document2-widget__icon-btn", title: messages.close, "aria-label": messages.close, onClick: onClose, children: "\xD7" })
12976
- ] }),
12977
- /* @__PURE__ */ jsx14("p", { className: "butex-document2-widget__labels-help", children: messages.labelsHelp }),
12978
- labels.length === 0 ? /* @__PURE__ */ jsx14("p", { className: "butex-document2-widget__cite-picker-empty", children: messages.noLabelsYet }) : /* @__PURE__ */ jsx14("ul", { className: "butex-document2-widget__refs-list", children: labels.map((entry) => /* @__PURE__ */ jsxs12("li", { className: "butex-document2-widget__refs-item", children: [
12979
- /* @__PURE__ */ jsx14("div", { className: "butex-document2-widget__refs-item-head", children: /* @__PURE__ */ jsxs12("strong", { children: [
12980
- "[",
12981
- kindLabel(entry.kind, uiLocale),
12982
- " ",
12983
- entry.number,
12984
- "] ",
12985
- entry.key
12986
- ] }) }),
12987
- /* @__PURE__ */ jsxs12("div", { className: "butex-document2-widget__refs-grid", children: [
12988
- /* @__PURE__ */ jsxs12("label", { className: "butex-document2-widget__refs-field", children: [
12989
- /* @__PURE__ */ jsx14("span", { children: messages.floatLabel }),
12990
- /* @__PURE__ */ jsx14(
12991
- "input",
12992
- {
12993
- value: entry.key,
12994
- dir: uiLocaleDirection(uiLocale),
12995
- onChange: (event) => {
12996
- const value = event.currentTarget.value;
12997
- if (entry.kind === "eq") {
12998
- onUpdateEquationLabel?.(entry.ownerId, { label: value, labelEnabled: true });
12999
- } else if (entry.kind === "fig" || entry.kind === "tab") {
13000
- onUpdateFloat?.(entry.ownerId, entry.kind, { label: value, labelEnabled: true });
13001
- }
13002
- }
13003
- }
13004
- )
13239
+ return /* @__PURE__ */ jsx14("div", { className: "butex-document2-widget__refs-backdrop", role: "presentation", onClick: onClose, children: /* @__PURE__ */ jsxs12(
13240
+ "div",
13241
+ {
13242
+ className: "butex-document2-widget__refs-panel",
13243
+ role: "dialog",
13244
+ "aria-label": messages.labelsTitle,
13245
+ onClick: (event) => event.stopPropagation(),
13246
+ children: [
13247
+ /* @__PURE__ */ jsxs12("header", { className: "butex-document2-widget__refs-header", children: [
13248
+ /* @__PURE__ */ jsx14("strong", { children: messages.labelsTitle }),
13249
+ /* @__PURE__ */ jsx14("button", { type: "button", className: "butex-document2-widget__icon-btn", title: messages.close, "aria-label": messages.close, onClick: onClose, children: "\xD7" })
13005
13250
  ] }),
13006
- entry.kind === "fig" || entry.kind === "tab" ? /* @__PURE__ */ jsxs12("label", { className: "butex-document2-widget__refs-field", children: [
13007
- /* @__PURE__ */ jsx14("span", { children: messages.floatCaption }),
13008
- /* @__PURE__ */ jsx14(
13009
- "input",
13010
- {
13011
- value: entry.caption,
13012
- onChange: (event) => {
13013
- const value = event.currentTarget.value;
13014
- const kind = entry.kind;
13015
- if (kind === "fig" || kind === "tab") {
13016
- onUpdateFloat?.(entry.ownerId, kind, { caption: value, captionEnabled: true });
13017
- }
13251
+ /* @__PURE__ */ jsx14("p", { className: "butex-document2-widget__labels-help", children: messages.labelsHelp }),
13252
+ labels.length === 0 ? /* @__PURE__ */ jsx14("p", { className: "butex-document2-widget__cite-picker-empty", children: messages.noLabelsYet }) : /* @__PURE__ */ jsx14("ul", { className: "butex-document2-widget__refs-list", children: labels.map((entry) => {
13253
+ const title = formatFloatCaptionTitle(entry.kind, entry.number, {
13254
+ uiLocale,
13255
+ digitForm,
13256
+ documentDirection
13257
+ });
13258
+ const captionPreview = entry.kind === "eq" ? title : formatFloatCaptionPreview(title, entry.caption, uiLocale);
13259
+ const overrideKey = `${entry.kind}:${entry.ownerId}`;
13260
+ const displayedKey = keyOverrides[overrideKey] ?? entry.key;
13261
+ const entryKeyError = document2KeyError(displayedKey, {
13262
+ references,
13263
+ labels,
13264
+ excludeOwnerId: entry.ownerId
13265
+ });
13266
+ return /* @__PURE__ */ jsxs12("li", { className: "butex-document2-widget__refs-item", children: [
13267
+ /* @__PURE__ */ jsxs12("div", { className: "butex-document2-widget__refs-item-head", children: [
13268
+ /* @__PURE__ */ jsx14("strong", { children: title }),
13269
+ /* @__PURE__ */ jsx14("span", { className: "butex-document2-widget__refs-item-key", children: entry.key })
13270
+ ] }),
13271
+ /* @__PURE__ */ jsx14(
13272
+ "div",
13273
+ {
13274
+ className: "butex-document2-widget__modal-preview",
13275
+ dir: uiLocaleDirection(uiLocale),
13276
+ "aria-label": messages.captionPreview,
13277
+ children: captionPreview
13018
13278
  }
13019
- }
13020
- )
13021
- ] }) : null
13022
- ] })
13023
- ] }, `${entry.kind}:${entry.ownerId}`)) })
13024
- ] }) });
13279
+ ),
13280
+ /* @__PURE__ */ jsxs12("div", { className: "butex-document2-widget__refs-grid", children: [
13281
+ /* @__PURE__ */ jsxs12("label", { className: "butex-document2-widget__refs-field", children: [
13282
+ /* @__PURE__ */ jsx14("span", { children: messages.floatLabel }),
13283
+ /* @__PURE__ */ jsx14(
13284
+ "input",
13285
+ {
13286
+ value: displayedKey,
13287
+ dir: uiLocaleDirection(uiLocale),
13288
+ "aria-invalid": entryKeyError !== null ? true : void 0,
13289
+ onChange: (event) => {
13290
+ const value = event.currentTarget.value;
13291
+ setKeyOverrides((current) => ({ ...current, [overrideKey]: value }));
13292
+ const error = document2KeyError(value, {
13293
+ references,
13294
+ labels,
13295
+ excludeOwnerId: entry.ownerId
13296
+ });
13297
+ if (error !== null) {
13298
+ return;
13299
+ }
13300
+ if (entry.kind === "eq") {
13301
+ onUpdateEquationLabel?.(entry.ownerId, { label: value, labelEnabled: true });
13302
+ } else if (entry.kind === "fig" || entry.kind === "tab") {
13303
+ onUpdateFloat?.(entry.ownerId, entry.kind, { label: value, labelEnabled: true });
13304
+ }
13305
+ setKeyOverrides((current) => {
13306
+ const next = { ...current };
13307
+ delete next[overrideKey];
13308
+ return next;
13309
+ });
13310
+ }
13311
+ }
13312
+ ),
13313
+ entryKeyError !== null ? /* @__PURE__ */ jsx14("p", { className: "butex-document2-widget__field-error", children: entryKeyError === "invalid" ? messages.keyRulesHint : keyErrorMessage3(entryKeyError, messages) }) : null
13314
+ ] }),
13315
+ entry.kind === "fig" || entry.kind === "tab" ? /* @__PURE__ */ jsxs12("label", { className: "butex-document2-widget__refs-field", children: [
13316
+ /* @__PURE__ */ jsx14("span", { children: messages.floatCaption }),
13317
+ /* @__PURE__ */ jsx14(
13318
+ "input",
13319
+ {
13320
+ value: entry.caption,
13321
+ onChange: (event) => {
13322
+ const value = event.currentTarget.value;
13323
+ const kind = entry.kind;
13324
+ if (kind === "fig" || kind === "tab") {
13325
+ onUpdateFloat?.(entry.ownerId, kind, { caption: value, captionEnabled: true });
13326
+ }
13327
+ }
13328
+ }
13329
+ )
13330
+ ] }) : null
13331
+ ] })
13332
+ ] }, overrideKey);
13333
+ }) })
13334
+ ]
13335
+ }
13336
+ ) });
13025
13337
  }
13026
13338
 
13027
13339
  // src/react-document2/ReferencesPanel.tsx
13028
- import { useState as useState7 } from "react";
13029
- import { jsx as jsx15, jsxs as jsxs13 } from "react/jsx-runtime";
13030
- var EMPTY_DRAFT = { key: "", authors: "", title: "", year: "", url: "", venue: "" };
13340
+ import { useMemo, useState as useState10 } from "react";
13341
+ import { Fragment as Fragment3, jsx as jsx15, jsxs as jsxs13 } from "react/jsx-runtime";
13342
+ var EMPTY_DRAFT = {
13343
+ key: "",
13344
+ authors: "",
13345
+ title: "",
13346
+ year: "",
13347
+ url: "",
13348
+ venue: "",
13349
+ field_separator: DEFAULT_REFERENCE_FIELD_SEPARATOR
13350
+ };
13351
+ var REFERENCE_FIELDS = [
13352
+ ["key", "referenceKey"],
13353
+ ["authors", "referenceAuthors"],
13354
+ ["title", "referenceTitle"],
13355
+ ["year", "referenceYear"],
13356
+ ["venue", "referenceVenue"],
13357
+ ["url", "referenceUrl"]
13358
+ ];
13359
+ function keyErrorMessage4(error, messages) {
13360
+ if (error === "empty") {
13361
+ return messages.keyEmpty;
13362
+ }
13363
+ if (error === "invalid") {
13364
+ return messages.keyInvalid;
13365
+ }
13366
+ if (error === "duplicate") {
13367
+ return messages.keyDuplicate;
13368
+ }
13369
+ return null;
13370
+ }
13371
+ function FieldSeparatorToggle({
13372
+ value,
13373
+ messages,
13374
+ onChange
13375
+ }) {
13376
+ return /* @__PURE__ */ jsxs13("div", { className: "butex-document2-widget__refs-field butex-document2-widget__refs-separator", children: [
13377
+ /* @__PURE__ */ jsx15("span", { children: messages.referenceFieldSeparator }),
13378
+ /* @__PURE__ */ jsxs13("div", { className: "butex-document2-widget__refs-separator-toggle", role: "group", "aria-label": messages.referenceFieldSeparator, children: [
13379
+ /* @__PURE__ */ jsx15(
13380
+ "button",
13381
+ {
13382
+ type: "button",
13383
+ className: value === "\u060C" ? "is-active" : void 0,
13384
+ title: messages.referenceCommaRtl,
13385
+ "aria-label": messages.referenceCommaRtl,
13386
+ "aria-pressed": value === "\u060C",
13387
+ onClick: () => onChange("\u060C"),
13388
+ children: "\u060C"
13389
+ }
13390
+ ),
13391
+ /* @__PURE__ */ jsx15(
13392
+ "button",
13393
+ {
13394
+ type: "button",
13395
+ className: value === "," ? "is-active" : void 0,
13396
+ title: messages.referenceCommaLtr,
13397
+ "aria-label": messages.referenceCommaLtr,
13398
+ "aria-pressed": value === ",",
13399
+ onClick: () => onChange(","),
13400
+ children: ","
13401
+ }
13402
+ )
13403
+ ] })
13404
+ ] });
13405
+ }
13406
+ function ReferencePreview({
13407
+ authors,
13408
+ title,
13409
+ venue,
13410
+ year,
13411
+ url,
13412
+ fieldSeparator,
13413
+ numberLabel,
13414
+ messages,
13415
+ uiLocale,
13416
+ digitForm
13417
+ }) {
13418
+ const body = bibliographyEntryBody({ authors, title, venue, year, fieldSeparator }, fieldSeparator, { digitForm });
13419
+ return /* @__PURE__ */ jsxs13(
13420
+ "div",
13421
+ {
13422
+ className: "butex-document2-widget__modal-preview",
13423
+ dir: uiLocaleDirection(uiLocale),
13424
+ "aria-label": messages.referencePreview,
13425
+ children: [
13426
+ numberLabel ? /* @__PURE__ */ jsxs13("span", { className: "butex-document2-widget__modal-preview-number", children: [
13427
+ "[",
13428
+ numberLabel,
13429
+ "] "
13430
+ ] }) : null,
13431
+ body.length > 0 ? body : /* @__PURE__ */ jsx15("span", { className: "butex-document2-widget__cite-picker-empty", children: "\u2026" }),
13432
+ url.trim().length > 0 ? /* @__PURE__ */ jsxs13(Fragment3, { children: [
13433
+ " ",
13434
+ /* @__PURE__ */ jsx15("span", { className: "butex-document2-widget__modal-preview-url", children: url.trim() })
13435
+ ] }) : null
13436
+ ]
13437
+ }
13438
+ );
13439
+ }
13031
13440
  function ReferencesPanel({
13032
13441
  open,
13033
13442
  references,
13443
+ labels = [],
13034
13444
  uiLocale = "ar",
13445
+ digitForm = "arabicIndic",
13035
13446
  onClose,
13036
13447
  onAdd,
13037
13448
  onUpdate,
@@ -13039,101 +13450,190 @@ function ReferencesPanel({
13039
13450
  onMove
13040
13451
  }) {
13041
13452
  const messages = document2Messages(uiLocale);
13042
- const [draft, setDraft] = useState7(EMPTY_DRAFT);
13453
+ const [draft, setDraft] = useState10(EMPTY_DRAFT);
13454
+ const [draftKeyTouched, setDraftKeyTouched] = useState10(false);
13455
+ const [keyOverrides, setKeyOverrides] = useState10({});
13456
+ const draftSeparator = draft.field_separator === "," ? "," : DEFAULT_REFERENCE_FIELD_SEPARATOR;
13457
+ const documentDirection = uiLocale === "ar" ? "rtl" : "ltr";
13458
+ const draftKeyError = useMemo(
13459
+ () => document2KeyError(draft.key ?? "", {
13460
+ references,
13461
+ labels
13462
+ }),
13463
+ [draft.key, references, labels]
13464
+ );
13043
13465
  if (!open) {
13044
13466
  return null;
13045
13467
  }
13046
- return /* @__PURE__ */ jsx15("div", { className: "butex-document2-widget__refs-backdrop", role: "presentation", onClick: onClose, children: /* @__PURE__ */ jsxs13("div", { className: "butex-document2-widget__refs-panel", role: "dialog", "aria-label": messages.referencesTitle, onClick: (event) => event.stopPropagation(), children: [
13047
- /* @__PURE__ */ jsxs13("header", { className: "butex-document2-widget__refs-header", children: [
13048
- /* @__PURE__ */ jsx15("strong", { children: messages.referencesTitle }),
13049
- /* @__PURE__ */ jsx15("button", { type: "button", className: "butex-document2-widget__icon-btn", title: messages.close, "aria-label": messages.close, onClick: onClose, children: "\xD7" })
13050
- ] }),
13051
- /* @__PURE__ */ jsxs13("div", { className: "butex-document2-widget__refs-add", children: [
13052
- /* @__PURE__ */ jsx15("h3", { children: messages.addReference }),
13053
- /* @__PURE__ */ jsx15("div", { className: "butex-document2-widget__refs-grid", children: [
13054
- ["key", messages.referenceKey],
13055
- ["authors", messages.referenceAuthors],
13056
- ["title", messages.referenceTitle],
13057
- ["year", messages.referenceYear],
13058
- ["venue", messages.referenceVenue],
13059
- ["url", messages.referenceUrl]
13060
- ].map(([field, label]) => /* @__PURE__ */ jsxs13("label", { className: "butex-document2-widget__refs-field", children: [
13061
- /* @__PURE__ */ jsx15("span", { children: label }),
13062
- /* @__PURE__ */ jsx15(
13063
- "input",
13064
- {
13065
- value: draft[field] ?? "",
13066
- onChange: (event) => {
13067
- const value = event.currentTarget.value;
13068
- setDraft((current) => ({ ...current, [field]: value }));
13069
- }
13070
- }
13071
- )
13072
- ] }, field)) }),
13073
- /* @__PURE__ */ jsx15(
13074
- "button",
13075
- {
13076
- type: "button",
13077
- className: "butex-document2-widget__primary-btn",
13078
- disabled: !draft.key?.trim(),
13079
- onClick: () => {
13080
- onAdd(draft);
13081
- setDraft(EMPTY_DRAFT);
13082
- },
13083
- children: messages.addReference
13084
- }
13085
- )
13086
- ] }),
13087
- /* @__PURE__ */ jsx15("ul", { className: "butex-document2-widget__refs-list", children: references.map((reference, index) => /* @__PURE__ */ jsxs13("li", { className: "butex-document2-widget__refs-item", children: [
13088
- /* @__PURE__ */ jsxs13("div", { className: "butex-document2-widget__refs-item-head", children: [
13089
- /* @__PURE__ */ jsxs13("strong", { children: [
13090
- "[",
13091
- index + 1,
13092
- "] ",
13093
- reference.key
13468
+ return /* @__PURE__ */ jsx15("div", { className: "butex-document2-widget__refs-backdrop", role: "presentation", onClick: onClose, children: /* @__PURE__ */ jsxs13(
13469
+ "div",
13470
+ {
13471
+ className: "butex-document2-widget__refs-panel",
13472
+ role: "dialog",
13473
+ "aria-label": messages.referencesTitle,
13474
+ onClick: (event) => event.stopPropagation(),
13475
+ children: [
13476
+ /* @__PURE__ */ jsxs13("header", { className: "butex-document2-widget__refs-header", children: [
13477
+ /* @__PURE__ */ jsx15("strong", { children: messages.referencesTitle }),
13478
+ /* @__PURE__ */ jsx15("button", { type: "button", className: "butex-document2-widget__icon-btn", title: messages.close, "aria-label": messages.close, onClick: onClose, children: "\xD7" })
13094
13479
  ] }),
13095
- /* @__PURE__ */ jsxs13("div", { className: "butex-document2-widget__refs-item-actions", children: [
13096
- /* @__PURE__ */ jsx15("button", { type: "button", title: messages.moveUp, "aria-label": messages.moveUp, disabled: index === 0, onClick: () => onMove(reference.id, -1), children: "\u2191" }),
13480
+ /* @__PURE__ */ jsxs13("div", { className: "butex-document2-widget__refs-add", children: [
13481
+ /* @__PURE__ */ jsx15("h3", { children: messages.addReference }),
13482
+ /* @__PURE__ */ jsxs13("div", { className: "butex-document2-widget__refs-grid", children: [
13483
+ REFERENCE_FIELDS.map(([field, messageKey]) => /* @__PURE__ */ jsxs13("label", { className: "butex-document2-widget__refs-field", children: [
13484
+ /* @__PURE__ */ jsx15("span", { children: messages[messageKey] }),
13485
+ /* @__PURE__ */ jsx15(
13486
+ "input",
13487
+ {
13488
+ value: draft[field] ?? "",
13489
+ "aria-invalid": field === "key" && draftKeyTouched && draftKeyError !== null ? true : void 0,
13490
+ onChange: (event) => {
13491
+ const value = event.currentTarget.value;
13492
+ if (field === "key") {
13493
+ setDraftKeyTouched(true);
13494
+ }
13495
+ setDraft((current) => ({ ...current, [field]: value }));
13496
+ }
13497
+ }
13498
+ ),
13499
+ field === "key" && draftKeyTouched && draftKeyError !== null ? /* @__PURE__ */ jsx15("p", { className: "butex-document2-widget__field-error", children: draftKeyError === "invalid" ? messages.keyRulesHint : keyErrorMessage4(draftKeyError, messages) }) : null
13500
+ ] }, field)),
13501
+ /* @__PURE__ */ jsx15(
13502
+ FieldSeparatorToggle,
13503
+ {
13504
+ value: draftSeparator,
13505
+ messages,
13506
+ onChange: (next) => setDraft((current) => ({ ...current, field_separator: next }))
13507
+ }
13508
+ )
13509
+ ] }),
13097
13510
  /* @__PURE__ */ jsx15(
13098
- "button",
13511
+ ReferencePreview,
13099
13512
  {
13100
- type: "button",
13101
- title: messages.moveDown,
13102
- "aria-label": messages.moveDown,
13103
- disabled: index === references.length - 1,
13104
- onClick: () => onMove(reference.id, 1),
13105
- children: "\u2193"
13513
+ authors: draft.authors ?? "",
13514
+ title: draft.title ?? "",
13515
+ venue: draft.venue ?? "",
13516
+ year: draft.year ?? "",
13517
+ url: draft.url ?? "",
13518
+ fieldSeparator: draftSeparator,
13519
+ messages,
13520
+ uiLocale,
13521
+ digitForm
13106
13522
  }
13107
13523
  ),
13108
- /* @__PURE__ */ jsx15("button", { type: "button", title: messages.deleteReference, "aria-label": messages.deleteReference, onClick: () => onRemove(reference.id), children: "\xD7" })
13109
- ] })
13110
- ] }),
13111
- /* @__PURE__ */ jsx15("div", { className: "butex-document2-widget__refs-grid", children: [
13112
- ["key", messages.referenceKey],
13113
- ["authors", messages.referenceAuthors],
13114
- ["title", messages.referenceTitle],
13115
- ["year", messages.referenceYear],
13116
- ["venue", messages.referenceVenue],
13117
- ["url", messages.referenceUrl]
13118
- ].map(([field, label]) => /* @__PURE__ */ jsxs13("label", { className: "butex-document2-widget__refs-field", children: [
13119
- /* @__PURE__ */ jsx15("span", { children: label }),
13120
- /* @__PURE__ */ jsx15(
13121
- "input",
13122
- {
13123
- value: reference[field],
13124
- onChange: (event) => {
13125
- const value = event.currentTarget.value;
13126
- onUpdate(reference.id, { [field]: value });
13524
+ /* @__PURE__ */ jsx15(
13525
+ "button",
13526
+ {
13527
+ type: "button",
13528
+ className: "butex-document2-widget__primary-btn",
13529
+ disabled: draftKeyError !== null,
13530
+ onClick: () => {
13531
+ onAdd({ ...draft, field_separator: draftSeparator });
13532
+ setDraft(EMPTY_DRAFT);
13533
+ setDraftKeyTouched(false);
13534
+ },
13535
+ children: messages.addReference
13127
13536
  }
13128
- }
13129
- )
13130
- ] }, field)) })
13131
- ] }, reference.id)) })
13132
- ] }) });
13537
+ )
13538
+ ] }),
13539
+ /* @__PURE__ */ jsx15("ul", { className: "butex-document2-widget__refs-list", children: references.map((reference, index) => {
13540
+ const displayedKey = keyOverrides[reference.id] ?? reference.key;
13541
+ const entryKeyError = document2KeyError(displayedKey, {
13542
+ references,
13543
+ labels,
13544
+ excludeReferenceId: reference.id
13545
+ });
13546
+ const numberLabel = formatBibliographyNumber(index + 1, { documentDirection, digitForm });
13547
+ return /* @__PURE__ */ jsxs13("li", { className: "butex-document2-widget__refs-item", children: [
13548
+ /* @__PURE__ */ jsxs13("div", { className: "butex-document2-widget__refs-item-head", children: [
13549
+ /* @__PURE__ */ jsxs13("strong", { children: [
13550
+ "[",
13551
+ numberLabel,
13552
+ "] ",
13553
+ reference.key
13554
+ ] }),
13555
+ /* @__PURE__ */ jsxs13("div", { className: "butex-document2-widget__refs-item-actions", children: [
13556
+ /* @__PURE__ */ jsx15("button", { type: "button", title: messages.moveUp, "aria-label": messages.moveUp, disabled: index === 0, onClick: () => onMove(reference.id, -1), children: "\u2191" }),
13557
+ /* @__PURE__ */ jsx15(
13558
+ "button",
13559
+ {
13560
+ type: "button",
13561
+ title: messages.moveDown,
13562
+ "aria-label": messages.moveDown,
13563
+ disabled: index === references.length - 1,
13564
+ onClick: () => onMove(reference.id, 1),
13565
+ children: "\u2193"
13566
+ }
13567
+ ),
13568
+ /* @__PURE__ */ jsx15("button", { type: "button", title: messages.deleteReference, "aria-label": messages.deleteReference, onClick: () => onRemove(reference.id), children: "\xD7" })
13569
+ ] })
13570
+ ] }),
13571
+ /* @__PURE__ */ jsx15(
13572
+ ReferencePreview,
13573
+ {
13574
+ authors: reference.authors,
13575
+ title: reference.title,
13576
+ venue: reference.venue,
13577
+ year: reference.year,
13578
+ url: reference.url,
13579
+ fieldSeparator: reference.fieldSeparator,
13580
+ numberLabel,
13581
+ messages,
13582
+ uiLocale,
13583
+ digitForm
13584
+ }
13585
+ ),
13586
+ /* @__PURE__ */ jsxs13("div", { className: "butex-document2-widget__refs-grid", children: [
13587
+ REFERENCE_FIELDS.map(([field, messageKey]) => /* @__PURE__ */ jsxs13("label", { className: "butex-document2-widget__refs-field", children: [
13588
+ /* @__PURE__ */ jsx15("span", { children: messages[messageKey] }),
13589
+ /* @__PURE__ */ jsx15(
13590
+ "input",
13591
+ {
13592
+ value: field === "key" ? displayedKey : reference[field],
13593
+ "aria-invalid": field === "key" && entryKeyError !== null ? true : void 0,
13594
+ onChange: (event) => {
13595
+ const value = event.currentTarget.value;
13596
+ if (field !== "key") {
13597
+ onUpdate(reference.id, { [field]: value });
13598
+ return;
13599
+ }
13600
+ setKeyOverrides((current) => ({ ...current, [reference.id]: value }));
13601
+ const error = document2KeyError(value, {
13602
+ references,
13603
+ labels,
13604
+ excludeReferenceId: reference.id
13605
+ });
13606
+ if (error === null) {
13607
+ onUpdate(reference.id, { key: value });
13608
+ setKeyOverrides((current) => {
13609
+ const next = { ...current };
13610
+ delete next[reference.id];
13611
+ return next;
13612
+ });
13613
+ }
13614
+ }
13615
+ }
13616
+ ),
13617
+ field === "key" && entryKeyError !== null ? /* @__PURE__ */ jsx15("p", { className: "butex-document2-widget__field-error", children: entryKeyError === "invalid" ? messages.keyRulesHint : keyErrorMessage4(entryKeyError, messages) }) : null
13618
+ ] }, field)),
13619
+ /* @__PURE__ */ jsx15(
13620
+ FieldSeparatorToggle,
13621
+ {
13622
+ value: reference.fieldSeparator,
13623
+ messages,
13624
+ onChange: (next) => onUpdate(reference.id, { field_separator: next })
13625
+ }
13626
+ )
13627
+ ] })
13628
+ ] }, reference.id);
13629
+ }) })
13630
+ ]
13631
+ }
13632
+ ) });
13133
13633
  }
13134
13634
 
13135
13635
  // src/react-document2/RefPickerPopover.tsx
13136
- import { useEffect as useEffect7, useState as useState8 } from "react";
13636
+ import { useEffect as useEffect7, useState as useState11 } from "react";
13137
13637
  import { jsx as jsx16, jsxs as jsxs14 } from "react/jsx-runtime";
13138
13638
  function RefPickerPopover({
13139
13639
  open,
@@ -13141,13 +13641,15 @@ function RefPickerPopover({
13141
13641
  initialKeys = [],
13142
13642
  initialRefCommand = "ref",
13143
13643
  uiLocale = "ar",
13644
+ digitForm = "arabicIndic",
13144
13645
  onClose,
13145
13646
  onConfirm,
13146
13647
  onManageLabels
13147
13648
  }) {
13148
13649
  const messages = document2Messages(uiLocale);
13149
- const [selected, setSelected] = useState8(initialKeys);
13150
- const [refCommand, setRefCommand] = useState8(initialRefCommand);
13650
+ const documentDirection = uiLocale === "ar" ? "rtl" : "ltr";
13651
+ const [selected, setSelected] = useState11(initialKeys);
13652
+ const [refCommand, setRefCommand] = useState11(initialRefCommand);
13151
13653
  useEffect7(() => {
13152
13654
  if (open) {
13153
13655
  setSelected(initialKeys);
@@ -13175,16 +13677,18 @@ function RefPickerPopover({
13175
13677
  ] }),
13176
13678
  labels.length === 0 ? /* @__PURE__ */ jsx16("p", { className: "butex-document2-widget__cite-picker-empty", children: messages.noLabelsYet }) : /* @__PURE__ */ jsx16("ul", { className: "butex-document2-widget__cite-picker-list", children: labels.map((entry) => {
13177
13679
  const checked = selected.includes(entry.key);
13680
+ const title = formatFloatCaptionTitle(entry.kind, entry.number, {
13681
+ uiLocale,
13682
+ digitForm,
13683
+ documentDirection
13684
+ });
13178
13685
  return /* @__PURE__ */ jsx16("li", { children: /* @__PURE__ */ jsxs14("label", { className: "butex-document2-widget__cite-picker-item", children: [
13179
13686
  /* @__PURE__ */ jsx16("input", { type: "radio", name: "butex-ref-key", checked, onChange: () => selectKey(entry.key, entry.kind) }),
13180
13687
  /* @__PURE__ */ jsxs14("span", { children: [
13181
13688
  /* @__PURE__ */ jsxs14("strong", { children: [
13182
- "[",
13183
- entry.kind,
13689
+ title,
13184
13690
  " ",
13185
- entry.number,
13186
- "] ",
13187
- entry.key
13691
+ /* @__PURE__ */ jsx16("span", { className: "butex-document2-widget__refs-item-key", children: entry.key })
13188
13692
  ] }),
13189
13693
  entry.caption ? /* @__PURE__ */ jsx16("span", { className: "butex-document2-widget__cite-picker-meta", children: entry.caption }) : null
13190
13694
  ] })
@@ -14376,6 +14880,24 @@ var DOCUMENT2_WIDGET_CSS = `
14376
14880
  width: 100%;
14377
14881
  }
14378
14882
 
14883
+ .butex-document2-widget__field-error {
14884
+ color: #b42318;
14885
+ font-size: 0.85em;
14886
+ line-height: 1.35;
14887
+ margin: 0;
14888
+ }
14889
+
14890
+ .butex-document2-widget__field-hint {
14891
+ color: var(--butex-document2-muted);
14892
+ font-size: 0.82em;
14893
+ line-height: 1.35;
14894
+ margin: 0;
14895
+ }
14896
+
14897
+ .butex-document2-widget__refs-field input[aria-invalid='true'] {
14898
+ border-color: color-mix(in srgb, #b42318 55%, var(--butex-document2-border));
14899
+ }
14900
+
14379
14901
  .butex-document2-widget__refs-item {
14380
14902
  border: 1px solid var(--butex-document2-border);
14381
14903
  border-radius: 10px;
@@ -14384,6 +14906,58 @@ var DOCUMENT2_WIDGET_CSS = `
14384
14906
  padding: 0.75rem;
14385
14907
  }
14386
14908
 
14909
+ .butex-document2-widget__refs-item-key {
14910
+ color: var(--butex-document2-muted);
14911
+ font-size: 0.9em;
14912
+ font-weight: 500;
14913
+ }
14914
+
14915
+ .butex-document2-widget__modal-preview {
14916
+ background: color-mix(in srgb, var(--butex-document2-panel) 88%, var(--butex-document2-accent-bg));
14917
+ border: 1px dashed color-mix(in srgb, var(--butex-document2-accent) 30%, var(--butex-document2-border));
14918
+ border-radius: 8px;
14919
+ color: inherit;
14920
+ font-size: 0.95em;
14921
+ line-height: 1.55;
14922
+ padding: 0.55rem 0.7rem;
14923
+ text-align: center;
14924
+ }
14925
+
14926
+ .butex-document2-widget__modal-preview-number {
14927
+ color: var(--butex-document2-muted);
14928
+ font-weight: 600;
14929
+ }
14930
+
14931
+ .butex-document2-widget__modal-preview-url {
14932
+ color: var(--butex-document2-accent);
14933
+ word-break: break-all;
14934
+ }
14935
+
14936
+ .butex-document2-widget__refs-separator-toggle {
14937
+ display: inline-flex;
14938
+ gap: 0.35rem;
14939
+ }
14940
+
14941
+ .butex-document2-widget__refs-separator-toggle button {
14942
+ background: var(--butex-document2-input-bg, var(--butex-document2-panel));
14943
+ border: 1px solid var(--butex-document2-border);
14944
+ border-radius: 6px;
14945
+ color: inherit;
14946
+ cursor: pointer;
14947
+ font: inherit;
14948
+ font-size: 1.15em;
14949
+ line-height: 1;
14950
+ min-width: 2.25rem;
14951
+ padding: 0.35rem 0.55rem;
14952
+ }
14953
+
14954
+ .butex-document2-widget__refs-separator-toggle button.is-active {
14955
+ background: var(--butex-document2-accent-bg);
14956
+ border-color: color-mix(in srgb, var(--butex-document2-accent) 45%, var(--butex-document2-border));
14957
+ color: var(--butex-document2-accent);
14958
+ font-weight: 700;
14959
+ }
14960
+
14387
14961
  .butex-document2-widget__refs-item-actions {
14388
14962
  display: inline-flex;
14389
14963
  gap: 0.25rem;
@@ -14666,7 +15240,7 @@ function injectBuTeXDocument2Styles(doc) {
14666
15240
  }
14667
15241
 
14668
15242
  // src/react-document2/ButexDocumentEditor2.tsx
14669
- import { Fragment as Fragment3, jsx as jsx17, jsxs as jsxs15 } from "react/jsx-runtime";
15243
+ import { Fragment as Fragment4, jsx as jsx17, jsxs as jsxs15 } from "react/jsx-runtime";
14670
15244
  function mathDelimiters(mode) {
14671
15245
  return mode === "inline" ? { opening: "$", closing: "$" } : { opening: "\\[", closing: "\\]" };
14672
15246
  }
@@ -14875,24 +15449,24 @@ function ButexDocumentEditor2({
14875
15449
  onLatexChange
14876
15450
  }) {
14877
15451
  const messages = document2Messages(uiLocale);
14878
- const initialState = useMemo(
15452
+ const initialState = useMemo2(
14879
15453
  () => resolveInitialDocument2(initialDocument, uiLocale, documentMeta),
14880
15454
  [initialDocument, documentMeta, uiLocale]
14881
15455
  );
14882
- const [documentNode, setDocumentNode] = useState9(initialState.document);
14883
- const [error, setError] = useState9(initialState.error);
14884
- const [editorOpen, setEditorOpen] = useState9(true);
14885
- const [previewOpen, setPreviewOpen] = useState9(true);
14886
- const [selectedMath, setSelectedMath] = useState9(null);
14887
- const [citePicker, setCitePicker] = useState9(null);
14888
- const [referencesOpen, setReferencesOpen] = useState9(false);
14889
- const [labelsOpen, setLabelsOpen] = useState9(false);
14890
- const [refPicker, setRefPicker] = useState9(null);
14891
- const [digitFormState, setDigitFormState] = useState9(null);
14892
- const [editorFocus, setEditorFocus] = useState9(createEmptyDocument2EditorFocus());
14893
- const [collapsedBlockIds, setCollapsedBlockIds] = useState9(() => /* @__PURE__ */ new Set());
14894
- const [blockSelectionState, setBlockSelectionState] = useState9(emptyBlockSelection);
14895
- const [historyTick, setHistoryTick] = useState9(0);
15456
+ const [documentNode, setDocumentNode] = useState12(initialState.document);
15457
+ const [error, setError] = useState12(initialState.error);
15458
+ const [editorOpen, setEditorOpen] = useState12(true);
15459
+ const [previewOpen, setPreviewOpen] = useState12(true);
15460
+ const [selectedMath, setSelectedMath] = useState12(null);
15461
+ const [citePicker, setCitePicker] = useState12(null);
15462
+ const [referencesOpen, setReferencesOpen] = useState12(false);
15463
+ const [labelsOpen, setLabelsOpen] = useState12(false);
15464
+ const [refPicker, setRefPicker] = useState12(null);
15465
+ const [digitFormState, setDigitFormState] = useState12(null);
15466
+ const [editorFocus, setEditorFocus] = useState12(createEmptyDocument2EditorFocus());
15467
+ const [collapsedBlockIds, setCollapsedBlockIds] = useState12(() => /* @__PURE__ */ new Set());
15468
+ const [blockSelectionState, setBlockSelectionState] = useState12(emptyBlockSelection);
15469
+ const [historyTick, setHistoryTick] = useState12(0);
14896
15470
  const digitForm = digitFormProp ?? digitFormState ?? (documentDirection === "rtl" ? "arabicIndic" : "western");
14897
15471
  const historyRef = useRef7(createDocument2History());
14898
15472
  const documentRef = useRef7(documentNode);
@@ -14903,8 +15477,8 @@ function ButexDocumentEditor2({
14903
15477
  const pendingFocusRef = useRef7(null);
14904
15478
  documentRef.current = documentNode;
14905
15479
  const latex = document2Latex(documentNode);
14906
- const documentLabels = useMemo(() => collectDocument2Labels(documentNode), [documentNode]);
14907
- const preview = useMemo(
15480
+ const documentLabels = useMemo2(() => collectDocument2Labels(documentNode), [documentNode]);
15481
+ const preview = useMemo2(
14908
15482
  () => document2Preview(documentNode, mathOutput, equationSide, {
14909
15483
  documentDirection,
14910
15484
  digitForm,
@@ -14912,7 +15486,7 @@ function ButexDocumentEditor2({
14912
15486
  }),
14913
15487
  [documentNode, documentDirection, digitForm, equationSide, mathOutput, uiLocale]
14914
15488
  );
14915
- const debugEnabled = useMemo(() => {
15489
+ const debugEnabled = useMemo2(() => {
14916
15490
  if (debug) {
14917
15491
  return true;
14918
15492
  }
@@ -14921,8 +15495,8 @@ function ButexDocumentEditor2({
14921
15495
  }
14922
15496
  return new URLSearchParams(window.location.search).get("debug") === "1";
14923
15497
  }, [debug]);
14924
- const canUndo = useMemo(() => document2HistoryCanUndo(historyRef.current), [historyTick, documentNode]);
14925
- const canRedo = useMemo(() => document2HistoryCanRedo(historyRef.current), [historyTick, documentNode]);
15498
+ const canUndo = useMemo2(() => document2HistoryCanUndo(historyRef.current), [historyTick, documentNode]);
15499
+ const canRedo = useMemo2(() => document2HistoryCanRedo(historyRef.current), [historyTick, documentNode]);
14926
15500
  const blockSelection = normalizeBlockSelection(blockSelectionState.selection, documentNode.blocks.length);
14927
15501
  const selectionCount = blockSelection ? blockSelection.to - blockSelection.from + 1 : 0;
14928
15502
  const canMoveSelectionUp = Boolean(blockSelection && blockSelection.from > 0);
@@ -15583,6 +16157,7 @@ function ButexDocumentEditor2({
15583
16157
  output: mathOutput,
15584
16158
  documentDirection,
15585
16159
  uiLocale,
16160
+ digitForm,
15586
16161
  resolveImageUrl
15587
16162
  }
15588
16163
  )
@@ -15592,7 +16167,7 @@ function ButexDocumentEditor2({
15592
16167
  }
15593
16168
  ),
15594
16169
  debugEnabled ? /* @__PURE__ */ jsxs15("div", { className: "butex-document2-widget__dev", children: [
15595
- debugEnabled && documentNode.diagnostics.length > 0 ? /* @__PURE__ */ jsxs15(Fragment3, { children: [
16170
+ debugEnabled && documentNode.diagnostics.length > 0 ? /* @__PURE__ */ jsxs15(Fragment4, { children: [
15596
16171
  /* @__PURE__ */ jsx17("strong", { children: messages.importWarnings }),
15597
16172
  documentNode.diagnostics.map((diagnostic) => /* @__PURE__ */ jsx17("p", { className: "butex-document2-widget__dev-diagnostic", children: formatDocument2Diagnostic(diagnostic, messages) }, `${diagnostic.path}-${diagnostic.message}`))
15598
16173
  ] }) : null,
@@ -15612,6 +16187,9 @@ function ButexDocumentEditor2({
15612
16187
  uiLocale,
15613
16188
  labelEnabled: selectedMath.labelEnabled,
15614
16189
  label: selectedMath.label,
16190
+ labels: documentLabels,
16191
+ references: documentNode.references,
16192
+ ownerId: selectedMath.tokenId ?? void 0,
15615
16193
  onLabelEnabledChange: (enabled) => setSelectedMath((current) => current ? { ...current, labelEnabled: enabled } : current),
15616
16194
  onLabelChange: (nextLabel) => setSelectedMath((current) => current ? { ...current, label: nextLabel } : current),
15617
16195
  onMathModeChange: (mode) => setSelectedMath((current) => current ? { ...current, mathMode: mode } : current),
@@ -15627,6 +16205,7 @@ function ButexDocumentEditor2({
15627
16205
  references: documentNode.references,
15628
16206
  initialKeys: citePicker?.keys ?? [],
15629
16207
  uiLocale,
16208
+ digitForm,
15630
16209
  onClose: () => setCitePicker(null),
15631
16210
  onConfirm: confirmCiteKeys,
15632
16211
  onManageReferences: () => {
@@ -15643,6 +16222,7 @@ function ButexDocumentEditor2({
15643
16222
  initialKeys: refPicker?.keys ?? [],
15644
16223
  initialRefCommand: refPicker?.refCommand ?? "ref",
15645
16224
  uiLocale,
16225
+ digitForm,
15646
16226
  onClose: () => setRefPicker(null),
15647
16227
  onConfirm: confirmRefKeys,
15648
16228
  onManageLabels: () => {
@@ -15656,7 +16236,9 @@ function ButexDocumentEditor2({
15656
16236
  {
15657
16237
  open: referencesOpen,
15658
16238
  references: documentNode.references,
16239
+ labels: documentLabels,
15659
16240
  uiLocale,
16241
+ digitForm,
15660
16242
  onClose: () => setReferencesOpen(false),
15661
16243
  onAdd: (partial) => applyDocument(addDocument2Reference(documentRef.current, partial), "immediate"),
15662
16244
  onUpdate: (referenceId, patch) => applyDocument(updateDocument2Reference(documentRef.current, referenceId, patch), "immediate"),
@@ -15669,7 +16251,9 @@ function ButexDocumentEditor2({
15669
16251
  {
15670
16252
  open: labelsOpen,
15671
16253
  labels: documentLabels,
16254
+ references: documentNode.references,
15672
16255
  uiLocale,
16256
+ digitForm,
15673
16257
  onClose: () => setLabelsOpen(false),
15674
16258
  onUpdateFloat: (ownerId, kind, patch) => applyDocument(
15675
16259
  kind === "fig" ? updateDocument2ImageMeta(documentRef.current, ownerId, patch) : updateDocument2TableMeta(documentRef.current, ownerId, patch),