@drghaliasri/butex 5.0.0 → 5.3.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,203 @@ function formatDigits(value, digitForm = "western") {
34
34
  }).join("");
35
35
  }
36
36
 
37
+ // src/document2/articleMeta.ts
38
+ var BUTEX_BASMALA_LINE1 = "\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";
39
+ var BUTEX_BASMALA_SALAWAT = "\u0635\u064E\u0644\u064E\u0651\u0649 \u0627\u0644\u0644\u064E\u0651\u0647\u064F \u0639\u064E\u0644\u064E\u064A\u0652\u0647\u0650 \u0648\u064E\u0633\u064E\u0644\u064E\u0651\u0645\u064E";
40
+ var BUTEX_BASMALA = `${BUTEX_BASMALA_LINE1}
41
+ ${BUTEX_BASMALA_SALAWAT}`;
42
+ 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";
43
+ function escapeButexTextArg(text) {
44
+ return text.replace(/\\/g, "\\textbackslash{}").replace(/[{}]/g, "\\$&");
45
+ }
46
+ function butexDiwaniDisplayTex(text) {
47
+ return `\\butexdiwani{${escapeButexTextArg(text)}}`;
48
+ }
49
+ function butexDiwaniDisplayLatex(text) {
50
+ return `\\[
51
+ ${butexDiwaniDisplayTex(text)}
52
+ \\]`;
53
+ }
54
+ function butexBasmalaDisplayTex() {
55
+ return `${butexDiwaniDisplayTex(BUTEX_BASMALA_LINE1)} \\\\ ${butexDiwaniDisplayTex(BUTEX_BASMALA_SALAWAT)}`;
56
+ }
57
+ function butexBasmalaDisplayLatex() {
58
+ return `\\[
59
+ ${butexBasmalaDisplayTex()}
60
+ \\]`;
61
+ }
62
+ function butexBasmalaTitlingPreamble() {
63
+ return [
64
+ "\\usepackage{titling}",
65
+ "\\pretitle{%",
66
+ " \\begin{center}",
67
+ ` ${butexDiwaniDisplayTex(BUTEX_BASMALA_LINE1)}\\\\`,
68
+ ` ${butexDiwaniDisplayTex(BUTEX_BASMALA_SALAWAT)}`,
69
+ " \\par\\vspace{1.5em}",
70
+ " \\LARGE",
71
+ "}",
72
+ "\\posttitle{\\par\\end{center}}"
73
+ ].join("\n");
74
+ }
75
+ var HIJRI_YEAR_MIN = 1400;
76
+ var HIJRI_YEAR_MAX = 1500;
77
+ var HIJRI_MONTH_IDS = [
78
+ "\u0645\u062D\u0631\u0645",
79
+ "\u0635\u0641\u0631",
80
+ "\u0631\u0628\u064A\u0639 \u0627\u0644\u0623\u0648\u0644",
81
+ "\u0631\u0628\u064A\u0639 \u0627\u0644\u0622\u062E\u0631",
82
+ "\u062C\u0645\u0627\u062F\u0649 \u0627\u0644\u0623\u0648\u0644\u0649",
83
+ "\u062C\u0645\u0627\u062F\u0649 \u0627\u0644\u0622\u062E\u0631\u0629",
84
+ "\u0631\u062C\u0628",
85
+ "\u0634\u0639\u0628\u0627\u0646",
86
+ "\u0631\u0645\u0636\u0627\u0646",
87
+ "\u0634\u0648\u0627\u0644",
88
+ "\u0630\u0648 \u0627\u0644\u0642\u0639\u062F\u0629",
89
+ "\u0630\u0648 \u0627\u0644\u062D\u062C\u0629"
90
+ ];
91
+ var HIJRI_MONTH_LABELS_EN = {
92
+ \u0645\u062D\u0631\u0645: "Muharram",
93
+ \u0635\u0641\u0631: "Safar",
94
+ "\u0631\u0628\u064A\u0639 \u0627\u0644\u0623\u0648\u0644": "Rabi\u02BB I",
95
+ "\u0631\u0628\u064A\u0639 \u0627\u0644\u0622\u062E\u0631": "Rabi\u02BB II",
96
+ "\u062C\u0645\u0627\u062F\u0649 \u0627\u0644\u0623\u0648\u0644\u0649": "Jumada I",
97
+ "\u062C\u0645\u0627\u062F\u0649 \u0627\u0644\u0622\u062E\u0631\u0629": "Jumada II",
98
+ \u0631\u062C\u0628: "Rajab",
99
+ \u0634\u0639\u0628\u0627\u0646: "Sha\u02BBban",
100
+ \u0631\u0645\u0636\u0627\u0646: "Ramadan",
101
+ \u0634\u0648\u0627\u0644: "Shawwal",
102
+ "\u0630\u0648 \u0627\u0644\u0642\u0639\u062F\u0629": "Dhul Qa\u02BBdah",
103
+ "\u0630\u0648 \u0627\u0644\u062D\u062C\u0629": "Dhul Hijjah"
104
+ };
105
+ var LEGACY_LATIN_MONTH = {
106
+ muharram: "\u0645\u062D\u0631\u0645",
107
+ safar: "\u0635\u0641\u0631",
108
+ rabiAwwal: "\u0631\u0628\u064A\u0639 \u0627\u0644\u0623\u0648\u0644",
109
+ rabiThani: "\u0631\u0628\u064A\u0639 \u0627\u0644\u0622\u062E\u0631",
110
+ jumadaAwwal: "\u062C\u0645\u0627\u062F\u0649 \u0627\u0644\u0623\u0648\u0644\u0649",
111
+ jumadaThani: "\u062C\u0645\u0627\u062F\u0649 \u0627\u0644\u0622\u062E\u0631\u0629",
112
+ rajab: "\u0631\u062C\u0628",
113
+ shaban: "\u0634\u0639\u0628\u0627\u0646",
114
+ ramadan: "\u0631\u0645\u0636\u0627\u0646",
115
+ shawwal: "\u0634\u0648\u0627\u0644",
116
+ dhulQadah: "\u0630\u0648 \u0627\u0644\u0642\u0639\u062F\u0629",
117
+ dhulHijjah: "\u0630\u0648 \u0627\u0644\u062D\u062C\u0629"
118
+ };
119
+ var DEFAULT_HIJRI_DATE = {
120
+ day: 1,
121
+ month: "\u0645\u062D\u0631\u0645",
122
+ year: 1448
123
+ };
124
+ function hijriMonthLabel(month, locale = "ar") {
125
+ return locale === "en" ? HIJRI_MONTH_LABELS_EN[month] : month;
126
+ }
127
+ function emptyDocument2Meta() {
128
+ return {
129
+ title: "",
130
+ authors: "",
131
+ date: { ...DEFAULT_HIJRI_DATE },
132
+ abstract: ""
133
+ };
134
+ }
135
+ function clampInt(value, min, max) {
136
+ if (!Number.isFinite(value)) {
137
+ return min;
138
+ }
139
+ return Math.min(max, Math.max(min, Math.trunc(value)));
140
+ }
141
+ function isHijriMonthId(value) {
142
+ return typeof value === "string" && HIJRI_MONTH_IDS.includes(value);
143
+ }
144
+ function resolveHijriMonthId(value) {
145
+ if (isHijriMonthId(value)) {
146
+ return value;
147
+ }
148
+ if (typeof value === "string" && value in LEGACY_LATIN_MONTH) {
149
+ return LEGACY_LATIN_MONTH[value];
150
+ }
151
+ return DEFAULT_HIJRI_DATE.month;
152
+ }
153
+ function normalizeDocument2HijriDate(value) {
154
+ if (typeof value !== "object" || value === null) {
155
+ return { ...DEFAULT_HIJRI_DATE };
156
+ }
157
+ const raw = value;
158
+ const day = clampInt(typeof raw.day === "number" ? raw.day : Number(raw.day), 1, 30);
159
+ const year = clampInt(typeof raw.year === "number" ? raw.year : Number(raw.year), HIJRI_YEAR_MIN, HIJRI_YEAR_MAX);
160
+ const month = resolveHijriMonthId(raw.month);
161
+ return { day, month, year };
162
+ }
163
+ function normalizeDocument2Meta(value) {
164
+ if (typeof value !== "object" || value === null) {
165
+ return emptyDocument2Meta();
166
+ }
167
+ const raw = value;
168
+ return {
169
+ title: typeof raw.title === "string" ? raw.title : "",
170
+ authors: typeof raw.authors === "string" ? raw.authors : "",
171
+ date: normalizeDocument2HijriDate(raw.date),
172
+ abstract: typeof raw.abstract === "string" ? raw.abstract : ""
173
+ };
174
+ }
175
+ function mergeDocument2Meta(fromJson, fromProp) {
176
+ const fromPropMeta = {
177
+ title: typeof fromProp?.title === "string" ? fromProp.title : "",
178
+ authors: typeof fromProp?.authors === "string" ? fromProp.authors : "",
179
+ abstract: typeof fromProp?.abstract === "string" ? fromProp.abstract : "",
180
+ date: fromProp?.date ? normalizeDocument2HijriDate({ ...DEFAULT_HIJRI_DATE, ...fromProp.date }) : { ...DEFAULT_HIJRI_DATE }
181
+ };
182
+ if (typeof fromJson !== "object" || fromJson === null) {
183
+ return fromPropMeta;
184
+ }
185
+ const json = fromJson;
186
+ return {
187
+ title: "title" in json && typeof json.title === "string" ? json.title : fromPropMeta.title,
188
+ authors: "authors" in json && typeof json.authors === "string" ? json.authors : fromPropMeta.authors,
189
+ abstract: "abstract" in json && typeof json.abstract === "string" ? json.abstract : fromPropMeta.abstract,
190
+ date: "date" in json ? normalizeDocument2HijriDate(json.date) : fromPropMeta.date
191
+ };
192
+ }
193
+ function fillDocument2MetaGaps(current, fromProp) {
194
+ if (!fromProp) {
195
+ return cloneDocument2Meta(current);
196
+ }
197
+ return {
198
+ title: current.title.trim().length > 0 ? current.title : typeof fromProp.title === "string" ? fromProp.title : current.title,
199
+ authors: current.authors.trim().length > 0 ? current.authors : typeof fromProp.authors === "string" ? fromProp.authors : current.authors,
200
+ abstract: current.abstract.trim().length > 0 ? current.abstract : typeof fromProp.abstract === "string" ? fromProp.abstract : current.abstract,
201
+ date: fromProp.date ? normalizeDocument2HijriDate({ ...current.date, ...fromProp.date }) : { ...current.date }
202
+ };
203
+ }
204
+ function document2HasMaketitle(meta) {
205
+ return meta.title.trim().length > 0 || meta.authors.trim().length > 0;
206
+ }
207
+ function document2HasAbstract(meta) {
208
+ return meta.abstract.trim().length > 0;
209
+ }
210
+ function document2HasTitleStack(meta) {
211
+ return document2HasMaketitle(meta) || document2HasAbstract(meta);
212
+ }
213
+ function formatHijriDate(date, options = "ar") {
214
+ const opts = typeof options === "string" ? { locale: options } : options;
215
+ const locale = opts.locale ?? "ar";
216
+ const digitForm = opts.digitForm ?? (locale === "en" ? "western" : "arabicIndic");
217
+ const month = hijriMonthLabel(date.month, locale);
218
+ const day = formatDigits(String(date.day), digitForm);
219
+ const year = formatDigits(String(date.year), digitForm);
220
+ if (locale === "en") {
221
+ return `${day} ${month} ${year} AH`;
222
+ }
223
+ return `${day} ${month} ${year}\u0647\u0640`;
224
+ }
225
+ function cloneDocument2Meta(meta) {
226
+ return {
227
+ title: meta.title,
228
+ authors: meta.authors,
229
+ date: { ...meta.date },
230
+ abstract: meta.abstract
231
+ };
232
+ }
233
+
37
234
  // src/document2/citations.ts
