@drghaliasri/butex 5.0.0 → 5.2.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.
@@ -34,6 +34,179 @@ function formatDigits(value, digitForm = "western") {
34
34
  }).join("");
35
35
  }
36
36
 
37
+ // src/document2/articleMeta.ts
38
+ var BUTEX_BASMALA = "\u0628\u0650\u0633\u0652\u0645\u0650 \u0627\u0644\u0644\u064E\u0651\u0647\u0650 \u0627\u0644\u0631\u064E\u0651\u062D\u0652\u0645\u064E\u0670\u0646\u0650 \u0627\u0644\u0631\u064E\u0651\u062D\u0650\u064A\u0645\u0650 \u0648\u064E\u0627\u0644\u0635\u064E\u0651\u0644\u064E\u0627\u0629\u064F \u0648\u064E\u0627\u0644\u0633\u064E\u0651\u0644\u064E\u0627\u0645\u064F \u0639\u064E\u0644\u064E\u0649\u0670 \u0631\u064E\u0633\u064F\u0648\u0644\u0650 \u0627\u0644\u0644\u064E\u0651\u0647\u0650 \uFDFA";
39
+ var BUTEX_CLOSING_HAMDALA = "\u0627\u0644\u0652\u062D\u064E\u0645\u0652\u062F\u064F \u0644\u0650\u0644\u064E\u0651\u0647\u0650 \u0646\u064E\u0641\u064E\u0639\u064E\u0646\u064E\u0627 \u0628\u0650\u0639\u0650\u0644\u0652\u0645\u0650\u0647\u0650";
40
+ function escapeButexTextArg(text) {
41
+ return text.replace(/\\/g, "\\textbackslash{}").replace(/[{}]/g, "\\$&");
42
+ }
43
+ function butexDiwaniDisplayTex(text) {
44
+ return `\\butexdiwani{${escapeButexTextArg(text)}}`;
45
+ }
46
+ function butexDiwaniDisplayLatex(text) {
47
+ return `\\[
48
+ ${butexDiwaniDisplayTex(text)}
49
+ \\]`;
50
+ }
51
+ var HIJRI_YEAR_MIN = 1400;
52
+ var HIJRI_YEAR_MAX = 1500;
53
+ var HIJRI_MONTH_IDS = [
54
+ "\u0645\u062D\u0631\u0645",
55
+ "\u0635\u0641\u0631",
56
+ "\u0631\u0628\u064A\u0639 \u0627\u0644\u0623\u0648\u0644",
57
+ "\u0631\u0628\u064A\u0639 \u0627\u0644\u0622\u062E\u0631",
58
+ "\u062C\u0645\u0627\u062F\u0649 \u0627\u0644\u0623\u0648\u0644\u0649",
59
+ "\u062C\u0645\u0627\u062F\u0649 \u0627\u0644\u0622\u062E\u0631\u0629",
60
+ "\u0631\u062C\u0628",
61
+ "\u0634\u0639\u0628\u0627\u0646",
62
+ "\u0631\u0645\u0636\u0627\u0646",
63
+ "\u0634\u0648\u0627\u0644",
64
+ "\u0630\u0648 \u0627\u0644\u0642\u0639\u062F\u0629",
65
+ "\u0630\u0648 \u0627\u0644\u062D\u062C\u0629"
66
+ ];
67
+ var HIJRI_MONTH_LABELS_EN = {
68
+ \u0645\u062D\u0631\u0645: "Muharram",
69
+ \u0635\u0641\u0631: "Safar",
70
+ "\u0631\u0628\u064A\u0639 \u0627\u0644\u0623\u0648\u0644": "Rabi\u02BB I",
71
+ "\u0631\u0628\u064A\u0639 \u0627\u0644\u0622\u062E\u0631": "Rabi\u02BB II",
72
+ "\u062C\u0645\u0627\u062F\u0649 \u0627\u0644\u0623\u0648\u0644\u0649": "Jumada I",
73
+ "\u062C\u0645\u0627\u062F\u0649 \u0627\u0644\u0622\u062E\u0631\u0629": "Jumada II",
74
+ \u0631\u062C\u0628: "Rajab",
75
+ \u0634\u0639\u0628\u0627\u0646: "Sha\u02BBban",
76
+ \u0631\u0645\u0636\u0627\u0646: "Ramadan",
77
+ \u0634\u0648\u0627\u0644: "Shawwal",
78
+ "\u0630\u0648 \u0627\u0644\u0642\u0639\u062F\u0629": "Dhul Qa\u02BBdah",
79
+ "\u0630\u0648 \u0627\u0644\u062D\u062C\u0629": "Dhul Hijjah"
80
+ };
81
+ var LEGACY_LATIN_MONTH = {
82
+ muharram: "\u0645\u062D\u0631\u0645",
83
+ safar: "\u0635\u0641\u0631",
84
+ rabiAwwal: "\u0631\u0628\u064A\u0639 \u0627\u0644\u0623\u0648\u0644",
85
+ rabiThani: "\u0631\u0628\u064A\u0639 \u0627\u0644\u0622\u062E\u0631",
86
+ jumadaAwwal: "\u062C\u0645\u0627\u062F\u0649 \u0627\u0644\u0623\u0648\u0644\u0649",
87
+ jumadaThani: "\u062C\u0645\u0627\u062F\u0649 \u0627\u0644\u0622\u062E\u0631\u0629",
88
+ rajab: "\u0631\u062C\u0628",
89
+ shaban: "\u0634\u0639\u0628\u0627\u0646",
90
+ ramadan: "\u0631\u0645\u0636\u0627\u0646",
91
+ shawwal: "\u0634\u0648\u0627\u0644",
92
+ dhulQadah: "\u0630\u0648 \u0627\u0644\u0642\u0639\u062F\u0629",
93
+ dhulHijjah: "\u0630\u0648 \u0627\u0644\u062D\u062C\u0629"
94
+ };
95
+ var DEFAULT_HIJRI_DATE = {
96
+ day: 1,
97
+ month: "\u0645\u062D\u0631\u0645",
98
+ year: 1448
99
+ };
100
+ function hijriMonthLabel(month, locale = "ar") {
101
+ return locale === "en" ? HIJRI_MONTH_LABELS_EN[month] : month;
102
+ }
103
+ function emptyDocument2Meta() {
104
+ return {
105
+ title: "",
106
+ authors: "",
107
+ date: { ...DEFAULT_HIJRI_DATE },
108
+ abstract: ""
109
+ };
110
+ }
111
+ function clampInt(value, min, max) {
112
+ if (!Number.isFinite(value)) {
113
+ return min;
114
+ }
115
+ return Math.min(max, Math.max(min, Math.trunc(value)));
116
+ }
117
+ function isHijriMonthId(value) {
118
+ return typeof value === "string" && HIJRI_MONTH_IDS.includes(value);
119
+ }
120
+ function resolveHijriMonthId(value) {
121
+ if (isHijriMonthId(value)) {
122
+ return value;
123
+ }
124
+ if (typeof value === "string" && value in LEGACY_LATIN_MONTH) {
125
+ return LEGACY_LATIN_MONTH[value];
126
+ }
127
+ return DEFAULT_HIJRI_DATE.month;
128
+ }
129
+ function normalizeDocument2HijriDate(value) {
130
+ if (typeof value !== "object" || value === null) {
131
+ return { ...DEFAULT_HIJRI_DATE };
132
+ }
133
+ const raw = value;
134
+ const day = clampInt(typeof raw.day === "number" ? raw.day : Number(raw.day), 1, 30);
135
+ const year = clampInt(typeof raw.year === "number" ? raw.year : Number(raw.year), HIJRI_YEAR_MIN, HIJRI_YEAR_MAX);
136
+ const month = resolveHijriMonthId(raw.month);
137
+ return { day, month, year };
138
+ }
139
+ function normalizeDocument2Meta(value) {
140
+ if (typeof value !== "object" || value === null) {
141
+ return emptyDocument2Meta();
142
+ }
143
+ const raw = value;
144
+ return {
145
+ title: typeof raw.title === "string" ? raw.title : "",
146
+ authors: typeof raw.authors === "string" ? raw.authors : "",
147
+ date: normalizeDocument2HijriDate(raw.date),
148
+ abstract: typeof raw.abstract === "string" ? raw.abstract : ""
149
+ };
150
+ }
151
+ function mergeDocument2Meta(fromJson, fromProp) {
152
+ const fromPropMeta = {
153
+ title: typeof fromProp?.title === "string" ? fromProp.title : "",
154
+ authors: typeof fromProp?.authors === "string" ? fromProp.authors : "",
155
+ abstract: typeof fromProp?.abstract === "string" ? fromProp.abstract : "",
156
+ date: fromProp?.date ? normalizeDocument2HijriDate({ ...DEFAULT_HIJRI_DATE, ...fromProp.date }) : { ...DEFAULT_HIJRI_DATE }
157
+ };
158
+ if (typeof fromJson !== "object" || fromJson === null) {
159
+ return fromPropMeta;
160
+ }
161
+ const json = fromJson;
162
+ return {
163
+ title: "title" in json && typeof json.title === "string" ? json.title : fromPropMeta.title,
164
+ authors: "authors" in json && typeof json.authors === "string" ? json.authors : fromPropMeta.authors,
165
+ abstract: "abstract" in json && typeof json.abstract === "string" ? json.abstract : fromPropMeta.abstract,
166
+ date: "date" in json ? normalizeDocument2HijriDate(json.date) : fromPropMeta.date
167
+ };
168
+ }
169
+ function fillDocument2MetaGaps(current, fromProp) {
170
+ if (!fromProp) {
171
+ return cloneDocument2Meta(current);
172
+ }
173
+ return {
174
+ title: current.title.trim().length > 0 ? current.title : typeof fromProp.title === "string" ? fromProp.title : current.title,
175
+ authors: current.authors.trim().length > 0 ? current.authors : typeof fromProp.authors === "string" ? fromProp.authors : current.authors,
176
+ abstract: current.abstract.trim().length > 0 ? current.abstract : typeof fromProp.abstract === "string" ? fromProp.abstract : current.abstract,
177
+ date: fromProp.date ? normalizeDocument2HijriDate({ ...current.date, ...fromProp.date }) : { ...current.date }
178
+ };
179
+ }
180
+ function document2HasMaketitle(meta) {
181
+ return meta.title.trim().length > 0 || meta.authors.trim().length > 0;
182
+ }
183
+ function document2HasAbstract(meta) {
184
+ return meta.abstract.trim().length > 0;
185
+ }
186
+ function document2HasTitleStack(meta) {
187
+ return document2HasMaketitle(meta) || document2HasAbstract(meta);
188
+ }
189
+ function formatHijriDate(date, options = "ar") {
190
+ const opts = typeof options === "string" ? { locale: options } : options;
191
+ const locale = opts.locale ?? "ar";
192
+ const digitForm = opts.digitForm ?? (locale === "en" ? "western" : "arabicIndic");
193
+ const month = hijriMonthLabel(date.month, locale);
194
+ const day = formatDigits(String(date.day), digitForm);
195
+ const year = formatDigits(String(date.year), digitForm);
196
+ if (locale === "en") {
197
+ return `${day} ${month} ${year} AH`;
198
+ }
199
+ return `${day} ${month} ${year}\u0647\u0640`;
200
+ }
201
+ function cloneDocument2Meta(meta) {
202
+ return {
203
+ title: meta.title,
204
+ authors: meta.authors,
205
+ date: { ...meta.date },
206
+ abstract: meta.abstract
207
+ };
208
+ }
209
+
37
210
  // src/document2/citations.ts