38
235
  function referenceNumberMap(references) {
39
236
  const map = /* @__PURE__ */ new Map();
@@ -469,6 +666,168 @@ ${lines.map((line) => ` ${line}`).join(" \\\\\n")}
469
666
  ${math.closing}`;
470
667
  }
471
668
 
669
+ // src/document2/labels.ts
670
+ function defaultFloatMeta() {
671
+ return {
672
+ centered: true,
673
+ captionEnabled: false,
674
+ caption: "",
675
+ labelEnabled: false,
676
+ label: ""
677
+ };
678
+ }
679
+ function parseFloatMetaFromJson(json) {
680
+ const caption = typeof json.caption === "string" ? json.caption : "";
681
+ const label = typeof json.label === "string" ? json.label : "";
682
+ return {
683
+ centered: json.centered !== false,
684
+ captionEnabled: json.caption_enabled === true || typeof json.caption_enabled !== "boolean" && caption.length > 0,
685
+ caption,
686
+ labelEnabled: json.label_enabled === true || typeof json.label_enabled !== "boolean" && label.trim().length > 0,
687
+ label
688
+ };
689
+ }
690
+ function parseRefKeys(source) {
691
+ const eqref = /^\\eqref\{([^}]*)\}$/.exec(source.trim());
692
+ if (eqref) {
693
+ return {
694
+ refCommand: "eqref",
695
+ keys: (eqref[1] ?? "").split(",").map((key) => key.trim()).filter((key) => key.length > 0)
696
+ };
697
+ }
698
+ const ref = /^\\ref\{([^}]*)\}$/.exec(source.trim());
699
+ if (ref) {
700
+ return {
701
+ refCommand: "ref",
702
+ keys: (ref[1] ?? "").split(",").map((key) => key.trim()).filter((key) => key.length > 0)
703
+ };
704
+ }
705
+ return { keys: [], refCommand: "ref" };
706
+ }
707
+ function refTokenLatex(keys, refCommand) {
708
+ const command = refCommand === "eqref" ? "\\eqref" : "\\ref";
709
+ return `${command}{${keys.join(",")}}`;
710
+ }
711
+ function collectDocument2Labels(document2) {
712
+ const entries = [];
713
+ const counters = { fig: 0, tab: 0, eq: 0 };
714
+ function pushEqFromField(tokens) {
715
+ for (const token of tokens) {
716
+ if (token.kind !== "math") {
717
+ continue;
718
+ }
719
+ if (token.display && token.labelEnabled && token.label && token.label.trim().length > 0) {
720
+ counters.eq += 1;
721
+ entries.push({
722
+ key: token.label.trim(),
723
+ kind: "eq",
724
+ caption: "",
725
+ ownerId: token.id,
726
+ number: counters.eq
727
+ });
728
+ }
729
+ }
730
+ }
731
+ function walk(blocks) {
732
+ for (const block of blocks) {
733
+ if (block.kind === "image" && block.labelEnabled && block.label.trim().length > 0) {
734
+ counters.fig += 1;
735
+ entries.push({
736
+ key: block.label.trim(),
737
+ kind: "fig",
738
+ caption: block.captionEnabled ? block.caption : "",
739
+ ownerId: block.id,
740
+ number: counters.fig
741
+ });
742
+ }
743
+ if (block.kind === "table") {
744
+ if (block.labelEnabled && block.label.trim().length > 0) {
745
+ counters.tab += 1;
746
+ entries.push({
747
+ key: block.label.trim(),
748
+ kind: "tab",
749
+ caption: block.captionEnabled ? block.caption : "",
750
+ ownerId: block.id,
751
+ number: counters.tab
752
+ });
753
+ }
754
+ for (const row of block.rows) {
755
+ for (const cell of row) {
756
+ pushEqFromField(cell.tokens);
757
+ }
758
+ }
759
+ }
760
+ if (block.kind === "textBlock") {
761
+ pushEqFromField(block.field.tokens);
762
+ }
763
+ if (block.kind === "list") {
764
+ for (const item of block.items) {
765
+ pushEqFromField(item.field.tokens);
766
+ walk(item.blocks);
767
+ }
768
+ }
769
+ }
770
+ }
771
+ walk(document2.blocks);
772
+ return entries;
773
+ }
774
+ function labelNumberMap(document2) {
775
+ const map = /* @__PURE__ */ new Map();
776
+ for (const entry of collectDocument2Labels(document2)) {
777
+ if (!map.has(entry.key)) {
778
+ map.set(entry.key, { kind: entry.kind, number: entry.number });
779
+ }
780
+ }
781
+ return map;
782
+ }
783
+ function formatRefLabel(keys, document2, refCommand, options = {}) {
784
+ const documentDirection = options.documentDirection ?? "rtl";
785
+ const digitForm = options.digitForm ?? (documentDirection === "rtl" ? "arabicIndic" : "western");
786
+ const map = labelNumberMap(document2);
787
+ const parts = keys.map((key) => {
788
+ const hit = map.get(key);
789
+ if (!hit) {
790
+ return "?";
791
+ }
792
+ return formatDigits(String(hit.number), digitForm);
793
+ });
794
+ const body = parts.join(documentDirection === "rtl" ? "\u060C" : ", ");
795
+ if (refCommand === "eqref") {
796
+ return `(${body})`;
797
+ }
798
+ return body;
799
+ }
800
+ function stripDisplayMathDelimiters(source) {
801
+ const trimmed = source.trim();
802
+ if (trimmed.startsWith("\\[") && trimmed.endsWith("\\]")) {
803
+ return trimmed.slice(2, -2).trim();
804
+ }
805
+ if (trimmed.startsWith("$$") && trimmed.endsWith("$$")) {
806
+ return trimmed.slice(2, -2).trim();
807
+ }
808
+ if (trimmed.startsWith("\\begin{equation}") && trimmed.endsWith("\\end{equation}")) {
809
+ return trimmed.slice("\\begin{equation}".length, -"\\end{equation}".length).trim();
810
+ }
811
+ return trimmed;
812
+ }
813
+ function applyFloatMetaPatch(block, patch) {
814
+ if (typeof patch.centered === "boolean") {
815
+ block.centered = patch.centered;
816
+ }
817
+ if (typeof patch.captionEnabled === "boolean") {
818
+ block.captionEnabled = patch.captionEnabled;
819
+ }
820
+ if (typeof patch.caption === "string") {
821
+ block.caption = patch.caption;
822
+ }
823
+ if (typeof patch.labelEnabled === "boolean") {
824
+ block.labelEnabled = patch.labelEnabled;
825
+ }
826
+ if (typeof patch.label === "string") {
827
+ block.label = patch.label;
828
+ }
829
+ }
830
+
472
831
  // src/document2/inlineScanner.ts
473
832
  var MATH_ENVIRONMENTS = /* @__PURE__ */ new Set([
474
833
  "equation",
@@ -544,6 +903,43 @@ function citeSpan(value, start) {
544
903
  }
545
904
  return null;
546
905
  }
906
+ function refSpan(value, start) {
907
+ const eqrefPrefix = "\\eqref{";
908
+ const refPrefix = "\\ref{";
909
+ let refCommand = "ref";
910
+ let prefix = refPrefix;
911
+ if (value.startsWith(eqrefPrefix, start) && !isEscaped(value, start)) {
912
+ refCommand = "eqref";
913
+ prefix = eqrefPrefix;
914
+ } else if (value.startsWith(refPrefix, start) && !isEscaped(value, start)) {
915
+ refCommand = "ref";
916
+ prefix = refPrefix;
917
+ } else {
918
+ return null;
919
+ }
920
+ const openBrace = start + prefix.length - 1;
921
+ if (value[openBrace] !== "{") {
922
+ return null;
923
+ }
924
+ let depth = 0;
925
+ for (let i = openBrace; i < value.length; i += 1) {
926
+ const char = value[i];
927
+ if (char === "{" && !isEscaped(value, i)) {
928
+ depth += 1;
929
+ continue;
930
+ }
931
+ if (char === "}" && !isEscaped(value, i)) {
932
+ depth -= 1;
933
+ if (depth === 0) {
934
+ const end = i + 1;
935
+ const source = value.slice(start, end);
936
+ const parsed = parseRefKeys(source);
937
+ return { kind: "ref", start, end, source, keys: parsed.keys, refCommand };
938
+ }
939
+ }
940
+ }
941
+ return null;
942
+ }
547
943
  function mathSpanAt(value, i) {
548
944
  if (value.startsWith("\\begin{", i)) {
549
945
  return environmentSpan(value, i);
@@ -591,6 +987,12 @@ function detectInlineSpans2(value) {
591
987
  i = cite.end;
592
988
  continue;
593
989
  }
990
+ const ref = refSpan(value, i);
991
+ if (ref) {
992
+ spans.push(ref);
993
+ i = ref.end;
994
+ continue;
995
+ }
594
996
  const math = mathSpanAt(value, i);
595
997
  if (math) {
596
998
  spans.push({ kind: "math", ...math });
@@ -677,6 +1079,16 @@ function createInlineField2(value = "", mathObjects = [], options = {}, path = "
677
1079
  index = span.end;
678
1080
  continue;
679
1081
  }
1082
+ if (span.kind === "ref") {
1083
+ tokens.push({
1084
+ id: document2Id("ref"),
1085
+ kind: "ref",
1086
+ keys: span.keys.length > 0 ? span.keys : [],
1087
+ refCommand: span.refCommand
1088
+ });
1089
+ index = span.end;
1090
+ continue;
1091
+ }
680
1092
  const mathJson = mathObjects[mathObjectIndex];
681
1093
  mathObjectIndex += 1;
682
1094
  if (mathJson && (mathJson.math_mode !== span.opening || mathJson.closing !== span.closing)) {
@@ -725,7 +1137,8 @@ function parseTextBlock(json, options, path, diagnostics) {
725
1137
  id: document2Id("block"),
726
1138
  kind: "textBlock",
727
1139
  command: json.command,
728
- field: createInlineField2(value, json.math_objects ?? [], options, `${path}.value`, diagnostics)
1140
+ field: createInlineField2(value, json.math_objects ?? [], options, `${path}.value`, diagnostics),
1141
+ ...json.centered === true ? { centered: true } : {}
729
1142
  };
730
1143
  }
731
1144
  function parseListItem(json, options, path, diagnostics) {
@@ -778,7 +1191,8 @@ function parseTableBlock(json, options, path, diagnostics) {
778
1191
  command: "\\begin{tabular}",
779
1192
  closing: "\\end{tabular}",
780
1193
  columns: typeof json.columns === "string" ? json.columns : "",
781
- rows
1194
+ rows,
1195
+ ...parseFloatMetaFromJson(json)
782
1196
  };
783
1197
  }
784
1198
  function parseImageBlock(json) {
@@ -790,7 +1204,8 @@ function parseImageBlock(json) {
790
1204
  command: "\\includegraphics",
791
1205
  value,
792
1206
  ...assetId !== void 0 ? { assetId } : {},
793
- options: isRecordOfStrings(json.options) ? json.options : {}
1207
+ options: isRecordOfStrings(json.options) ? json.options : {},
1208
+ ...parseFloatMetaFromJson(json)
794
1209
  };
795
1210
  }
796
1211
  function parseRawBlock(json) {
@@ -845,13 +1260,20 @@ function fromDocumentJson2(json, options = {}) {
845
1260
  const diagnostics = [];
846
1261
  return {
847
1262
  nodeType: "DocumentObject",
1263
+ meta: normalizeDocument2Meta(json.meta),
848
1264
  references: parseReferences(json.references),
849
1265
  blocks: json.blocks.map((block, index) => parseBlock(asBlockJson(block), options, `$.blocks[${String(index)}]`, diagnostics)),
850
1266
  diagnostics
851
1267
  };
852
1268
  }
853
1269
  function createEmptyDocument2() {
854
- return { nodeType: "DocumentObject", references: [], blocks: [], diagnostics: [] };
1270
+ return {
1271
+ nodeType: "DocumentObject",
1272
+ meta: emptyDocument2Meta(),
1273
+ references: [],
1274
+ blocks: [],
1275
+ diagnostics: []
1276
+ };
855
1277
  }
856
1278
 
857
1279
  // src/editor/atomicCommands.ts
@@ -3206,6 +3628,7 @@ function cloneBlock(block) {
3206
3628
  function cloneDocument(document2) {
3207
3629
  return {
3208
3630
  nodeType: "DocumentObject",
3631
+ meta: cloneDocument2Meta(document2.meta ?? emptyDocument2Meta()),
3209
3632
  references: document2.references.map((reference) => ({ ...reference })),
3210
3633
  blocks: document2.blocks.map(cloneBlock),
3211
3634
  diagnostics: document2.diagnostics.map((diagnostic) => ({ ...diagnostic }))
@@ -3316,6 +3739,29 @@ function moveDocument2BlockById(document2, blockId, direction) {
3316
3739
  next.blocks.splice(targetIndex, 0, block);
3317
3740
  return next;
3318
3741
  }
3742
+ function moveDocument2BlockRange(document2, from, to, direction) {
3743
+ const count = document2.blocks.length;
3744
+ if (from < 0 || to < 0 || from >= count || to >= count || from > to) {
3745
+ return document2;
3746
+ }
3747
+ const insertAt = from + direction;
3748
+ if (insertAt < 0 || to + direction >= count) {
3749
+ return document2;
3750
+ }
3751
+ const next = cloneDocument(document2);
3752
+ const slice = next.blocks.splice(from, to - from + 1);
3753
+ next.blocks.splice(insertAt, 0, ...slice);
3754
+ return next;
3755
+ }
3756
+ function removeDocument2BlockRange(document2, from, to) {
3757
+ const count = document2.blocks.length;
3758
+ if (from < 0 || to < 0 || from >= count || to >= count || from > to) {
3759
+ return document2;
3760
+ }
3761
+ const next = cloneDocument(document2);
3762
+ next.blocks = [...next.blocks.slice(0, from), ...next.blocks.slice(to + 1)];
3763
+ return next;
3764
+ }
3319
3765
  function normalizeFieldTokens(tokens) {
3320
3766
  if (tokens.length === 0) {
3321
3767
  return [{ id: document2Id("text"), kind: "text", text: "" }];
@@ -3410,16 +3856,50 @@ function replaceMathTokenFromSession(document2, tokenId, session, opening, closi
3410
3856
  const current = field.tokens[index];
3411
3857
  const open = opening ?? current.opening;
3412
3858
  const close = closing ?? current.closing;
3413
- field.tokens[index] = mathTokenFromSession(session, open, close, side);
3414
- field.tokens[index].id = current.id;
3859
+ const replaced = mathTokenFromSession(session, open, close, side);
3860
+ replaced.id = current.id;
3861
+ if (current.labelEnabled !== void 0) {
3862
+ replaced.labelEnabled = current.labelEnabled;
3863
+ }
3864
+ if (current.label !== void 0) {
3865
+ replaced.label = current.label;
3866
+ }
3867
+ field.tokens[index] = replaced;
3415
3868
  return true;
3416
3869
  });
3417
3870
  return next;
3418
3871
  }
3419
3872
  function addDocument2TextBlock(document2, command = "\\paragraph", afterBlockId) {
3420
- const block = { id: document2Id("block"), kind: "textBlock", command, field: createInlineField2("") };
3873
+ const block = {
3874
+ id: document2Id("block"),
3875
+ kind: "textBlock",
3876
+ command,
3877
+ field: createInlineField2(""),
3878
+ ...command === "\\paragraph" ? { centered: false } : {}
3879
+ };
3421
3880
  return insertDocument2BlockAfter(document2, afterBlockId, block);
3422
3881
  }
3882
+ function updateDocument2TextBlockCentered(document2, blockId, centered) {
3883
+ const next = cloneDocument(document2);
3884
+ function visit(blocks) {
3885
+ for (const block of blocks) {
3886
+ if (block.id === blockId && block.kind === "textBlock" && block.command === "\\paragraph") {
3887
+ block.centered = centered;
3888
+ return true;
3889
+ }
3890
+ if (block.kind === "list") {
3891
+ for (const item of block.items) {
3892
+ if (visit(item.blocks)) {
3893
+ return true;
3894
+ }
3895
+ }
3896
+ }
3897
+ }
3898
+ return false;
3899
+ }
3900
+ visit(next.blocks);
3901
+ return next;
3902
+ }
3423
3903
  function removeDocument2BlockById(document2, blockId) {
3424
3904
  const next = cloneDocument(document2);
3425
3905
  next.blocks = next.blocks.filter((block) => block.id !== blockId);
@@ -3456,7 +3936,8 @@ function addDocument2TableBlock(document2, columns = "lll", rowCount = 3, colCou
3456
3936
  command: "\\begin{tabular}",
3457
3937
  closing: "\\end{tabular}",
3458
3938
  columns: columns || "l".repeat(cols),
3459
- rows: tableRows
3939
+ rows: tableRows,
3940
+ ...defaultFloatMeta()
3460
3941
  };
3461
3942
  return insertDocument2BlockAfter(document2, afterBlockId, block);
3462
3943
  }
@@ -3466,7 +3947,8 @@ function addDocument2ImageBlock(document2, src = "", afterBlockId) {
3466
3947
  kind: "image",
3467
3948
  command: "\\includegraphics",
3468
3949
  value: src,
3469
- options: {}
3950
+ options: { width: "0.8\\textwidth" },
3951
+ ...defaultFloatMeta()
3470
3952
  };
3471
3953
  return insertDocument2BlockAfter(document2, afterBlockId, block);
3472
3954
  }
@@ -3491,6 +3973,128 @@ function updateDocument2ImageValue(document2, blockId, value) {
3491
3973
  visit(next.blocks);
3492
3974
  return next;
3493
3975
  }
3976
+ function updateDocument2ImageMeta(document2, blockId, patch) {
3977
+ const next = cloneDocument(document2);
3978
+ function visit(blocks) {
3979
+ for (const block of blocks) {
3980
+ if (block.id === blockId && block.kind === "image") {
3981
+ applyFloatMetaPatch(block, patch);
3982
+ return true;
3983
+ }
3984
+ if (block.kind === "list") {
3985
+ for (const item of block.items) {
3986
+ if (visit(item.blocks)) {
3987
+ return true;
3988
+ }
3989
+ }
3990
+ }
3991
+ }
3992
+ return false;
3993
+ }
3994
+ visit(next.blocks);
3995
+ return next;
3996
+ }
3997
+ function updateDocument2TableMeta(document2, blockId, patch) {
3998
+ const next = cloneDocument(document2);
3999
+ function visit(blocks) {
4000
+ for (const block of blocks) {
4001
+ if (block.id === blockId && block.kind === "table") {
4002
+ applyFloatMetaPatch(block, patch);
4003
+ return true;
4004
+ }
4005
+ if (block.kind === "list") {
4006
+ for (const item of block.items) {
4007
+ if (visit(item.blocks)) {
4008
+ return true;
4009
+ }
4010
+ }
4011
+ }
4012
+ }
4013
+ return false;
4014
+ }
4015
+ visit(next.blocks);
4016
+ return next;
4017
+ }
4018
+ function updateMathTokenLabel(document2, tokenId, patch) {
4019
+ const next = cloneDocument(document2);
4020
+ visitFields(next.blocks, (field) => {
4021
+ const token = field.tokens.find((entry) => entry.id === tokenId && entry.kind === "math");
4022
+ if (!token) {
4023
+ return false;
4024
+ }
4025
+ if (typeof patch.labelEnabled === "boolean") {
4026
+ token.labelEnabled = patch.labelEnabled;
4027
+ }
4028
+ if (typeof patch.label === "string") {
4029
+ token.label = patch.label;
4030
+ }
4031
+ return true;
4032
+ });
4033
+ return next;
4034
+ }
4035
+ function refTokenFromKeys(keys, refCommand) {
4036
+ return { id: document2Id("ref"), kind: "ref", keys: [...keys], refCommand };
4037
+ }
4038
+ function insertRefTokenAtCaret(document2, fieldId, textTokenId, caretOffset, keys, refCommand = "ref") {
4039
+ if (keys.length === 0) {
4040
+ return document2;
4041
+ }
4042
+ const next = cloneDocument(document2);
4043
+ const refToken = refTokenFromKeys(keys, refCommand);
4044
+ visitFields(next.blocks, (field) => {
4045
+ if (field.id !== fieldId) {
4046
+ return false;
4047
+ }
4048
+ const textIndex = textTokenId ? field.tokens.findIndex((token) => token.id === textTokenId && token.kind === "text") : field.tokens.findIndex((token) => token.kind === "text");
4049
+ if (textIndex < 0) {
4050
+ field.tokens.push(refToken);
4051
+ field.tokens.push({ id: document2Id("text"), kind: "text", text: "" });
4052
+ return true;
4053
+ }
4054
+ const current = field.tokens[textIndex];
4055
+ const [before, after] = splitTextTokenAt(current, caretOffset);
4056
+ const parts = [...field.tokens.slice(0, textIndex)];
4057
+ if (before.text.length > 0) {
4058
+ parts.push(before);
4059
+ }
4060
+ parts.push(refToken);
4061
+ parts.push(after);
4062
+ parts.push(...field.tokens.slice(textIndex + 1));
4063
+ field.tokens = normalizeFieldTokens(parts);
4064
+ return true;
4065
+ });
4066
+ return next;
4067
+ }
4068
+ function updateRefTokenKeys(document2, tokenId, keys, refCommand) {
4069
+ if (keys.length === 0) {
4070
+ return document2;
4071
+ }
4072
+ const next = cloneDocument(document2);
4073
+ visitFields(next.blocks, (field) => {
4074
+ const token = field.tokens.find((entry) => entry.id === tokenId && entry.kind === "ref");
4075
+ if (!token) {
4076
+ return false;
4077
+ }
4078
+ token.keys = [...keys];
4079
+ if (refCommand) {
4080
+ token.refCommand = refCommand;
4081
+ }
4082
+ return true;
4083
+ });
4084
+ return next;
4085
+ }
4086
+ function removeRefTokenById(document2, tokenId) {
4087
+ const next = cloneDocument(document2);
4088
+ visitFields(next.blocks, (field) => {
4089
+ const index = field.tokens.findIndex((token) => token.id === tokenId && token.kind === "ref");
4090
+ if (index < 0) {
4091
+ return false;
4092
+ }
4093
+ field.tokens = normalizeFieldTokens(stitchTextAroundRemovedToken(field.tokens, index));
4094
+ return true;
4095
+ });
4096
+ return next;
4097
+ }
3494
4098
  function mutateListBlockById(blocks, listBlockId, mutator) {
3495
4099
  for (const block of blocks) {
3496
4100
  if (block.id === listBlockId && block.kind === "list") {
@@ -3654,12 +4258,24 @@ function setDocument2References(document2, references) {
3654
4258
  next.references = references.map((reference) => ({ ...reference }));
3655
4259
  return next;
3656
4260
  }
4261
+ function updateDocument2Meta(document2, patch) {
4262
+ const next = cloneDocument(document2);
4263
+ const current = next.meta ?? emptyDocument2Meta();
4264
+ next.meta = {
4265
+ title: typeof patch.title === "string" ? patch.title : current.title,
4266
+ authors: typeof patch.authors === "string" ? patch.authors : current.authors,
4267
+ abstract: typeof patch.abstract === "string" ? patch.abstract : current.abstract,
4268
+ date: patch.date ? normalizeDocument2HijriDate({ ...current.date, ...patch.date }) : { ...current.date }
4269
+ };
4270
+ return next;
4271
+ }
3657
4272
 
3658
4273
  // 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}
4274
+ function arabicXeLatexPreamblePkg(twocolumn = false) {
4275
+ const classOptions = twocolumn ? "12pt,a4paper,notitlepage,twocolumn" : "12pt,a4paper,notitlepage";
4276
+ return `
4277
+ \\documentclass[${classOptions}]{article}
4278
+ ` + String.raw`\usepackage{amsmath,amsfonts,amssymb,mathrsfs,tikz,fancyhdr, mathtools}
3663
4279
  \usepackage{fontspec} % For loading OpenType fonts
3664
4280
  \usepackage{unicode-math} % For setting the math font
3665
4281
  \usepackage{polyglossia} % For Arabic support
@@ -3849,11 +4465,21 @@ function arabicXeLatexPreambleCmd() {
3849
4465
  \newcommand{\arbinom}[2]{\left(\begin{array}{c} #1 \\ #2 \end{array} \right)}
3850
4466
  `;
3851
4467
  }
3852
- function getArabicXeLatexPreamble(digitsMapping = "arabicdigits") {
3853
- return arabicXeLatexPreamblePkg() + arabicXeLatexPreambleFont(digitsMapping) + arabicXeLatexPreambleCmd();
4468
+ function getArabicXeLatexPreamble(digitsMappingOrOptions = "arabicdigits") {
4469
+ const options = typeof digitsMappingOrOptions === "string" ? { digitsMapping: digitsMappingOrOptions } : digitsMappingOrOptions;
4470
+ const digitsMapping = options.digitsMapping ?? "arabicdigits";
4471
+ const twocolumn = options.twocolumn === true;
4472
+ return arabicXeLatexPreamblePkg(twocolumn) + arabicXeLatexPreambleFont(digitsMapping) + arabicXeLatexPreambleCmd();
3854
4473
  }
3855
4474
 
3856
4475
  // src/document2/exportLatex.ts
4476
+ function mathBodyLatex(token) {
4477
+ if (!token.math || token.sourceOwner === "raw" || token.sourceOwner === "editor") {
4478
+ return stripDisplayMathDelimiters(token.source);
4479
+ }
4480
+ const rendered = renderMathNodeLatex(token.math);
4481
+ return stripDisplayMathDelimiters(rendered);
4482
+ }
3857
4483
  function tokenLatex(token) {
3858
4484
  if (token.kind === "text") {
3859
4485
  return token.text;
@@ -3861,6 +4487,16 @@ function tokenLatex(token) {
3861
4487
  if (token.kind === "cite") {
3862
4488
  return citeTokenLatex(token.keys);
3863
4489
  }
4490
+ if (token.kind === "ref") {
4491
+ return refTokenLatex(token.keys, token.refCommand);
4492
+ }
4493
+ if (token.display && token.labelEnabled && token.label && token.label.trim().length > 0) {
4494
+ const body = mathBodyLatex(token);
4495
+ return `\\begin{equation}
4496
+ ${body}
4497
+ \\label{${token.label.trim()}}
4498
+ \\end{equation}`;
4499
+ }
3864
4500
  if (!token.math || token.sourceOwner === "raw" || token.sourceOwner === "editor") {
3865
4501
  return token.source;
3866
4502
  }
@@ -3870,7 +4506,13 @@ function inlineFieldLatex(field) {
3870
4506
  return field.tokens.map((token) => tokenLatex(token)).join("");
3871
4507
  }
3872
4508
  function textBlockLatex(block) {
3873
- return `${block.command}{${inlineFieldLatex(block.field)}}`;
4509
+ const body = `${block.command}{${inlineFieldLatex(block.field)}}`;
4510
+ if (block.command === "\\paragraph" && block.centered) {
4511
+ return `\\begin{center}
4512
+ ${body}
4513
+ \\end{center}`;
4514
+ }
4515
+ return body;
3874
4516
  }
3875
4517
  function indent(value) {
3876
4518
  return value.split("\n").map((line) => ` ${line}`).join("\n");
@@ -3884,12 +4526,32 @@ function listBlockLatex(block, references) {
3884
4526
  ${items}
3885
4527
  ${block.closing}`;
3886
4528
  }
3887
- function tableBlockLatex(block) {
4529
+ function tabularInnerLatex(block) {
3888
4530
  const rows = block.rows.map((row) => ` ${row.map((cell) => inlineFieldLatex(cell)).join(" & ")}`).join(" \\\\\n");
3889
4531
  return `${block.command}{${block.columns}}
3890
4532
  ${rows}
3891
4533
  ${block.closing}`;
3892
4534
  }
4535
+ function floatCaptionLabelLines(block) {
4536
+ const lines = [];
4537
+ if (block.captionEnabled && block.caption.trim().length > 0) {
4538
+ lines.push(` \\caption{${block.caption.trim()}}`);
4539
+ }
4540
+ if (block.labelEnabled && block.label.trim().length > 0) {
4541
+ lines.push(` \\label{${block.label.trim()}}`);
4542
+ }
4543
+ return lines;
4544
+ }
4545
+ function tableBlockLatex(block) {
4546
+ const lines = ["\\begin{table}[htbp]"];
4547
+ if (block.centered) {
4548
+ lines.push(" \\centering");
4549
+ }
4550
+ lines.push(...floatCaptionLabelLines(block));
4551
+ lines.push(indent(tabularInnerLatex(block)));
4552
+ lines.push("\\end{table}");
4553
+ return lines.join("\n");
4554
+ }
3893
4555
  function imageOptionsLatex(block) {
3894
4556
  const entries = Object.entries(block.options);
3895
4557
  if (entries.length === 0) {
@@ -3897,6 +4559,16 @@ function imageOptionsLatex(block) {
3897
4559
  }
3898
4560
  return `[${entries.map(([key, value]) => `${key}=${value}`).join(",")}]`;
3899
4561
  }
4562
+ function imageBlockLatex(block) {
4563
+ const lines = ["\\begin{figure}[htbp]"];
4564
+ if (block.centered) {
4565
+ lines.push(" \\centering");
4566
+ }
4567
+ lines.push(` ${block.command}${imageOptionsLatex(block)}{${block.value}}`);
4568
+ lines.push(...floatCaptionLabelLines(block));
4569
+ lines.push("\\end{figure}");
4570
+ return lines.join("\n");
4571
+ }
3900
4572
  function blockLatex(block, references) {
3901
4573
  if (block.kind === "textBlock") {
3902
4574
  return textBlockLatex(block);
@@ -3908,7 +4580,7 @@ function blockLatex(block, references) {
3908
4580
  return tableBlockLatex(block);
3909
4581
  }
3910
4582
  if (block.kind === "image") {
3911
- return `${block.command}${imageOptionsLatex(block)}{${block.value}}`;
4583
+ return imageBlockLatex(block);
3912
4584
  }
3913
4585
  if (block.kind === "bibliography") {
3914
4586
  const width = String(Math.max(references.length, 9));
@@ -3919,13 +4591,54 @@ ${block.closing}`;
3919
4591
  }
3920
4592
  return block.value;
3921
4593
  }
4594
+ function authorLatex(authors) {
4595
+ return authors.split(/\n+/).map((line) => line.trim()).filter((line) => line.length > 0).join(" \\\\ ");
4596
+ }
4597
+ function articleMaketitlePreamble(meta) {
4598
+ if (!document2HasMaketitle(meta)) {
4599
+ return "";
4600
+ }
4601
+ const parts = [butexBasmalaTitlingPreamble()];
4602
+ if (meta.title.trim().length > 0) {
4603
+ parts.push(`\\title{${meta.title.trim()}}`);
4604
+ }
4605
+ if (meta.authors.trim().length > 0) {
4606
+ parts.push(`\\author{${authorLatex(meta.authors)}}`);
4607
+ }
4608
+ parts.push(`\\date{${formatHijriDate(meta.date, { locale: "ar", digitForm: "arabicIndic" })}}`);
4609
+ return parts.join("\n");
4610
+ }
4611
+ function articleFrontMatterLatex(meta) {
4612
+ const parts = [];
4613
+ if (document2HasMaketitle(meta)) {
4614
+ parts.push("\\maketitle");
4615
+ } else {
4616
+ parts.push(butexBasmalaDisplayLatex());
4617
+ }
4618
+ if (document2HasAbstract(meta)) {
4619
+ parts.push(`\\begin{abstract}
4620
+ ${meta.abstract.trim()}
4621
+ \\end{abstract}`);
4622
+ }
4623
+ return parts;
4624
+ }
3922
4625
  function document2Latex(document2, options = {}) {
3923
- const body = document2.blocks.map((block) => blockLatex(block, document2.references)).join("\n\n");
4626
+ const meta = document2.meta ?? emptyDocument2Meta();
4627
+ const front = articleFrontMatterLatex(meta);
4628
+ const bodyBlocks = document2.blocks.map((block) => blockLatex(block, document2.references));
4629
+ const closing = butexDiwaniDisplayLatex(BUTEX_CLOSING_HAMDALA);
4630
+ const body = [...front, ...bodyBlocks, closing].filter((part) => part.length > 0).join("\n\n");
3924
4631
  const wrapDocument = options.wrapDocument !== false;
3925
4632
  if (!wrapDocument) {
3926
- return body;
4633
+ const metaPreamble = articleMaketitlePreamble(meta);
4634
+ return metaPreamble.length > 0 ? `${metaPreamble}
4635
+
4636
+ ${body}` : body;
3927
4637
  }
3928
- const preamble = getArabicXeLatexPreamble(options.digitsMapping ?? "arabicdigits");
4638
+ const preamble = getArabicXeLatexPreamble({
4639
+ digitsMapping: options.digitsMapping ?? "arabicdigits",
4640
+ twocolumn: options.twocolumn === true
4641
+ }) + "\n" + articleMaketitlePreamble(meta);
3929
4642
  return `${preamble}
3930
4643
  \\begin{document}
3931
4644
 
@@ -3968,6 +4681,67 @@ function document2HistoryCanRedo(stacks) {
3968
4681
  return stacks.future.length > 0;
3969
4682
  }
3970
4683
 
4684
+ // src/document2/blockSelection.ts
4685
+ function emptyBlockSelection() {
4686
+ return { selection: null, anchor: null };
4687
+ }
4688
+ function normalizeBlockSelection(selection, blockCount) {
4689
+ if (!selection) {
4690
+ return null;
4691
+ }
4692
+ const { from, to } = selection;
4693
+ if (blockCount <= 0 || from < 0 || to < 0 || from >= blockCount || to >= blockCount || from > to) {
4694
+ return null;
4695
+ }
4696
+ return { from, to };
4697
+ }
4698
+ function inRange(selection, index) {
4699
+ return index >= selection.from && index <= selection.to;
4700
+ }
4701
+ function toggleBlockInSelection(state, index, blockCount) {
4702
+ if (index < 0 || index >= blockCount) {
4703
+ return state;
4704
+ }
4705
+ const selection = normalizeBlockSelection(state.selection, blockCount);
4706
+ if (!selection) {
4707
+ return { selection: { from: index, to: index }, anchor: index };
4708
+ }
4709
+ if (inRange(selection, index)) {
4710
+ if (selection.from === selection.to) {
4711
+ return emptyBlockSelection();
4712
+ }
4713
+ if (index === selection.from) {
4714
+ return { selection: { from: selection.from + 1, to: selection.to }, anchor: state.anchor };
4715
+ }
4716
+ if (index === selection.to) {
4717
+ return { selection: { from: selection.from, to: selection.to - 1 }, anchor: state.anchor };
4718
+ }
4719
+ return emptyBlockSelection();
4720
+ }
4721
+ if (index === selection.from - 1) {
4722
+ return { selection: { from: index, to: selection.to }, anchor: state.anchor ?? index };
4723
+ }
4724
+ if (index === selection.to + 1) {
4725
+ return { selection: { from: selection.from, to: index }, anchor: state.anchor ?? index };
4726
+ }
4727
+ return { selection: { from: index, to: index }, anchor: index };
4728
+ }
4729
+ function extendBlockSelectionFromAnchor(state, index, blockCount) {
4730
+ if (index < 0 || index >= blockCount) {
4731
+ return state;
4732
+ }
4733
+ const anchor = state.anchor ?? index;
4734
+ const from = Math.min(anchor, index);
4735
+ const to = Math.max(anchor, index);
4736
+ return {
4737
+ selection: { from, to },
4738
+ anchor: state.anchor ?? index
4739
+ };
4740
+ }
4741
+ function remapSelectionAfterRangeMove(selection, direction) {
4742
+ return { from: selection.from + direction, to: selection.to + direction };
4743
+ }
4744
+
3971
4745
  // src/document2/previewModel.ts
3972
4746
  function mathTex(token, equationSide) {
3973
4747
  return mathTokenSourceForSide(token, equationSide);
@@ -3988,6 +4762,18 @@ function previewInlines(field, islands, output, equationSide, document2, preview
3988
4762
  })
3989
4763
  };
3990
4764
  }
4765
+ if (token.kind === "ref") {
4766
+ return {
4767
+ kind: "ref",
4768
+ id: token.id,
4769
+ keys: [...token.keys],
4770
+ refCommand: token.refCommand,
4771
+ label: formatRefLabel(token.keys, document2, token.refCommand, {
4772
+ documentDirection: previewOptions.documentDirection,
4773
+ digitForm: previewOptions.digitForm
4774
+ })
4775
+ };
4776
+ }
3991
4777
  const island = {
3992
4778
  id: token.id,
3993
4779
  tex: mathTex(token, equationSide),
@@ -4010,7 +4796,28 @@ function textBlockPreview(block, islands, output, equationSide, document2, previ
4010
4796
  if (block.command === "\\subsubsection") {
4011
4797
  return { kind: "heading", id: block.id, level: 3, inlines };
4012
4798
  }
4013
- return { kind: "paragraph", id: block.id, inlines };
4799
+ return {
4800
+ kind: "paragraph",
4801
+ id: block.id,
4802
+ inlines,
4803
+ ...block.centered ? { centered: true } : {}
4804
+ };
4805
+ }
4806
+ function floatNumberLabel(document2, key, previewOptions) {
4807
+ if (!key || key.trim().length === 0) {
4808
+ return void 0;
4809
+ }
4810
+ const hit = labelNumberMap(document2).get(key.trim());
4811
+ if (!hit || hit.kind !== "fig" && hit.kind !== "tab") {
4812
+ return void 0;
4813
+ }
4814
+ const locale = previewOptions.uiLocale ?? "ar";
4815
+ const kindWord = hit.kind === "fig" ? locale === "ar" ? "\u0627\u0644\u0634\u0643\u0644" : "Figure" : locale === "ar" ? "\u0627\u0644\u062C\u062F\u0648\u0644" : "Table";
4816
+ const digits = formatBibliographyNumber(hit.number, {
4817
+ documentDirection: previewOptions.documentDirection,
4818
+ digitForm: previewOptions.digitForm
4819
+ });
4820
+ return `${kindWord} ${digits}`;
4014
4821
  }
4015
4822
  function blockPreview(block, islands, output, equationSide, document2, previewOptions) {
4016
4823
  if (block.kind === "textBlock") {
@@ -4029,19 +4836,25 @@ function blockPreview(block, islands, output, equationSide, document2, previewOp
4029
4836
  };
4030
4837
  }
4031
4838
  if (block.kind === "table") {
4839
+ const label = block.labelEnabled ? block.label.trim() : "";
4032
4840
  return {
4033
4841
  kind: "table",
4034
4842
  id: block.id,
4035
- rows: block.rows.map((row) => row.map((cell) => previewInlines(cell, islands, output, equationSide, document2, previewOptions)))
4843
+ rows: block.rows.map((row) => row.map((cell) => previewInlines(cell, islands, output, equationSide, document2, previewOptions))),
4844
+ ...block.captionEnabled && block.caption.trim().length > 0 ? { caption: block.caption.trim() } : {},
4845
+ ...label.length > 0 ? { label, numberLabel: floatNumberLabel(document2, label, previewOptions) } : {}
4036
4846
  };
4037
4847
  }
4038
4848
  if (block.kind === "image") {
4849
+ const label = block.labelEnabled ? block.label.trim() : "";
4039
4850
  return {
4040
4851
  kind: "image",
4041
4852
  id: block.id,
4042
4853
  src: block.value,
4043
4854
  ...block.assetId !== void 0 ? { assetId: block.assetId } : {},
4044
- options: block.options
4855
+ options: block.options,
4856
+ ...block.captionEnabled && block.caption.trim().length > 0 ? { caption: block.caption.trim() } : {},
4857
+ ...label.length > 0 ? { label, numberLabel: floatNumberLabel(document2, label, previewOptions) } : {}
4045
4858
  };
4046
4859
  }
4047
4860
  if (block.kind === "bibliography") {
@@ -4065,54 +4878,111 @@ function blockPreview(block, islands, output, equationSide, document2, previewOp
4065
4878
  }
4066
4879
  return { kind: "omit", id: block.id };
4067
4880
  }
4881
+ function articleChromePreview(document2, uiLocale, previewOptions) {
4882
+ const meta = document2.meta ?? emptyDocument2Meta();
4883
+ const blocks = [
4884
+ { kind: "basmala", id: "article-basmala", text: BUTEX_BASMALA, tex: butexBasmalaDisplayTex() }
4885
+ ];
4886
+ if (document2HasMaketitle(meta)) {
4887
+ blocks.push({
4888
+ kind: "titleBlock",
4889
+ id: "article-title",
4890
+ ...meta.title.trim().length > 0 ? { title: meta.title.trim() } : {},
4891
+ ...meta.authors.trim().length > 0 ? { authors: meta.authors.trim() } : {},
4892
+ date: formatHijriDate(meta.date, {
4893
+ locale: uiLocale,
4894
+ digitForm: previewOptions.digitForm
4895
+ })
4896
+ });
4897
+ }
4898
+ if (document2HasAbstract(meta)) {
4899
+ blocks.push({ kind: "abstract", id: "article-abstract", text: meta.abstract.trim() });
4900
+ }
4901
+ return blocks;
4902
+ }
4068
4903
  function document2Preview(document2, output = "svg", equationSide = "arabic", previewOptions = {}) {
4069
4904
  const mathIslands = [];
4070
4905
  const options = {
4071
4906
  documentDirection: previewOptions.documentDirection ?? "rtl",
4072
- digitForm: previewOptions.digitForm ?? (previewOptions.documentDirection === "ltr" ? "western" : "arabicIndic")
4907
+ digitForm: previewOptions.digitForm ?? (previewOptions.documentDirection === "ltr" ? "western" : "arabicIndic"),
4908
+ uiLocale: previewOptions.uiLocale ?? "ar"
4073
4909
  };
4910
+ const body = document2.blocks.map((block) => blockPreview(block, mathIslands, output, equationSide, document2, options));
4074
4911
  return {
4075
- blocks: document2.blocks.map((block) => blockPreview(block, mathIslands, output, equationSide, document2, options)),
4912
+ blocks: [
4913
+ ...articleChromePreview(document2, options.uiLocale ?? "ar", options),
4914
+ ...body,
4915
+ { kind: "closing", id: "article-closing", text: BUTEX_CLOSING_HAMDALA, tex: butexDiwaniDisplayTex(BUTEX_CLOSING_HAMDALA) }
4916
+ ],
4076
4917
  mathIslands
4077
4918
  };
4078
4919
  }
4079
4920
  export {
4921
+ BUTEX_BASMALA,
4922
+ BUTEX_BASMALA_LINE1,
4923
+ BUTEX_BASMALA_SALAWAT,
4924
+ BUTEX_CLOSING_HAMDALA,
4080
4925
  DEFAULT_DOCUMENT2_HISTORY_MAX_DEPTH,
4926
+ DEFAULT_HIJRI_DATE,
4927
+ HIJRI_MONTH_IDS,
4928
+ HIJRI_YEAR_MAX,
4929
+ HIJRI_YEAR_MIN,
4081
4930
  addDocument2ImageBlock,
4082
4931
  addDocument2ListBlock,
4083
4932
  addDocument2ListItem,
4084
4933
  addDocument2Reference,
4085
4934
  addDocument2TableBlock,
4086
4935
  addDocument2TextBlock,
4936
+ applyFloatMetaPatch,
4087
4937
  arabicXeLatexPreambleCmd,
4088
4938
  arabicXeLatexPreambleFont,
4089
4939
  arabicXeLatexPreamblePkg,
4090
4940
  bibliographyEntryLatex,
4941
+ butexBasmalaDisplayLatex,
4942
+ butexBasmalaDisplayTex,
4943
+ butexBasmalaTitlingPreamble,
4944
+ butexDiwaniDisplayLatex,
4945
+ butexDiwaniDisplayTex,
4091
4946
  citeTokenLatex,
4947
+ cloneDocument2Meta,
4092
4948
  cloneDocument2Node,
4949
+ collectDocument2Labels,
4093
4950
  createDocument2History,
4094
4951
  createEmptyArabicEquationSession,
4095
4952
  createEmptyDocument2,
4096
4953
  createEmptyEquationSession,
4097
4954
  createEmptyReference2,
4098
4955
  createInlineField2,
4956
+ defaultFloatMeta,
4099
4957
  detectInlineSpans2,
4100
4958
  detectMathSpans2,
4959
+ document2HasAbstract,
4960
+ document2HasMaketitle,
4961
+ document2HasTitleStack,
4101
4962
  document2HistoryCanRedo,
4102
4963
  document2HistoryCanUndo,
4103
4964
  document2Latex,
4104
4965
  document2Preview,
4966
+ emptyBlockSelection,
4967
+ emptyDocument2Meta,
4105
4968
  ensureDocument2BibliographyBlock,
4969
+ extendBlockSelectionFromAnchor,
4970
+ fillDocument2MetaGaps,
4106
4971
  findFirstInlineField,
4107
4972
  formatBibliographyNumber,
4108
4973
  formatCiteLabel,
4974
+ formatHijriDate,
4975
+ formatRefLabel,
4109
4976
  fromDocumentJson2,
4110
4977
  getArabicXeLatexPreamble,
4978
+ hijriMonthLabel,
4111
4979
  inlineFieldLatex,
4112
4980
  insertCiteTokenAtCaret,
4113
4981
  insertDocument2BlockAfter,
4114
4982
  insertMathToken,
4115
4983
  insertMathTokenAtCaret,
4984
+ insertRefTokenAtCaret,
4985
+ labelNumberMap,
4116
4986
  mathNodeFromArabicSession,
4117
4987
  mathNodeFromSession,
4118
4988
  mathSourceFromArabicSession,
@@ -4120,25 +4990,44 @@ export {
4120
4990
  mathTokenSourceForSide,
4121
4991
  mathTokenToArabicEditorSession,
4122
4992
  mathTokenToEditorSession,
4993
+ mergeDocument2Meta,
4123
4994
  moveDocument2BlockById,
4995
+ moveDocument2BlockRange,
4124
4996
  moveDocument2Reference,
4997
+ normalizeBlockSelection,
4998
+ normalizeDocument2HijriDate,
4999
+ normalizeDocument2Meta,
4125
5000
  parseCiteKeys,
5001
+ parseFloatMetaFromJson,
5002
+ parseRefKeys,
4126
5003
  pushDocument2Snapshot,
5004
+ refTokenLatex,
4127
5005
  referenceFromJson,
4128
5006
  referenceNumberMap,
5007
+ remapSelectionAfterRangeMove,
4129
5008
  removeCiteTokenById,
4130
5009
  removeDocument2BlockById,
5010
+ removeDocument2BlockRange,
4131
5011
  removeDocument2ListItem,
4132
5012
  removeDocument2Reference,
4133
5013
  removeMathTokenById,
5014
+ removeRefTokenById,
4134
5015
  replaceMathTokenFromSession,
4135
5016
  resolveCiteNumbers,
4136
5017
  restoreDocument2Redo,
4137
5018
  restoreDocument2Undo,
4138
5019
  setDocument2References,
5020
+ stripDisplayMathDelimiters,
5021
+ toggleBlockInSelection,
4139
5022
  updateCiteTokenKeys,
5023
+ updateDocument2ImageMeta,
4140
5024
  updateDocument2ImageValue,
5025
+ updateDocument2Meta,
4141
5026
  updateDocument2Reference,
5027
+ updateDocument2TableMeta,
5028
+ updateDocument2TextBlockCentered,
5029
+ updateMathTokenLabel,
5030
+ updateRefTokenKeys,
4142
5031
  updateTextToken
4143
5032
  };
4144
5033
  //# sourceMappingURL=document2.mjs.map