38
211
  function referenceNumberMap(references) {
39
212
  const map = /* @__PURE__ */ new Map();
@@ -469,6 +642,168 @@ ${lines.map((line) => ` ${line}`).join(" \\\\\n")}
469
642
  ${math.closing}`;
470
643
  }
471
644
 
645
+ // src/document2/labels.ts
646
+ function defaultFloatMeta() {
647
+ return {
648
+ centered: true,
649
+ captionEnabled: false,
650
+ caption: "",
651
+ labelEnabled: false,
652
+ label: ""
653
+ };
654
+ }
655
+ function parseFloatMetaFromJson(json) {
656
+ const caption = typeof json.caption === "string" ? json.caption : "";
657
+ const label = typeof json.label === "string" ? json.label : "";
658
+ return {
659
+ centered: json.centered !== false,
660
+ captionEnabled: json.caption_enabled === true || typeof json.caption_enabled !== "boolean" && caption.length > 0,
661
+ caption,
662
+ labelEnabled: json.label_enabled === true || typeof json.label_enabled !== "boolean" && label.trim().length > 0,
663
+ label
664
+ };
665
+ }
666
+ function parseRefKeys(source) {
667
+ const eqref = /^\\eqref\{([^}]*)\}$/.exec(source.trim());
668
+ if (eqref) {
669
+ return {
670
+ refCommand: "eqref",
671
+ keys: (eqref[1] ?? "").split(",").map((key) => key.trim()).filter((key) => key.length > 0)
672
+ };
673
+ }
674
+ const ref = /^\\ref\{([^}]*)\}$/.exec(source.trim());
675
+ if (ref) {
676
+ return {
677
+ refCommand: "ref",
678
+ keys: (ref[1] ?? "").split(",").map((key) => key.trim()).filter((key) => key.length > 0)
679
+ };
680
+ }
681
+ return { keys: [], refCommand: "ref" };
682
+ }
683
+ function refTokenLatex(keys, refCommand) {
684
+ const command = refCommand === "eqref" ? "\\eqref" : "\\ref";
685
+ return `${command}{${keys.join(",")}}`;
686
+ }
687
+ function collectDocument2Labels(document2) {
688
+ const entries = [];
689
+ const counters = { fig: 0, tab: 0, eq: 0 };
690
+ function pushEqFromField(tokens) {
691
+ for (const token of tokens) {
692
+ if (token.kind !== "math") {
693
+ continue;
694
+ }
695
+ if (token.display && token.labelEnabled && token.label && token.label.trim().length > 0) {
696
+ counters.eq += 1;
697
+ entries.push({
698
+ key: token.label.trim(),
699
+ kind: "eq",
700
+ caption: "",
701
+ ownerId: token.id,
702
+ number: counters.eq
703
+ });
704
+ }
705
+ }
706
+ }
707
+ function walk(blocks) {
708
+ for (const block of blocks) {
709
+ if (block.kind === "image" && block.labelEnabled && block.label.trim().length > 0) {
710
+ counters.fig += 1;
711
+ entries.push({
712
+ key: block.label.trim(),
713
+ kind: "fig",
714
+ caption: block.captionEnabled ? block.caption : "",
715
+ ownerId: block.id,
716
+ number: counters.fig
717
+ });
718
+ }
719
+ if (block.kind === "table") {
720
+ if (block.labelEnabled && block.label.trim().length > 0) {
721
+ counters.tab += 1;
722
+ entries.push({
723
+ key: block.label.trim(),
724
+ kind: "tab",
725
+ caption: block.captionEnabled ? block.caption : "",
726
+ ownerId: block.id,
727
+ number: counters.tab
728
+ });
729
+ }
730
+ for (const row of block.rows) {
731
+ for (const cell of row) {
732
+ pushEqFromField(cell.tokens);
733
+ }
734
+ }
735
+ }
736
+ if (block.kind === "textBlock") {
737
+ pushEqFromField(block.field.tokens);
738
+ }
739
+ if (block.kind === "list") {
740
+ for (const item of block.items) {
741
+ pushEqFromField(item.field.tokens);
742
+ walk(item.blocks);
743
+ }
744
+ }
745
+ }
746
+ }
747
+ walk(document2.blocks);
748
+ return entries;
749
+ }
750
+ function labelNumberMap(document2) {
751
+ const map = /* @__PURE__ */ new Map();
752
+ for (const entry of collectDocument2Labels(document2)) {
753
+ if (!map.has(entry.key)) {
754
+ map.set(entry.key, { kind: entry.kind, number: entry.number });
755
+ }
756
+ }
757
+ return map;
758
+ }
759
+ function formatRefLabel(keys, document2, refCommand, options = {}) {
760
+ const documentDirection = options.documentDirection ?? "rtl";
761
+ const digitForm = options.digitForm ?? (documentDirection === "rtl" ? "arabicIndic" : "western");
762
+ const map = labelNumberMap(document2);
763
+ const parts = keys.map((key) => {
764
+ const hit = map.get(key);
765
+ if (!hit) {
766
+ return "?";
767
+ }
768
+ return formatDigits(String(hit.number), digitForm);
769
+ });
770
+ const body = parts.join(documentDirection === "rtl" ? "\u060C" : ", ");
771
+ if (refCommand === "eqref") {
772
+ return `(${body})`;
773
+ }
774
+ return body;
775
+ }
776
+ function stripDisplayMathDelimiters(source) {
777
+ const trimmed = source.trim();
778
+ if (trimmed.startsWith("\\[") && trimmed.endsWith("\\]")) {
779
+ return trimmed.slice(2, -2).trim();
780
+ }
781
+ if (trimmed.startsWith("$$") && trimmed.endsWith("$$")) {
782
+ return trimmed.slice(2, -2).trim();
783
+ }
784
+ if (trimmed.startsWith("\\begin{equation}") && trimmed.endsWith("\\end{equation}")) {
785
+ return trimmed.slice("\\begin{equation}".length, -"\\end{equation}".length).trim();
786
+ }
787
+ return trimmed;
788
+ }
789
+ function applyFloatMetaPatch(block, patch) {
790
+ if (typeof patch.centered === "boolean") {
791
+ block.centered = patch.centered;
792
+ }
793
+ if (typeof patch.captionEnabled === "boolean") {
794
+ block.captionEnabled = patch.captionEnabled;
795
+ }
796
+ if (typeof patch.caption === "string") {
797
+ block.caption = patch.caption;
798
+ }
799
+ if (typeof patch.labelEnabled === "boolean") {
800
+ block.labelEnabled = patch.labelEnabled;
801
+ }
802
+ if (typeof patch.label === "string") {
803
+ block.label = patch.label;
804
+ }
805
+ }
806
+
472
807
  // src/document2/inlineScanner.ts
473
808
  var MATH_ENVIRONMENTS = /* @__PURE__ */ new Set([
474
809
  "equation",
@@ -544,6 +879,43 @@ function citeSpan(value, start) {
544
879
  }
545
880
  return null;
546
881
  }
882
+ function refSpan(value, start) {
883
+ const eqrefPrefix = "\\eqref{";
884
+ const refPrefix = "\\ref{";
885
+ let refCommand = "ref";
886
+ let prefix = refPrefix;
887
+ if (value.startsWith(eqrefPrefix, start) && !isEscaped(value, start)) {
888
+ refCommand = "eqref";
889
+ prefix = eqrefPrefix;
890
+ } else if (value.startsWith(refPrefix, start) && !isEscaped(value, start)) {
891
+ refCommand = "ref";
892
+ prefix = refPrefix;
893
+ } else {
894
+ return null;
895
+ }
896
+ const openBrace = start + prefix.length - 1;
897
+ if (value[openBrace] !== "{") {
898
+ return null;
899
+ }
900
+ let depth = 0;
901
+ for (let i = openBrace; i < value.length; i += 1) {
902
+ const char = value[i];
903
+ if (char === "{" && !isEscaped(value, i)) {
904
+ depth += 1;
905
+ continue;
906
+ }
907
+ if (char === "}" && !isEscaped(value, i)) {
908
+ depth -= 1;
909
+ if (depth === 0) {
910
+ const end = i + 1;
911
+ const source = value.slice(start, end);
912
+ const parsed = parseRefKeys(source);
913
+ return { kind: "ref", start, end, source, keys: parsed.keys, refCommand };
914
+ }
915
+ }
916
+ }
917
+ return null;
918
+ }
547
919
  function mathSpanAt(value, i) {
548
920
  if (value.startsWith("\\begin{", i)) {
549
921
  return environmentSpan(value, i);
@@ -591,6 +963,12 @@ function detectInlineSpans2(value) {
591
963
  i = cite.end;
592
964
  continue;
593
965
  }
966
+ const ref = refSpan(value, i);
967
+ if (ref) {
968
+ spans.push(ref);
969
+ i = ref.end;
970
+ continue;
971
+ }
594
972
  const math = mathSpanAt(value, i);
595
973
  if (math) {
596
974
  spans.push({ kind: "math", ...math });
@@ -677,6 +1055,16 @@ function createInlineField2(value = "", mathObjects = [], options = {}, path = "
677
1055
  index = span.end;
678
1056
  continue;
679
1057
  }
1058
+ if (span.kind === "ref") {
1059
+ tokens.push({
1060
+ id: document2Id("ref"),
1061
+ kind: "ref",
1062
+ keys: span.keys.length > 0 ? span.keys : [],
1063
+ refCommand: span.refCommand
1064
+ });
1065
+ index = span.end;
1066
+ continue;
1067
+ }
680
1068
  const mathJson = mathObjects[mathObjectIndex];
681
1069
  mathObjectIndex += 1;
682
1070
  if (mathJson && (mathJson.math_mode !== span.opening || mathJson.closing !== span.closing)) {
@@ -725,7 +1113,8 @@ function parseTextBlock(json, options, path, diagnostics) {
725
1113
  id: document2Id("block"),
726
1114
  kind: "textBlock",
727
1115
  command: json.command,
728
- field: createInlineField2(value, json.math_objects ?? [], options, `${path}.value`, diagnostics)
1116
+ field: createInlineField2(value, json.math_objects ?? [], options, `${path}.value`, diagnostics),
1117
+ ...json.centered === true ? { centered: true } : {}
729
1118
  };
730
1119
  }
731
1120
  function parseListItem(json, options, path, diagnostics) {
@@ -778,7 +1167,8 @@ function parseTableBlock(json, options, path, diagnostics) {
778
1167
  command: "\\begin{tabular}",
779
1168
  closing: "\\end{tabular}",
780
1169
  columns: typeof json.columns === "string" ? json.columns : "",
781
- rows
1170
+ rows,
1171
+ ...parseFloatMetaFromJson(json)
782
1172
  };
783
1173
  }
784
1174
  function parseImageBlock(json) {
@@ -790,7 +1180,8 @@ function parseImageBlock(json) {
790
1180
  command: "\\includegraphics",
791
1181
  value,
792
1182
  ...assetId !== void 0 ? { assetId } : {},
793
- options: isRecordOfStrings(json.options) ? json.options : {}
1183
+ options: isRecordOfStrings(json.options) ? json.options : {},
1184
+ ...parseFloatMetaFromJson(json)
794
1185
  };
795
1186
  }
796
1187
  function parseRawBlock(json) {
@@ -845,13 +1236,20 @@ function fromDocumentJson2(json, options = {}) {
845
1236
  const diagnostics = [];
846
1237
  return {
847
1238
  nodeType: "DocumentObject",
1239
+ meta: normalizeDocument2Meta(json.meta),
848
1240
  references: parseReferences(json.references),
849
1241
  blocks: json.blocks.map((block, index) => parseBlock(asBlockJson(block), options, `$.blocks[${String(index)}]`, diagnostics)),
850
1242
  diagnostics
851
1243
  };
852
1244
  }
853
1245
  function createEmptyDocument2() {
854
- return { nodeType: "DocumentObject", references: [], blocks: [], diagnostics: [] };
1246
+ return {
1247
+ nodeType: "DocumentObject",
1248
+ meta: emptyDocument2Meta(),
1249
+ references: [],
1250
+ blocks: [],
1251
+ diagnostics: []
1252
+ };
855
1253
  }
856
1254
 
857
1255
  // src/editor/atomicCommands.ts
@@ -3206,6 +3604,7 @@ function cloneBlock(block) {
3206
3604
  function cloneDocument(document2) {
3207
3605
  return {
3208
3606
  nodeType: "DocumentObject",
3607
+ meta: cloneDocument2Meta(document2.meta ?? emptyDocument2Meta()),
3209
3608
  references: document2.references.map((reference) => ({ ...reference })),
3210
3609
  blocks: document2.blocks.map(cloneBlock),
3211
3610
  diagnostics: document2.diagnostics.map((diagnostic) => ({ ...diagnostic }))
@@ -3316,6 +3715,29 @@ function moveDocument2BlockById(document2, blockId, direction) {
3316
3715
  next.blocks.splice(targetIndex, 0, block);
3317
3716
  return next;
3318
3717
  }
3718
+ function moveDocument2BlockRange(document2, from, to, direction) {
3719
+ const count = document2.blocks.length;
3720
+ if (from < 0 || to < 0 || from >= count || to >= count || from > to) {
3721
+ return document2;
3722
+ }
3723
+ const insertAt = from + direction;
3724
+ if (insertAt < 0 || to + direction >= count) {
3725
+ return document2;
3726
+ }
3727
+ const next = cloneDocument(document2);
3728
+ const slice = next.blocks.splice(from, to - from + 1);
3729
+ next.blocks.splice(insertAt, 0, ...slice);
3730
+ return next;
3731
+ }
3732
+ function removeDocument2BlockRange(document2, from, to) {
3733
+ const count = document2.blocks.length;
3734
+ if (from < 0 || to < 0 || from >= count || to >= count || from > to) {
3735
+ return document2;
3736
+ }
3737
+ const next = cloneDocument(document2);
3738
+ next.blocks = [...next.blocks.slice(0, from), ...next.blocks.slice(to + 1)];
3739
+ return next;
3740
+ }
3319
3741
  function normalizeFieldTokens(tokens) {
3320
3742
  if (tokens.length === 0) {
3321
3743
  return [{ id: document2Id("text"), kind: "text", text: "" }];
@@ -3410,16 +3832,50 @@ function replaceMathTokenFromSession(document2, tokenId, session, opening, closi
3410
3832
  const current = field.tokens[index];
3411
3833
  const open = opening ?? current.opening;
3412
3834
  const close = closing ?? current.closing;
3413
- field.tokens[index] = mathTokenFromSession(session, open, close, side);
3414
- field.tokens[index].id = current.id;
3835
+ const replaced = mathTokenFromSession(session, open, close, side);
3836
+ replaced.id = current.id;
3837
+ if (current.labelEnabled !== void 0) {
3838
+ replaced.labelEnabled = current.labelEnabled;
3839
+ }
3840
+ if (current.label !== void 0) {
3841
+ replaced.label = current.label;
3842
+ }
3843
+ field.tokens[index] = replaced;
3415
3844
  return true;
3416
3845
  });
3417
3846
  return next;
3418
3847
  }
3419
3848
  function addDocument2TextBlock(document2, command = "\\paragraph", afterBlockId) {
3420
- const block = { id: document2Id("block"), kind: "textBlock", command, field: createInlineField2("") };
3849
+ const block = {
3850
+ id: document2Id("block"),
3851
+ kind: "textBlock",
3852
+ command,
3853
+ field: createInlineField2(""),
3854
+ ...command === "\\paragraph" ? { centered: false } : {}
3855
+ };
3421
3856
  return insertDocument2BlockAfter(document2, afterBlockId, block);
3422
3857
  }
3858
+ function updateDocument2TextBlockCentered(document2, blockId, centered) {
3859
+ const next = cloneDocument(document2);
3860
+ function visit(blocks) {
3861
+ for (const block of blocks) {
3862
+ if (block.id === blockId && block.kind === "textBlock" && block.command === "\\paragraph") {
3863
+ block.centered = centered;
3864
+ return true;
3865
+ }
3866
+ if (block.kind === "list") {
3867
+ for (const item of block.items) {
3868
+ if (visit(item.blocks)) {
3869
+ return true;
3870
+ }
3871
+ }
3872
+ }
3873
+ }
3874
+ return false;
3875
+ }
3876
+ visit(next.blocks);
3877
+ return next;
3878
+ }
3423
3879
  function removeDocument2BlockById(document2, blockId) {
3424
3880
  const next = cloneDocument(document2);
3425
3881
  next.blocks = next.blocks.filter((block) => block.id !== blockId);
@@ -3456,7 +3912,8 @@ function addDocument2TableBlock(document2, columns = "lll", rowCount = 3, colCou
3456
3912
  command: "\\begin{tabular}",
3457
3913
  closing: "\\end{tabular}",
3458
3914
  columns: columns || "l".repeat(cols),
3459
- rows: tableRows
3915
+ rows: tableRows,
3916
+ ...defaultFloatMeta()
3460
3917
  };
3461
3918
  return insertDocument2BlockAfter(document2, afterBlockId, block);
3462
3919
  }
@@ -3466,7 +3923,8 @@ function addDocument2ImageBlock(document2, src = "", afterBlockId) {
3466
3923
  kind: "image",
3467
3924
  command: "\\includegraphics",
3468
3925
  value: src,
3469
- options: {}
3926
+ options: { width: "0.8\\textwidth" },
3927
+ ...defaultFloatMeta()
3470
3928
  };
3471
3929
  return insertDocument2BlockAfter(document2, afterBlockId, block);
3472
3930
  }
@@ -3491,6 +3949,128 @@ function updateDocument2ImageValue(document2, blockId, value) {
3491
3949
  visit(next.blocks);
3492
3950
  return next;
3493
3951
  }
3952
+ function updateDocument2ImageMeta(document2, blockId, patch) {
3953
+ const next = cloneDocument(document2);
3954
+ function visit(blocks) {
3955
+ for (const block of blocks) {
3956
+ if (block.id === blockId && block.kind === "image") {
3957
+ applyFloatMetaPatch(block, patch);
3958
+ return true;
3959
+ }
3960
+ if (block.kind === "list") {
3961
+ for (const item of block.items) {
3962
+ if (visit(item.blocks)) {
3963
+ return true;
3964
+ }
3965
+ }
3966
+ }
3967
+ }
3968
+ return false;
3969
+ }
3970
+ visit(next.blocks);
3971
+ return next;
3972
+ }
3973
+ function updateDocument2TableMeta(document2, blockId, patch) {
3974
+ const next = cloneDocument(document2);
3975
+ function visit(blocks) {
3976
+ for (const block of blocks) {
3977
+ if (block.id === blockId && block.kind === "table") {
3978
+ applyFloatMetaPatch(block, patch);
3979
+ return true;
3980
+ }
3981
+ if (block.kind === "list") {
3982
+ for (const item of block.items) {
3983
+ if (visit(item.blocks)) {
3984
+ return true;
3985
+ }
3986
+ }
3987
+ }
3988
+ }
3989
+ return false;
3990
+ }
3991
+ visit(next.blocks);
3992
+ return next;
3993
+ }
3994
+ function updateMathTokenLabel(document2, tokenId, patch) {
3995
+ const next = cloneDocument(document2);
3996
+ visitFields(next.blocks, (field) => {
3997
+ const token = field.tokens.find((entry) => entry.id === tokenId && entry.kind === "math");
3998
+ if (!token) {
3999
+ return false;
4000
+ }
4001
+ if (typeof patch.labelEnabled === "boolean") {
4002
+ token.labelEnabled = patch.labelEnabled;
4003
+ }
4004
+ if (typeof patch.label === "string") {
4005
+ token.label = patch.label;
4006
+ }
4007
+ return true;
4008
+ });
4009
+ return next;
4010
+ }
4011
+ function refTokenFromKeys(keys, refCommand) {
4012
+ return { id: document2Id("ref"), kind: "ref", keys: [...keys], refCommand };
4013
+ }
4014
+ function insertRefTokenAtCaret(document2, fieldId, textTokenId, caretOffset, keys, refCommand = "ref") {
4015
+ if (keys.length === 0) {
4016
+ return document2;
4017
+ }
4018
+ const next = cloneDocument(document2);
4019
+ const refToken = refTokenFromKeys(keys, refCommand);
4020
+ visitFields(next.blocks, (field) => {
4021
+ if (field.id !== fieldId) {
4022
+ return false;
4023
+ }
4024
+ const textIndex = textTokenId ? field.tokens.findIndex((token) => token.id === textTokenId && token.kind === "text") : field.tokens.findIndex((token) => token.kind === "text");
4025
+ if (textIndex < 0) {
4026
+ field.tokens.push(refToken);
4027
+ field.tokens.push({ id: document2Id("text"), kind: "text", text: "" });
4028
+ return true;
4029
+ }
4030
+ const current = field.tokens[textIndex];
4031
+ const [before, after] = splitTextTokenAt(current, caretOffset);
4032
+ const parts = [...field.tokens.slice(0, textIndex)];
4033
+ if (before.text.length > 0) {
4034
+ parts.push(before);
4035
+ }
4036
+ parts.push(refToken);
4037
+ parts.push(after);
4038
+ parts.push(...field.tokens.slice(textIndex + 1));
4039
+ field.tokens = normalizeFieldTokens(parts);
4040
+ return true;
4041
+ });
4042
+ return next;
4043
+ }
4044
+ function updateRefTokenKeys(document2, tokenId, keys, refCommand) {
4045
+ if (keys.length === 0) {
4046
+ return document2;
4047
+ }
4048
+ const next = cloneDocument(document2);
4049
+ visitFields(next.blocks, (field) => {
4050
+ const token = field.tokens.find((entry) => entry.id === tokenId && entry.kind === "ref");
4051
+ if (!token) {
4052
+ return false;
4053
+ }
4054
+ token.keys = [...keys];
4055
+ if (refCommand) {
4056
+ token.refCommand = refCommand;
4057
+ }
4058
+ return true;
4059
+ });
4060
+ return next;
4061
+ }
4062
+ function removeRefTokenById(document2, tokenId) {
4063
+ const next = cloneDocument(document2);
4064
+ visitFields(next.blocks, (field) => {
4065
+ const index = field.tokens.findIndex((token) => token.id === tokenId && token.kind === "ref");
4066
+ if (index < 0) {
4067
+ return false;
4068
+ }
4069
+ field.tokens = normalizeFieldTokens(stitchTextAroundRemovedToken(field.tokens, index));
4070
+ return true;
4071
+ });
4072
+ return next;
4073
+ }
3494
4074
  function mutateListBlockById(blocks, listBlockId, mutator) {
3495
4075
  for (const block of blocks) {
3496
4076
  if (block.id === listBlockId && block.kind === "list") {
@@ -3654,12 +4234,24 @@ function setDocument2References(document2, references) {
3654
4234
  next.references = references.map((reference) => ({ ...reference }));
3655
4235
  return next;
3656
4236
  }
4237
+ function updateDocument2Meta(document2, patch) {
4238
+ const next = cloneDocument(document2);
4239
+ const current = next.meta ?? emptyDocument2Meta();
4240
+ next.meta = {
4241
+ title: typeof patch.title === "string" ? patch.title : current.title,
4242
+ authors: typeof patch.authors === "string" ? patch.authors : current.authors,
4243
+ abstract: typeof patch.abstract === "string" ? patch.abstract : current.abstract,
4244
+ date: patch.date ? normalizeDocument2HijriDate({ ...current.date, ...patch.date }) : { ...current.date }
4245
+ };
4246
+ return next;
4247
+ }
3657
4248
 
3658
4249
  // src/document2/arabicPreamble.ts
3659
- function arabicXeLatexPreamblePkg() {
3660
- return String.raw`
3661
- \documentclass[12pt,a4paper]{article}
3662
- \usepackage{amsmath,amsfonts,amssymb,mathrsfs,tikz,fancyhdr, mathtools}
4250
+ function arabicXeLatexPreamblePkg(twocolumn = false) {
4251
+ const classOptions = twocolumn ? "12pt,a4paper,twocolumn" : "12pt,a4paper";
4252
+ return `
4253
+ \\documentclass[${classOptions}]{article}
4254
+ ` + String.raw`\usepackage{amsmath,amsfonts,amssymb,mathrsfs,tikz,fancyhdr, mathtools}
3663
4255
  \usepackage{fontspec} % For loading OpenType fonts
3664
4256
  \usepackage{unicode-math} % For setting the math font
3665
4257
  \usepackage{polyglossia} % For Arabic support
@@ -3849,11 +4441,21 @@ function arabicXeLatexPreambleCmd() {
3849
4441
  \newcommand{\arbinom}[2]{\left(\begin{array}{c} #1 \\ #2 \end{array} \right)}
3850
4442
  `;
3851
4443
  }
3852
- function getArabicXeLatexPreamble(digitsMapping = "arabicdigits") {
3853
- return arabicXeLatexPreamblePkg() + arabicXeLatexPreambleFont(digitsMapping) + arabicXeLatexPreambleCmd();
4444
+ function getArabicXeLatexPreamble(digitsMappingOrOptions = "arabicdigits") {
4445
+ const options = typeof digitsMappingOrOptions === "string" ? { digitsMapping: digitsMappingOrOptions } : digitsMappingOrOptions;
4446
+ const digitsMapping = options.digitsMapping ?? "arabicdigits";
4447
+ const twocolumn = options.twocolumn === true;
4448
+ return arabicXeLatexPreamblePkg(twocolumn) + arabicXeLatexPreambleFont(digitsMapping) + arabicXeLatexPreambleCmd();
3854
4449
  }
3855
4450
 
3856
4451
  // src/document2/exportLatex.ts
4452
+ function mathBodyLatex(token) {
4453
+ if (!token.math || token.sourceOwner === "raw" || token.sourceOwner === "editor") {
4454
+ return stripDisplayMathDelimiters(token.source);
4455
+ }
4456
+ const rendered = renderMathNodeLatex(token.math);
4457
+ return stripDisplayMathDelimiters(rendered);
4458
+ }
3857
4459
  function tokenLatex(token) {
3858
4460
  if (token.kind === "text") {
3859
4461
  return token.text;
@@ -3861,6 +4463,16 @@ function tokenLatex(token) {
3861
4463
  if (token.kind === "cite") {
3862
4464
  return citeTokenLatex(token.keys);
3863
4465
  }
4466
+ if (token.kind === "ref") {
4467
+ return refTokenLatex(token.keys, token.refCommand);
4468
+ }
4469
+ if (token.display && token.labelEnabled && token.label && token.label.trim().length > 0) {
4470
+ const body = mathBodyLatex(token);
4471
+ return `\\begin{equation}
4472
+ ${body}
4473
+ \\label{${token.label.trim()}}
4474
+ \\end{equation}`;
4475
+ }
3864
4476
  if (!token.math || token.sourceOwner === "raw" || token.sourceOwner === "editor") {
3865
4477
  return token.source;
3866
4478
  }
@@ -3870,7 +4482,13 @@ function inlineFieldLatex(field) {
3870
4482
  return field.tokens.map((token) => tokenLatex(token)).join("");
3871
4483
  }
3872
4484
  function textBlockLatex(block) {
3873
- return `${block.command}{${inlineFieldLatex(block.field)}}`;
4485
+ const body = `${block.command}{${inlineFieldLatex(block.field)}}`;
4486
+ if (block.command === "\\paragraph" && block.centered) {
4487
+ return `\\begin{center}
4488
+ ${body}
4489
+ \\end{center}`;
4490
+ }
4491
+ return body;
3874
4492
  }
3875
4493
  function indent(value) {
3876
4494
  return value.split("\n").map((line) => ` ${line}`).join("\n");
@@ -3884,12 +4502,32 @@ function listBlockLatex(block, references) {
3884
4502
  ${items}
3885
4503
  ${block.closing}`;
3886
4504
  }
3887
- function tableBlockLatex(block) {
4505
+ function tabularInnerLatex(block) {
3888
4506
  const rows = block.rows.map((row) => ` ${row.map((cell) => inlineFieldLatex(cell)).join(" & ")}`).join(" \\\\\n");
3889
4507
  return `${block.command}{${block.columns}}
3890
4508
  ${rows}
3891
4509
  ${block.closing}`;
3892
4510
  }
4511
+ function floatCaptionLabelLines(block) {
4512
+ const lines = [];
4513
+ if (block.captionEnabled && block.caption.trim().length > 0) {
4514
+ lines.push(` \\caption{${block.caption.trim()}}`);
4515
+ }
4516
+ if (block.labelEnabled && block.label.trim().length > 0) {
4517
+ lines.push(` \\label{${block.label.trim()}}`);
4518
+ }
4519
+ return lines;
4520
+ }
4521
+ function tableBlockLatex(block) {
4522
+ const lines = ["\\begin{table}[htbp]"];
4523
+ if (block.centered) {
4524
+ lines.push(" \\centering");
4525
+ }
4526
+ lines.push(...floatCaptionLabelLines(block));
4527
+ lines.push(indent(tabularInnerLatex(block)));
4528
+ lines.push("\\end{table}");
4529
+ return lines.join("\n");
4530
+ }
3893
4531
  function imageOptionsLatex(block) {
3894
4532
  const entries = Object.entries(block.options);
3895
4533
  if (entries.length === 0) {
@@ -3897,6 +4535,16 @@ function imageOptionsLatex(block) {
3897
4535
  }
3898
4536
  return `[${entries.map(([key, value]) => `${key}=${value}`).join(",")}]`;
3899
4537
  }
4538
+ function imageBlockLatex(block) {
4539
+ const lines = ["\\begin{figure}[htbp]"];
4540
+ if (block.centered) {
4541
+ lines.push(" \\centering");
4542
+ }
4543
+ lines.push(` ${block.command}${imageOptionsLatex(block)}{${block.value}}`);
4544
+ lines.push(...floatCaptionLabelLines(block));
4545
+ lines.push("\\end{figure}");
4546
+ return lines.join("\n");
4547
+ }
3900
4548
  function blockLatex(block, references) {
3901
4549
  if (block.kind === "textBlock") {
3902
4550
  return textBlockLatex(block);
@@ -3908,7 +4556,7 @@ function blockLatex(block, references) {
3908
4556
  return tableBlockLatex(block);
3909
4557
  }
3910
4558
  if (block.kind === "image") {
3911
- return `${block.command}${imageOptionsLatex(block)}{${block.value}}`;
4559
+ return imageBlockLatex(block);
3912
4560
  }
3913
4561
  if (block.kind === "bibliography") {
3914
4562
  const width = String(Math.max(references.length, 9));
@@ -3919,13 +4567,42 @@ ${block.closing}`;
3919
4567
  }
3920
4568
  return block.value;
3921
4569
  }
4570
+ function authorLatex(authors) {
4571
+ return authors.split(/\n+/).map((line) => line.trim()).filter((line) => line.length > 0).join(" \\\\ ");
4572
+ }
4573
+ function articleFrontMatterLatex(meta) {
4574
+ const parts = [butexDiwaniDisplayLatex(BUTEX_BASMALA)];
4575
+ if (document2HasMaketitle(meta)) {
4576
+ if (meta.title.trim().length > 0) {
4577
+ parts.push(`\\title{${meta.title.trim()}}`);
4578
+ }
4579
+ if (meta.authors.trim().length > 0) {
4580
+ parts.push(`\\author{${authorLatex(meta.authors)}}`);
4581
+ }
4582
+ parts.push(`\\date{${formatHijriDate(meta.date, { locale: "ar", digitForm: "arabicIndic" })}}`);
4583
+ parts.push("\\maketitle");
4584
+ }
4585
+ if (document2HasAbstract(meta)) {
4586
+ parts.push(`\\begin{abstract}
4587
+ ${meta.abstract.trim()}
4588
+ \\end{abstract}`);
4589
+ }
4590
+ return parts;
4591
+ }
3922
4592
  function document2Latex(document2, options = {}) {
3923
- const body = document2.blocks.map((block) => blockLatex(block, document2.references)).join("\n\n");
4593
+ const meta = document2.meta ?? emptyDocument2Meta();
4594
+ const front = articleFrontMatterLatex(meta);
4595
+ const bodyBlocks = document2.blocks.map((block) => blockLatex(block, document2.references));
4596
+ const closing = butexDiwaniDisplayLatex(BUTEX_CLOSING_HAMDALA);
4597
+ const body = [...front, ...bodyBlocks, closing].filter((part) => part.length > 0).join("\n\n");
3924
4598
  const wrapDocument = options.wrapDocument !== false;
3925
4599
  if (!wrapDocument) {
3926
4600
  return body;
3927
4601
  }
3928
- const preamble = getArabicXeLatexPreamble(options.digitsMapping ?? "arabicdigits");
4602
+ const preamble = getArabicXeLatexPreamble({
4603
+ digitsMapping: options.digitsMapping ?? "arabicdigits",
4604
+ twocolumn: options.twocolumn === true
4605
+ });
3929
4606
  return `${preamble}
3930
4607
  \\begin{document}
3931
4608
 
@@ -3968,6 +4645,67 @@ function document2HistoryCanRedo(stacks) {
3968
4645
  return stacks.future.length > 0;
3969
4646
  }
3970
4647
 
4648
+ // src/document2/blockSelection.ts
4649
+ function emptyBlockSelection() {
4650
+ return { selection: null, anchor: null };
4651
+ }
4652
+ function normalizeBlockSelection(selection, blockCount) {
4653
+ if (!selection) {
4654
+ return null;
4655
+ }
4656
+ const { from, to } = selection;
4657
+ if (blockCount <= 0 || from < 0 || to < 0 || from >= blockCount || to >= blockCount || from > to) {
4658
+ return null;
4659
+ }
4660
+ return { from, to };
4661
+ }
4662
+ function inRange(selection, index) {
4663
+ return index >= selection.from && index <= selection.to;
4664
+ }
4665
+ function toggleBlockInSelection(state, index, blockCount) {
4666
+ if (index < 0 || index >= blockCount) {
4667
+ return state;
4668
+ }
4669
+ const selection = normalizeBlockSelection(state.selection, blockCount);
4670
+ if (!selection) {
4671
+ return { selection: { from: index, to: index }, anchor: index };
4672
+ }
4673
+ if (inRange(selection, index)) {
4674
+ if (selection.from === selection.to) {
4675
+ return emptyBlockSelection();
4676
+ }
4677
+ if (index === selection.from) {
4678
+ return { selection: { from: selection.from + 1, to: selection.to }, anchor: state.anchor };
4679
+ }
4680
+ if (index === selection.to) {
4681
+ return { selection: { from: selection.from, to: selection.to - 1 }, anchor: state.anchor };
4682
+ }
4683
+ return emptyBlockSelection();
4684
+ }
4685
+ if (index === selection.from - 1) {
4686
+ return { selection: { from: index, to: selection.to }, anchor: state.anchor ?? index };
4687
+ }
4688
+ if (index === selection.to + 1) {
4689
+ return { selection: { from: selection.from, to: index }, anchor: state.anchor ?? index };
4690
+ }
4691
+ return { selection: { from: index, to: index }, anchor: index };
4692
+ }
4693
+ function extendBlockSelectionFromAnchor(state, index, blockCount) {
4694
+ if (index < 0 || index >= blockCount) {
4695
+ return state;
4696
+ }
4697
+ const anchor = state.anchor ?? index;
4698
+ const from = Math.min(anchor, index);
4699
+ const to = Math.max(anchor, index);
4700
+ return {
4701
+ selection: { from, to },
4702
+ anchor: state.anchor ?? index
4703
+ };
4704
+ }
4705
+ function remapSelectionAfterRangeMove(selection, direction) {
4706
+ return { from: selection.from + direction, to: selection.to + direction };
4707
+ }
4708
+
3971
4709
  // src/document2/previewModel.ts
3972
4710
  function mathTex(token, equationSide) {
3973
4711
  return mathTokenSourceForSide(token, equationSide);
@@ -3988,6 +4726,18 @@ function previewInlines(field, islands, output, equationSide, document2, preview
3988
4726
  })
3989
4727
  };
3990
4728
  }
4729
+ if (token.kind === "ref") {
4730
+ return {
4731
+ kind: "ref",
4732
+ id: token.id,
4733
+ keys: [...token.keys],
4734
+ refCommand: token.refCommand,
4735
+ label: formatRefLabel(token.keys, document2, token.refCommand, {
4736
+ documentDirection: previewOptions.documentDirection,
4737
+ digitForm: previewOptions.digitForm
4738
+ })
4739
+ };
4740
+ }
3991
4741
  const island = {
3992
4742
  id: token.id,
3993
4743
  tex: mathTex(token, equationSide),
@@ -4010,7 +4760,28 @@ function textBlockPreview(block, islands, output, equationSide, document2, previ
4010
4760
  if (block.command === "\\subsubsection") {
4011
4761
  return { kind: "heading", id: block.id, level: 3, inlines };
4012
4762
  }
4013
- return { kind: "paragraph", id: block.id, inlines };
4763
+ return {
4764
+ kind: "paragraph",
4765
+ id: block.id,
4766
+ inlines,
4767
+ ...block.centered ? { centered: true } : {}
4768
+ };
4769
+ }
4770
+ function floatNumberLabel(document2, key, previewOptions) {
4771
+ if (!key || key.trim().length === 0) {
4772
+ return void 0;
4773
+ }
4774
+ const hit = labelNumberMap(document2).get(key.trim());
4775
+ if (!hit || hit.kind !== "fig" && hit.kind !== "tab") {
4776
+ return void 0;
4777
+ }
4778
+ const locale = previewOptions.uiLocale ?? "ar";
4779
+ const kindWord = hit.kind === "fig" ? locale === "ar" ? "\u0627\u0644\u0634\u0643\u0644" : "Figure" : locale === "ar" ? "\u0627\u0644\u062C\u062F\u0648\u0644" : "Table";
4780
+ const digits = formatBibliographyNumber(hit.number, {
4781
+ documentDirection: previewOptions.documentDirection,
4782
+ digitForm: previewOptions.digitForm
4783
+ });
4784
+ return `${kindWord} ${digits}`;
4014
4785
  }
4015
4786
  function blockPreview(block, islands, output, equationSide, document2, previewOptions) {
4016
4787
  if (block.kind === "textBlock") {
@@ -4029,19 +4800,25 @@ function blockPreview(block, islands, output, equationSide, document2, previewOp
4029
4800
  };
4030
4801
  }
4031
4802
  if (block.kind === "table") {
4803
+ const label = block.labelEnabled ? block.label.trim() : "";
4032
4804
  return {
4033
4805
  kind: "table",
4034
4806
  id: block.id,
4035
- rows: block.rows.map((row) => row.map((cell) => previewInlines(cell, islands, output, equationSide, document2, previewOptions)))
4807
+ rows: block.rows.map((row) => row.map((cell) => previewInlines(cell, islands, output, equationSide, document2, previewOptions))),
4808
+ ...block.captionEnabled && block.caption.trim().length > 0 ? { caption: block.caption.trim() } : {},
4809
+ ...label.length > 0 ? { label, numberLabel: floatNumberLabel(document2, label, previewOptions) } : {}
4036
4810
  };
4037
4811
  }
4038
4812
  if (block.kind === "image") {
4813
+ const label = block.labelEnabled ? block.label.trim() : "";
4039
4814
  return {
4040
4815
  kind: "image",
4041
4816
  id: block.id,
4042
4817
  src: block.value,
4043
4818
  ...block.assetId !== void 0 ? { assetId: block.assetId } : {},
4044
- options: block.options
4819
+ options: block.options,
4820
+ ...block.captionEnabled && block.caption.trim().length > 0 ? { caption: block.caption.trim() } : {},
4821
+ ...label.length > 0 ? { label, numberLabel: floatNumberLabel(document2, label, previewOptions) } : {}
4045
4822
  };
4046
4823
  }
4047
4824
  if (block.kind === "bibliography") {
@@ -4065,54 +4842,106 @@ function blockPreview(block, islands, output, equationSide, document2, previewOp
4065
4842
  }
4066
4843
  return { kind: "omit", id: block.id };
4067
4844
  }
4845
+ function articleChromePreview(document2, uiLocale, previewOptions) {
4846
+ const meta = document2.meta ?? emptyDocument2Meta();
4847
+ const blocks = [
4848
+ { kind: "basmala", id: "article-basmala", text: BUTEX_BASMALA, tex: butexDiwaniDisplayTex(BUTEX_BASMALA) }
4849
+ ];
4850
+ if (document2HasMaketitle(meta)) {
4851
+ blocks.push({
4852
+ kind: "titleBlock",
4853
+ id: "article-title",
4854
+ ...meta.title.trim().length > 0 ? { title: meta.title.trim() } : {},
4855
+ ...meta.authors.trim().length > 0 ? { authors: meta.authors.trim() } : {},
4856
+ date: formatHijriDate(meta.date, {
4857
+ locale: uiLocale,
4858
+ digitForm: previewOptions.digitForm
4859
+ })
4860
+ });
4861
+ }
4862
+ if (document2HasAbstract(meta)) {
4863
+ blocks.push({ kind: "abstract", id: "article-abstract", text: meta.abstract.trim() });
4864
+ }
4865
+ return blocks;
4866
+ }
4068
4867
  function document2Preview(document2, output = "svg", equationSide = "arabic", previewOptions = {}) {
4069
4868
  const mathIslands = [];
4070
4869
  const options = {
4071
4870
  documentDirection: previewOptions.documentDirection ?? "rtl",
4072
- digitForm: previewOptions.digitForm ?? (previewOptions.documentDirection === "ltr" ? "western" : "arabicIndic")
4871
+ digitForm: previewOptions.digitForm ?? (previewOptions.documentDirection === "ltr" ? "western" : "arabicIndic"),
4872
+ uiLocale: previewOptions.uiLocale ?? "ar"
4073
4873
  };
4874
+ const body = document2.blocks.map((block) => blockPreview(block, mathIslands, output, equationSide, document2, options));
4074
4875
  return {
4075
- blocks: document2.blocks.map((block) => blockPreview(block, mathIslands, output, equationSide, document2, options)),
4876
+ blocks: [
4877
+ ...articleChromePreview(document2, options.uiLocale ?? "ar", options),
4878
+ ...body,
4879
+ { kind: "closing", id: "article-closing", text: BUTEX_CLOSING_HAMDALA, tex: butexDiwaniDisplayTex(BUTEX_CLOSING_HAMDALA) }
4880
+ ],
4076
4881
  mathIslands
4077
4882
  };
4078
4883
  }
4079
4884
  export {
4885
+ BUTEX_BASMALA,
4886
+ BUTEX_CLOSING_HAMDALA,
4080
4887
  DEFAULT_DOCUMENT2_HISTORY_MAX_DEPTH,
4888
+ DEFAULT_HIJRI_DATE,
4889
+ HIJRI_MONTH_IDS,
4890
+ HIJRI_YEAR_MAX,
4891
+ HIJRI_YEAR_MIN,
4081
4892
  addDocument2ImageBlock,
4082
4893
  addDocument2ListBlock,
4083
4894
  addDocument2ListItem,
4084
4895
  addDocument2Reference,
4085
4896
  addDocument2TableBlock,
4086
4897
  addDocument2TextBlock,
4898
+ applyFloatMetaPatch,
4087
4899
  arabicXeLatexPreambleCmd,
4088
4900
  arabicXeLatexPreambleFont,
4089
4901
  arabicXeLatexPreamblePkg,
4090
4902
  bibliographyEntryLatex,
4903
+ butexDiwaniDisplayLatex,
4904
+ butexDiwaniDisplayTex,
4091
4905
  citeTokenLatex,
4906
+ cloneDocument2Meta,
4092
4907
  cloneDocument2Node,
4908
+ collectDocument2Labels,
4093
4909
  createDocument2History,
4094
4910
  createEmptyArabicEquationSession,
4095
4911
  createEmptyDocument2,
4096
4912
  createEmptyEquationSession,
4097
4913
  createEmptyReference2,
4098
4914
  createInlineField2,
4915
+ defaultFloatMeta,
4099
4916
  detectInlineSpans2,
4100
4917
  detectMathSpans2,
4918
+ document2HasAbstract,
4919
+ document2HasMaketitle,
4920
+ document2HasTitleStack,
4101
4921
  document2HistoryCanRedo,
4102
4922
  document2HistoryCanUndo,
4103
4923
  document2Latex,
4104
4924
  document2Preview,
4925
+ emptyBlockSelection,
4926
+ emptyDocument2Meta,
4105
4927
  ensureDocument2BibliographyBlock,
4928
+ extendBlockSelectionFromAnchor,
4929
+ fillDocument2MetaGaps,
4106
4930
  findFirstInlineField,
4107
4931
  formatBibliographyNumber,
4108
4932
  formatCiteLabel,
4933
+ formatHijriDate,
4934
+ formatRefLabel,
4109
4935
  fromDocumentJson2,
4110
4936
  getArabicXeLatexPreamble,
4937
+ hijriMonthLabel,
4111
4938
  inlineFieldLatex,
4112
4939
  insertCiteTokenAtCaret,
4113
4940
  insertDocument2BlockAfter,
4114
4941
  insertMathToken,
4115
4942
  insertMathTokenAtCaret,
4943
+ insertRefTokenAtCaret,
4944
+ labelNumberMap,
4116
4945
  mathNodeFromArabicSession,
4117
4946
  mathNodeFromSession,
4118
4947
  mathSourceFromArabicSession,
@@ -4120,25 +4949,44 @@ export {
4120
4949
  mathTokenSourceForSide,
4121
4950
  mathTokenToArabicEditorSession,
4122
4951
  mathTokenToEditorSession,
4952
+ mergeDocument2Meta,
4123
4953
  moveDocument2BlockById,
4954
+ moveDocument2BlockRange,
4124
4955
  moveDocument2Reference,
4956
+ normalizeBlockSelection,
4957
+ normalizeDocument2HijriDate,
4958
+ normalizeDocument2Meta,
4125
4959
  parseCiteKeys,
4960
+ parseFloatMetaFromJson,
4961
+ parseRefKeys,
4126
4962
  pushDocument2Snapshot,
4963
+ refTokenLatex,
4127
4964
  referenceFromJson,
4128
4965
  referenceNumberMap,
4966
+ remapSelectionAfterRangeMove,
4129
4967
  removeCiteTokenById,
4130
4968
  removeDocument2BlockById,
4969
+ removeDocument2BlockRange,
4131
4970
  removeDocument2ListItem,
4132
4971
  removeDocument2Reference,
4133
4972
  removeMathTokenById,
4973
+ removeRefTokenById,
4134
4974
  replaceMathTokenFromSession,
4135
4975
  resolveCiteNumbers,
4136
4976
  restoreDocument2Redo,
4137
4977
  restoreDocument2Undo,
4138
4978
  setDocument2References,
4979
+ stripDisplayMathDelimiters,
4980
+ toggleBlockInSelection,
4139
4981
  updateCiteTokenKeys,
4982
+ updateDocument2ImageMeta,
4140
4983
  updateDocument2ImageValue,
4984
+ updateDocument2Meta,
4141
4985
  updateDocument2Reference,
4986
+ updateDocument2TableMeta,
4987
+ updateDocument2TextBlockCentered,
4988
+ updateMathTokenLabel,
4989
+ updateRefTokenKeys,
4142
4990
  updateTextToken
4143
4991
  };
4144
4992
  //# sourceMappingURL=document2.mjs.map