@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.
package/dist/document2.js CHANGED
@@ -20,42 +20,66 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/document2/index.ts
21
21
  var document2_exports = {};
22
22
  __export(document2_exports, {
23
+ BUTEX_BASMALA: () => BUTEX_BASMALA,
24
+ BUTEX_CLOSING_HAMDALA: () => BUTEX_CLOSING_HAMDALA,
23
25
  DEFAULT_DOCUMENT2_HISTORY_MAX_DEPTH: () => DEFAULT_DOCUMENT2_HISTORY_MAX_DEPTH,
26
+ DEFAULT_HIJRI_DATE: () => DEFAULT_HIJRI_DATE,
27
+ HIJRI_MONTH_IDS: () => HIJRI_MONTH_IDS,
28
+ HIJRI_YEAR_MAX: () => HIJRI_YEAR_MAX,
29
+ HIJRI_YEAR_MIN: () => HIJRI_YEAR_MIN,
24
30
  addDocument2ImageBlock: () => addDocument2ImageBlock,
25
31
  addDocument2ListBlock: () => addDocument2ListBlock,
26
32
  addDocument2ListItem: () => addDocument2ListItem,
27
33
  addDocument2Reference: () => addDocument2Reference,
28
34
  addDocument2TableBlock: () => addDocument2TableBlock,
29
35
  addDocument2TextBlock: () => addDocument2TextBlock,
36
+ applyFloatMetaPatch: () => applyFloatMetaPatch,
30
37
  arabicXeLatexPreambleCmd: () => arabicXeLatexPreambleCmd,
31
38
  arabicXeLatexPreambleFont: () => arabicXeLatexPreambleFont,
32
39
  arabicXeLatexPreamblePkg: () => arabicXeLatexPreamblePkg,
33
40
  bibliographyEntryLatex: () => bibliographyEntryLatex,
41
+ butexDiwaniDisplayLatex: () => butexDiwaniDisplayLatex,
42
+ butexDiwaniDisplayTex: () => butexDiwaniDisplayTex,
34
43
  citeTokenLatex: () => citeTokenLatex,
44
+ cloneDocument2Meta: () => cloneDocument2Meta,
35
45
  cloneDocument2Node: () => cloneDocument2Node,
46
+ collectDocument2Labels: () => collectDocument2Labels,
36
47
  createDocument2History: () => createDocument2History,
37
48
  createEmptyArabicEquationSession: () => createEmptyArabicEquationSession,
38
49
  createEmptyDocument2: () => createEmptyDocument2,
39
50
  createEmptyEquationSession: () => createEmptyEquationSession,
40
51
  createEmptyReference2: () => createEmptyReference2,
41
52
  createInlineField2: () => createInlineField2,
53
+ defaultFloatMeta: () => defaultFloatMeta,
42
54
  detectInlineSpans2: () => detectInlineSpans2,
43
55
  detectMathSpans2: () => detectMathSpans2,
56
+ document2HasAbstract: () => document2HasAbstract,
57
+ document2HasMaketitle: () => document2HasMaketitle,
58
+ document2HasTitleStack: () => document2HasTitleStack,
44
59
  document2HistoryCanRedo: () => document2HistoryCanRedo,
45
60
  document2HistoryCanUndo: () => document2HistoryCanUndo,
46
61
  document2Latex: () => document2Latex,
47
62
  document2Preview: () => document2Preview,
63
+ emptyBlockSelection: () => emptyBlockSelection,
64
+ emptyDocument2Meta: () => emptyDocument2Meta,
48
65
  ensureDocument2BibliographyBlock: () => ensureDocument2BibliographyBlock,
66
+ extendBlockSelectionFromAnchor: () => extendBlockSelectionFromAnchor,
67
+ fillDocument2MetaGaps: () => fillDocument2MetaGaps,
49
68
  findFirstInlineField: () => findFirstInlineField,
50
69
  formatBibliographyNumber: () => formatBibliographyNumber,
51
70
  formatCiteLabel: () => formatCiteLabel,
71
+ formatHijriDate: () => formatHijriDate,
72
+ formatRefLabel: () => formatRefLabel,
52
73
  fromDocumentJson2: () => fromDocumentJson2,
53
74
  getArabicXeLatexPreamble: () => getArabicXeLatexPreamble,
75
+ hijriMonthLabel: () => hijriMonthLabel,
54
76
  inlineFieldLatex: () => inlineFieldLatex,
55
77
  insertCiteTokenAtCaret: () => insertCiteTokenAtCaret,
56
78
  insertDocument2BlockAfter: () => insertDocument2BlockAfter,
57
79
  insertMathToken: () => insertMathToken,
58
80
  insertMathTokenAtCaret: () => insertMathTokenAtCaret,
81
+ insertRefTokenAtCaret: () => insertRefTokenAtCaret,
82
+ labelNumberMap: () => labelNumberMap,
59
83
  mathNodeFromArabicSession: () => mathNodeFromArabicSession,
60
84
  mathNodeFromSession: () => mathNodeFromSession,
61
85
  mathSourceFromArabicSession: () => mathSourceFromArabicSession,
@@ -63,25 +87,44 @@ __export(document2_exports, {
63
87
  mathTokenSourceForSide: () => mathTokenSourceForSide,
64
88
  mathTokenToArabicEditorSession: () => mathTokenToArabicEditorSession,
65
89
  mathTokenToEditorSession: () => mathTokenToEditorSession,
90
+ mergeDocument2Meta: () => mergeDocument2Meta,
66
91
  moveDocument2BlockById: () => moveDocument2BlockById,
92
+ moveDocument2BlockRange: () => moveDocument2BlockRange,
67
93
  moveDocument2Reference: () => moveDocument2Reference,
94
+ normalizeBlockSelection: () => normalizeBlockSelection,
95
+ normalizeDocument2HijriDate: () => normalizeDocument2HijriDate,
96
+ normalizeDocument2Meta: () => normalizeDocument2Meta,
68
97
  parseCiteKeys: () => parseCiteKeys,
98
+ parseFloatMetaFromJson: () => parseFloatMetaFromJson,
99
+ parseRefKeys: () => parseRefKeys,
69
100
  pushDocument2Snapshot: () => pushDocument2Snapshot,
101
+ refTokenLatex: () => refTokenLatex,
70
102
  referenceFromJson: () => referenceFromJson,
71
103
  referenceNumberMap: () => referenceNumberMap,
104
+ remapSelectionAfterRangeMove: () => remapSelectionAfterRangeMove,
72
105
  removeCiteTokenById: () => removeCiteTokenById,
73
106
  removeDocument2BlockById: () => removeDocument2BlockById,
107
+ removeDocument2BlockRange: () => removeDocument2BlockRange,
74
108
  removeDocument2ListItem: () => removeDocument2ListItem,
75
109
  removeDocument2Reference: () => removeDocument2Reference,
76
110
  removeMathTokenById: () => removeMathTokenById,
111
+ removeRefTokenById: () => removeRefTokenById,
77
112
  replaceMathTokenFromSession: () => replaceMathTokenFromSession,
78
113
  resolveCiteNumbers: () => resolveCiteNumbers,
79
114
  restoreDocument2Redo: () => restoreDocument2Redo,
80
115
  restoreDocument2Undo: () => restoreDocument2Undo,
81
116
  setDocument2References: () => setDocument2References,
117
+ stripDisplayMathDelimiters: () => stripDisplayMathDelimiters,
118
+ toggleBlockInSelection: () => toggleBlockInSelection,
82
119
  updateCiteTokenKeys: () => updateCiteTokenKeys,
120
+ updateDocument2ImageMeta: () => updateDocument2ImageMeta,
83
121
  updateDocument2ImageValue: () => updateDocument2ImageValue,
122
+ updateDocument2Meta: () => updateDocument2Meta,
84
123
  updateDocument2Reference: () => updateDocument2Reference,
124
+ updateDocument2TableMeta: () => updateDocument2TableMeta,
125
+ updateDocument2TextBlockCentered: () => updateDocument2TextBlockCentered,
126
+ updateMathTokenLabel: () => updateMathTokenLabel,
127
+ updateRefTokenKeys: () => updateRefTokenKeys,
85
128
  updateTextToken: () => updateTextToken
86
129
  });
87
130
  module.exports = __toCommonJS(document2_exports);
@@ -122,6 +165,179 @@ function formatDigits(value, digitForm = "western") {
122
165
  }).join("");
123
166
  }
124
167
 
168
+ // src/document2/articleMeta.ts
169
+ 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";
170
+ 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";
171
+ function escapeButexTextArg(text) {
172
+ return text.replace(/\\/g, "\\textbackslash{}").replace(/[{}]/g, "\\$&");
173
+ }
174
+ function butexDiwaniDisplayTex(text) {
175
+ return `\\butexdiwani{${escapeButexTextArg(text)}}`;
176
+ }
177
+ function butexDiwaniDisplayLatex(text) {
178
+ return `\\[
179
+ ${butexDiwaniDisplayTex(text)}
180
+ \\]`;
181
+ }
182
+ var HIJRI_YEAR_MIN = 1400;
183
+ var HIJRI_YEAR_MAX = 1500;
184
+ var HIJRI_MONTH_IDS = [
185
+ "\u0645\u062D\u0631\u0645",
186
+ "\u0635\u0641\u0631",
187
+ "\u0631\u0628\u064A\u0639 \u0627\u0644\u0623\u0648\u0644",
188
+ "\u0631\u0628\u064A\u0639 \u0627\u0644\u0622\u062E\u0631",
189
+ "\u062C\u0645\u0627\u062F\u0649 \u0627\u0644\u0623\u0648\u0644\u0649",
190
+ "\u062C\u0645\u0627\u062F\u0649 \u0627\u0644\u0622\u062E\u0631\u0629",
191
+ "\u0631\u062C\u0628",
192
+ "\u0634\u0639\u0628\u0627\u0646",
193
+ "\u0631\u0645\u0636\u0627\u0646",
194
+ "\u0634\u0648\u0627\u0644",
195
+ "\u0630\u0648 \u0627\u0644\u0642\u0639\u062F\u0629",
196
+ "\u0630\u0648 \u0627\u0644\u062D\u062C\u0629"
197
+ ];
198
+ var HIJRI_MONTH_LABELS_EN = {
199
+ \u0645\u062D\u0631\u0645: "Muharram",
200
+ \u0635\u0641\u0631: "Safar",
201
+ "\u0631\u0628\u064A\u0639 \u0627\u0644\u0623\u0648\u0644": "Rabi\u02BB I",
202
+ "\u0631\u0628\u064A\u0639 \u0627\u0644\u0622\u062E\u0631": "Rabi\u02BB II",
203
+ "\u062C\u0645\u0627\u062F\u0649 \u0627\u0644\u0623\u0648\u0644\u0649": "Jumada I",
204
+ "\u062C\u0645\u0627\u062F\u0649 \u0627\u0644\u0622\u062E\u0631\u0629": "Jumada II",
205
+ \u0631\u062C\u0628: "Rajab",
206
+ \u0634\u0639\u0628\u0627\u0646: "Sha\u02BBban",
207
+ \u0631\u0645\u0636\u0627\u0646: "Ramadan",
208
+ \u0634\u0648\u0627\u0644: "Shawwal",
209
+ "\u0630\u0648 \u0627\u0644\u0642\u0639\u062F\u0629": "Dhul Qa\u02BBdah",
210
+ "\u0630\u0648 \u0627\u0644\u062D\u062C\u0629": "Dhul Hijjah"
211
+ };
212
+ var LEGACY_LATIN_MONTH = {
213
+ muharram: "\u0645\u062D\u0631\u0645",
214
+ safar: "\u0635\u0641\u0631",
215
+ rabiAwwal: "\u0631\u0628\u064A\u0639 \u0627\u0644\u0623\u0648\u0644",
216
+ rabiThani: "\u0631\u0628\u064A\u0639 \u0627\u0644\u0622\u062E\u0631",
217
+ jumadaAwwal: "\u062C\u0645\u0627\u062F\u0649 \u0627\u0644\u0623\u0648\u0644\u0649",
218
+ jumadaThani: "\u062C\u0645\u0627\u062F\u0649 \u0627\u0644\u0622\u062E\u0631\u0629",
219
+ rajab: "\u0631\u062C\u0628",
220
+ shaban: "\u0634\u0639\u0628\u0627\u0646",
221
+ ramadan: "\u0631\u0645\u0636\u0627\u0646",
222
+ shawwal: "\u0634\u0648\u0627\u0644",
223
+ dhulQadah: "\u0630\u0648 \u0627\u0644\u0642\u0639\u062F\u0629",
224
+ dhulHijjah: "\u0630\u0648 \u0627\u0644\u062D\u062C\u0629"
225
+ };
226
+ var DEFAULT_HIJRI_DATE = {
227
+ day: 1,
228
+ month: "\u0645\u062D\u0631\u0645",
229
+ year: 1448
230
+ };
231
+ function hijriMonthLabel(month, locale = "ar") {
232
+ return locale === "en" ? HIJRI_MONTH_LABELS_EN[month] : month;
233
+ }
234
+ function emptyDocument2Meta() {
235
+ return {
236
+ title: "",
237
+ authors: "",
238
+ date: { ...DEFAULT_HIJRI_DATE },
239
+ abstract: ""
240
+ };
241
+ }
242
+ function clampInt(value, min, max) {
243
+ if (!Number.isFinite(value)) {
244
+ return min;
245
+ }
246
+ return Math.min(max, Math.max(min, Math.trunc(value)));
247
+ }
248
+ function isHijriMonthId(value) {
249
+ return typeof value === "string" && HIJRI_MONTH_IDS.includes(value);
250
+ }
251
+ function resolveHijriMonthId(value) {
252
+ if (isHijriMonthId(value)) {
253
+ return value;
254
+ }
255
+ if (typeof value === "string" && value in LEGACY_LATIN_MONTH) {
256
+ return LEGACY_LATIN_MONTH[value];
257
+ }
258
+ return DEFAULT_HIJRI_DATE.month;
259
+ }
260
+ function normalizeDocument2HijriDate(value) {
261
+ if (typeof value !== "object" || value === null) {
262
+ return { ...DEFAULT_HIJRI_DATE };
263
+ }
264
+ const raw = value;
265
+ const day = clampInt(typeof raw.day === "number" ? raw.day : Number(raw.day), 1, 30);
266
+ const year = clampInt(typeof raw.year === "number" ? raw.year : Number(raw.year), HIJRI_YEAR_MIN, HIJRI_YEAR_MAX);
267
+ const month = resolveHijriMonthId(raw.month);
268
+ return { day, month, year };
269
+ }
270
+ function normalizeDocument2Meta(value) {
271
+ if (typeof value !== "object" || value === null) {
272
+ return emptyDocument2Meta();
273
+ }
274
+ const raw = value;
275
+ return {
276
+ title: typeof raw.title === "string" ? raw.title : "",
277
+ authors: typeof raw.authors === "string" ? raw.authors : "",
278
+ date: normalizeDocument2HijriDate(raw.date),
279
+ abstract: typeof raw.abstract === "string" ? raw.abstract : ""
280
+ };
281
+ }
282
+ function mergeDocument2Meta(fromJson, fromProp) {
283
+ const fromPropMeta = {
284
+ title: typeof fromProp?.title === "string" ? fromProp.title : "",
285
+ authors: typeof fromProp?.authors === "string" ? fromProp.authors : "",
286
+ abstract: typeof fromProp?.abstract === "string" ? fromProp.abstract : "",
287
+ date: fromProp?.date ? normalizeDocument2HijriDate({ ...DEFAULT_HIJRI_DATE, ...fromProp.date }) : { ...DEFAULT_HIJRI_DATE }
288
+ };
289
+ if (typeof fromJson !== "object" || fromJson === null) {
290
+ return fromPropMeta;
291
+ }
292
+ const json = fromJson;
293
+ return {
294
+ title: "title" in json && typeof json.title === "string" ? json.title : fromPropMeta.title,
295
+ authors: "authors" in json && typeof json.authors === "string" ? json.authors : fromPropMeta.authors,
296
+ abstract: "abstract" in json && typeof json.abstract === "string" ? json.abstract : fromPropMeta.abstract,
297
+ date: "date" in json ? normalizeDocument2HijriDate(json.date) : fromPropMeta.date
298
+ };
299
+ }
300
+ function fillDocument2MetaGaps(current, fromProp) {
301
+ if (!fromProp) {
302
+ return cloneDocument2Meta(current);
303
+ }
304
+ return {
305
+ title: current.title.trim().length > 0 ? current.title : typeof fromProp.title === "string" ? fromProp.title : current.title,
306
+ authors: current.authors.trim().length > 0 ? current.authors : typeof fromProp.authors === "string" ? fromProp.authors : current.authors,
307
+ abstract: current.abstract.trim().length > 0 ? current.abstract : typeof fromProp.abstract === "string" ? fromProp.abstract : current.abstract,
308
+ date: fromProp.date ? normalizeDocument2HijriDate({ ...current.date, ...fromProp.date }) : { ...current.date }
309
+ };
310
+ }
311
+ function document2HasMaketitle(meta) {
312
+ return meta.title.trim().length > 0 || meta.authors.trim().length > 0;
313
+ }
314
+ function document2HasAbstract(meta) {
315
+ return meta.abstract.trim().length > 0;
316
+ }
317
+ function document2HasTitleStack(meta) {
318
+ return document2HasMaketitle(meta) || document2HasAbstract(meta);
319
+ }
320
+ function formatHijriDate(date, options = "ar") {
321
+ const opts = typeof options === "string" ? { locale: options } : options;
322
+ const locale = opts.locale ?? "ar";
323
+ const digitForm = opts.digitForm ?? (locale === "en" ? "western" : "arabicIndic");
324
+ const month = hijriMonthLabel(date.month, locale);
325
+ const day = formatDigits(String(date.day), digitForm);
326
+ const year = formatDigits(String(date.year), digitForm);
327
+ if (locale === "en") {
328
+ return `${day} ${month} ${year} AH`;
329
+ }
330
+ return `${day} ${month} ${year}\u0647\u0640`;
331
+ }
332
+ function cloneDocument2Meta(meta) {
333
+ return {
334
+ title: meta.title,
335
+ authors: meta.authors,
336
+ date: { ...meta.date },
337
+ abstract: meta.abstract
338
+ };
339
+ }
340
+
125
341
  // src/document2/citations.ts
126
342
  function referenceNumberMap(references) {
127
343
  const map = /* @__PURE__ */ new Map();
@@ -557,6 +773,168 @@ ${lines.map((line) => ` ${line}`).join(" \\\\\n")}
557
773
  ${math.closing}`;
558
774
  }
559
775
 
776
+ // src/document2/labels.ts
777
+ function defaultFloatMeta() {
778
+ return {
779
+ centered: true,
780
+ captionEnabled: false,
781
+ caption: "",
782
+ labelEnabled: false,
783
+ label: ""
784
+ };
785
+ }
786
+ function parseFloatMetaFromJson(json) {
787
+ const caption = typeof json.caption === "string" ? json.caption : "";
788
+ const label = typeof json.label === "string" ? json.label : "";
789
+ return {
790
+ centered: json.centered !== false,
791
+ captionEnabled: json.caption_enabled === true || typeof json.caption_enabled !== "boolean" && caption.length > 0,
792
+ caption,
793
+ labelEnabled: json.label_enabled === true || typeof json.label_enabled !== "boolean" && label.trim().length > 0,
794
+ label
795
+ };
796
+ }
797
+ function parseRefKeys(source) {
798
+ const eqref = /^\\eqref\{([^}]*)\}$/.exec(source.trim());
799
+ if (eqref) {
800
+ return {
801
+ refCommand: "eqref",
802
+ keys: (eqref[1] ?? "").split(",").map((key) => key.trim()).filter((key) => key.length > 0)
803
+ };
804
+ }
805
+ const ref = /^\\ref\{([^}]*)\}$/.exec(source.trim());
806
+ if (ref) {
807
+ return {
808
+ refCommand: "ref",
809
+ keys: (ref[1] ?? "").split(",").map((key) => key.trim()).filter((key) => key.length > 0)
810
+ };
811
+ }
812
+ return { keys: [], refCommand: "ref" };
813
+ }
814
+ function refTokenLatex(keys, refCommand) {
815
+ const command = refCommand === "eqref" ? "\\eqref" : "\\ref";
816
+ return `${command}{${keys.join(",")}}`;
817
+ }
818
+ function collectDocument2Labels(document2) {
819
+ const entries = [];
820
+ const counters = { fig: 0, tab: 0, eq: 0 };
821
+ function pushEqFromField(tokens) {
822
+ for (const token of tokens) {
823
+ if (token.kind !== "math") {
824
+ continue;
825
+ }
826
+ if (token.display && token.labelEnabled && token.label && token.label.trim().length > 0) {
827
+ counters.eq += 1;
828
+ entries.push({
829
+ key: token.label.trim(),
830
+ kind: "eq",
831
+ caption: "",
832
+ ownerId: token.id,
833
+ number: counters.eq
834
+ });
835
+ }
836
+ }
837
+ }
838
+ function walk(blocks) {
839
+ for (const block of blocks) {
840
+ if (block.kind === "image" && block.labelEnabled && block.label.trim().length > 0) {
841
+ counters.fig += 1;
842
+ entries.push({
843
+ key: block.label.trim(),
844
+ kind: "fig",
845
+ caption: block.captionEnabled ? block.caption : "",
846
+ ownerId: block.id,
847
+ number: counters.fig
848
+ });
849
+ }
850
+ if (block.kind === "table") {
851
+ if (block.labelEnabled && block.label.trim().length > 0) {
852
+ counters.tab += 1;
853
+ entries.push({
854
+ key: block.label.trim(),
855
+ kind: "tab",
856
+ caption: block.captionEnabled ? block.caption : "",
857
+ ownerId: block.id,
858
+ number: counters.tab
859
+ });
860
+ }
861
+ for (const row of block.rows) {
862
+ for (const cell of row) {
863
+ pushEqFromField(cell.tokens);
864
+ }
865
+ }
866
+ }
867
+ if (block.kind === "textBlock") {
868
+ pushEqFromField(block.field.tokens);
869
+ }
870
+ if (block.kind === "list") {
871
+ for (const item of block.items) {
872
+ pushEqFromField(item.field.tokens);
873
+ walk(item.blocks);
874
+ }
875
+ }
876
+ }
877
+ }
878
+ walk(document2.blocks);
879
+ return entries;
880
+ }
881
+ function labelNumberMap(document2) {
882
+ const map = /* @__PURE__ */ new Map();
883
+ for (const entry of collectDocument2Labels(document2)) {
884
+ if (!map.has(entry.key)) {
885
+ map.set(entry.key, { kind: entry.kind, number: entry.number });
886
+ }
887
+ }
888
+ return map;
889
+ }
890
+ function formatRefLabel(keys, document2, refCommand, options = {}) {
891
+ const documentDirection = options.documentDirection ?? "rtl";
892
+ const digitForm = options.digitForm ?? (documentDirection === "rtl" ? "arabicIndic" : "western");
893
+ const map = labelNumberMap(document2);
894
+ const parts = keys.map((key) => {
895
+ const hit = map.get(key);
896
+ if (!hit) {
897
+ return "?";
898
+ }
899
+ return formatDigits(String(hit.number), digitForm);
900
+ });
901
+ const body = parts.join(documentDirection === "rtl" ? "\u060C" : ", ");
902
+ if (refCommand === "eqref") {
903
+ return `(${body})`;
904
+ }
905
+ return body;
906
+ }
907
+ function stripDisplayMathDelimiters(source) {
908
+ const trimmed = source.trim();
909
+ if (trimmed.startsWith("\\[") && trimmed.endsWith("\\]")) {
910
+ return trimmed.slice(2, -2).trim();
911
+ }
912
+ if (trimmed.startsWith("$$") && trimmed.endsWith("$$")) {
913
+ return trimmed.slice(2, -2).trim();
914
+ }
915
+ if (trimmed.startsWith("\\begin{equation}") && trimmed.endsWith("\\end{equation}")) {
916
+ return trimmed.slice("\\begin{equation}".length, -"\\end{equation}".length).trim();
917
+ }
918
+ return trimmed;
919
+ }
920
+ function applyFloatMetaPatch(block, patch) {
921
+ if (typeof patch.centered === "boolean") {
922
+ block.centered = patch.centered;
923
+ }
924
+ if (typeof patch.captionEnabled === "boolean") {
925
+ block.captionEnabled = patch.captionEnabled;
926
+ }
927
+ if (typeof patch.caption === "string") {
928
+ block.caption = patch.caption;
929
+ }
930
+ if (typeof patch.labelEnabled === "boolean") {
931
+ block.labelEnabled = patch.labelEnabled;
932
+ }
933
+ if (typeof patch.label === "string") {
934
+ block.label = patch.label;
935
+ }
936
+ }
937
+
560
938
  // src/document2/inlineScanner.ts
561
939
  var MATH_ENVIRONMENTS = /* @__PURE__ */ new Set([
562
940
  "equation",
@@ -632,6 +1010,43 @@ function citeSpan(value, start) {
632
1010
  }
633
1011
  return null;
634
1012
  }
1013
+ function refSpan(value, start) {
1014
+ const eqrefPrefix = "\\eqref{";
1015
+ const refPrefix = "\\ref{";
1016
+ let refCommand = "ref";
1017
+ let prefix = refPrefix;
1018
+ if (value.startsWith(eqrefPrefix, start) && !isEscaped(value, start)) {
1019
+ refCommand = "eqref";
1020
+ prefix = eqrefPrefix;
1021
+ } else if (value.startsWith(refPrefix, start) && !isEscaped(value, start)) {
1022
+ refCommand = "ref";
1023
+ prefix = refPrefix;
1024
+ } else {
1025
+ return null;
1026
+ }
1027
+ const openBrace = start + prefix.length - 1;
1028
+ if (value[openBrace] !== "{") {
1029
+ return null;
1030
+ }
1031
+ let depth = 0;
1032
+ for (let i = openBrace; i < value.length; i += 1) {
1033
+ const char = value[i];
1034
+ if (char === "{" && !isEscaped(value, i)) {
1035
+ depth += 1;
1036
+ continue;
1037
+ }
1038
+ if (char === "}" && !isEscaped(value, i)) {
1039
+ depth -= 1;
1040
+ if (depth === 0) {
1041
+ const end = i + 1;
1042
+ const source = value.slice(start, end);
1043
+ const parsed = parseRefKeys(source);
1044
+ return { kind: "ref", start, end, source, keys: parsed.keys, refCommand };
1045
+ }
1046
+ }
1047
+ }
1048
+ return null;
1049
+ }
635
1050
  function mathSpanAt(value, i) {
636
1051
  if (value.startsWith("\\begin{", i)) {
637
1052
  return environmentSpan(value, i);
@@ -679,6 +1094,12 @@ function detectInlineSpans2(value) {
679
1094
  i = cite.end;
680
1095
  continue;
681
1096
  }
1097
+ const ref = refSpan(value, i);
1098
+ if (ref) {
1099
+ spans.push(ref);
1100
+ i = ref.end;
1101
+ continue;
1102
+ }
682
1103
  const math = mathSpanAt(value, i);
683
1104
  if (math) {
684
1105
  spans.push({ kind: "math", ...math });
@@ -765,6 +1186,16 @@ function createInlineField2(value = "", mathObjects = [], options = {}, path = "
765
1186
  index = span.end;
766
1187
  continue;
767
1188
  }
1189
+ if (span.kind === "ref") {
1190
+ tokens.push({
1191
+ id: document2Id("ref"),
1192
+ kind: "ref",
1193
+ keys: span.keys.length > 0 ? span.keys : [],
1194
+ refCommand: span.refCommand
1195
+ });
1196
+ index = span.end;
1197
+ continue;
1198
+ }
768
1199
  const mathJson = mathObjects[mathObjectIndex];
769
1200
  mathObjectIndex += 1;
770
1201
  if (mathJson && (mathJson.math_mode !== span.opening || mathJson.closing !== span.closing)) {
@@ -813,7 +1244,8 @@ function parseTextBlock(json, options, path, diagnostics) {
813
1244
  id: document2Id("block"),
814
1245
  kind: "textBlock",
815
1246
  command: json.command,
816
- field: createInlineField2(value, json.math_objects ?? [], options, `${path}.value`, diagnostics)
1247
+ field: createInlineField2(value, json.math_objects ?? [], options, `${path}.value`, diagnostics),
1248
+ ...json.centered === true ? { centered: true } : {}
817
1249
  };
818
1250
  }
819
1251
  function parseListItem(json, options, path, diagnostics) {
@@ -866,7 +1298,8 @@ function parseTableBlock(json, options, path, diagnostics) {
866
1298
  command: "\\begin{tabular}",
867
1299
  closing: "\\end{tabular}",
868
1300
  columns: typeof json.columns === "string" ? json.columns : "",
869
- rows
1301
+ rows,
1302
+ ...parseFloatMetaFromJson(json)
870
1303
  };
871
1304
  }
872
1305
  function parseImageBlock(json) {
@@ -878,7 +1311,8 @@ function parseImageBlock(json) {
878
1311
  command: "\\includegraphics",
879
1312
  value,
880
1313
  ...assetId !== void 0 ? { assetId } : {},
881
- options: isRecordOfStrings(json.options) ? json.options : {}
1314
+ options: isRecordOfStrings(json.options) ? json.options : {},
1315
+ ...parseFloatMetaFromJson(json)
882
1316
  };
883
1317
  }
884
1318
  function parseRawBlock(json) {
@@ -933,13 +1367,20 @@ function fromDocumentJson2(json, options = {}) {
933
1367
  const diagnostics = [];
934
1368
  return {
935
1369
  nodeType: "DocumentObject",
1370
+ meta: normalizeDocument2Meta(json.meta),
936
1371
  references: parseReferences(json.references),
937
1372
  blocks: json.blocks.map((block, index) => parseBlock(asBlockJson(block), options, `$.blocks[${String(index)}]`, diagnostics)),
938
1373
  diagnostics
939
1374
  };
940
1375
  }
941
1376
  function createEmptyDocument2() {
942
- return { nodeType: "DocumentObject", references: [], blocks: [], diagnostics: [] };
1377
+ return {
1378
+ nodeType: "DocumentObject",
1379
+ meta: emptyDocument2Meta(),
1380
+ references: [],
1381
+ blocks: [],
1382
+ diagnostics: []
1383
+ };
943
1384
  }
944
1385
 
945
1386
  // src/editor/atomicCommands.ts
@@ -3294,6 +3735,7 @@ function cloneBlock(block) {
3294
3735
  function cloneDocument(document2) {
3295
3736
  return {
3296
3737
  nodeType: "DocumentObject",
3738
+ meta: cloneDocument2Meta(document2.meta ?? emptyDocument2Meta()),
3297
3739
  references: document2.references.map((reference) => ({ ...reference })),
3298
3740
  blocks: document2.blocks.map(cloneBlock),
3299
3741
  diagnostics: document2.diagnostics.map((diagnostic) => ({ ...diagnostic }))
@@ -3404,6 +3846,29 @@ function moveDocument2BlockById(document2, blockId, direction) {
3404
3846
  next.blocks.splice(targetIndex, 0, block);
3405
3847
  return next;
3406
3848
  }
3849
+ function moveDocument2BlockRange(document2, from, to, direction) {
3850
+ const count = document2.blocks.length;
3851
+ if (from < 0 || to < 0 || from >= count || to >= count || from > to) {
3852
+ return document2;
3853
+ }
3854
+ const insertAt = from + direction;
3855
+ if (insertAt < 0 || to + direction >= count) {
3856
+ return document2;
3857
+ }
3858
+ const next = cloneDocument(document2);
3859
+ const slice = next.blocks.splice(from, to - from + 1);
3860
+ next.blocks.splice(insertAt, 0, ...slice);
3861
+ return next;
3862
+ }
3863
+ function removeDocument2BlockRange(document2, from, to) {
3864
+ const count = document2.blocks.length;
3865
+ if (from < 0 || to < 0 || from >= count || to >= count || from > to) {
3866
+ return document2;
3867
+ }
3868
+ const next = cloneDocument(document2);
3869
+ next.blocks = [...next.blocks.slice(0, from), ...next.blocks.slice(to + 1)];
3870
+ return next;
3871
+ }
3407
3872
  function normalizeFieldTokens(tokens) {
3408
3873
  if (tokens.length === 0) {
3409
3874
  return [{ id: document2Id("text"), kind: "text", text: "" }];
@@ -3498,16 +3963,50 @@ function replaceMathTokenFromSession(document2, tokenId, session, opening, closi
3498
3963
  const current = field.tokens[index];
3499
3964
  const open = opening ?? current.opening;
3500
3965
  const close = closing ?? current.closing;
3501
- field.tokens[index] = mathTokenFromSession(session, open, close, side);
3502
- field.tokens[index].id = current.id;
3966
+ const replaced = mathTokenFromSession(session, open, close, side);
3967
+ replaced.id = current.id;
3968
+ if (current.labelEnabled !== void 0) {
3969
+ replaced.labelEnabled = current.labelEnabled;
3970
+ }
3971
+ if (current.label !== void 0) {
3972
+ replaced.label = current.label;
3973
+ }
3974
+ field.tokens[index] = replaced;
3503
3975
  return true;
3504
3976
  });
3505
3977
  return next;
3506
3978
  }
3507
3979
  function addDocument2TextBlock(document2, command = "\\paragraph", afterBlockId) {
3508
- const block = { id: document2Id("block"), kind: "textBlock", command, field: createInlineField2("") };
3980
+ const block = {
3981
+ id: document2Id("block"),
3982
+ kind: "textBlock",
3983
+ command,
3984
+ field: createInlineField2(""),
3985
+ ...command === "\\paragraph" ? { centered: false } : {}
3986
+ };
3509
3987
  return insertDocument2BlockAfter(document2, afterBlockId, block);
3510
3988
  }
3989
+ function updateDocument2TextBlockCentered(document2, blockId, centered) {
3990
+ const next = cloneDocument(document2);
3991
+ function visit(blocks) {
3992
+ for (const block of blocks) {
3993
+ if (block.id === blockId && block.kind === "textBlock" && block.command === "\\paragraph") {
3994
+ block.centered = centered;
3995
+ return true;
3996
+ }
3997
+ if (block.kind === "list") {
3998
+ for (const item of block.items) {
3999
+ if (visit(item.blocks)) {
4000
+ return true;
4001
+ }
4002
+ }
4003
+ }
4004
+ }
4005
+ return false;
4006
+ }
4007
+ visit(next.blocks);
4008
+ return next;
4009
+ }
3511
4010
  function removeDocument2BlockById(document2, blockId) {
3512
4011
  const next = cloneDocument(document2);
3513
4012
  next.blocks = next.blocks.filter((block) => block.id !== blockId);
@@ -3544,7 +4043,8 @@ function addDocument2TableBlock(document2, columns = "lll", rowCount = 3, colCou
3544
4043
  command: "\\begin{tabular}",
3545
4044
  closing: "\\end{tabular}",
3546
4045
  columns: columns || "l".repeat(cols),
3547
- rows: tableRows
4046
+ rows: tableRows,
4047
+ ...defaultFloatMeta()
3548
4048
  };
3549
4049
  return insertDocument2BlockAfter(document2, afterBlockId, block);
3550
4050
  }
@@ -3554,7 +4054,8 @@ function addDocument2ImageBlock(document2, src = "", afterBlockId) {
3554
4054
  kind: "image",
3555
4055
  command: "\\includegraphics",
3556
4056
  value: src,
3557
- options: {}
4057
+ options: { width: "0.8\\textwidth" },
4058
+ ...defaultFloatMeta()
3558
4059
  };
3559
4060
  return insertDocument2BlockAfter(document2, afterBlockId, block);
3560
4061
  }
@@ -3579,6 +4080,128 @@ function updateDocument2ImageValue(document2, blockId, value) {
3579
4080
  visit(next.blocks);
3580
4081
  return next;
3581
4082
  }
4083
+ function updateDocument2ImageMeta(document2, blockId, patch) {
4084
+ const next = cloneDocument(document2);
4085
+ function visit(blocks) {
4086
+ for (const block of blocks) {
4087
+ if (block.id === blockId && block.kind === "image") {
4088
+ applyFloatMetaPatch(block, patch);
4089
+ return true;
4090
+ }
4091
+ if (block.kind === "list") {
4092
+ for (const item of block.items) {
4093
+ if (visit(item.blocks)) {
4094
+ return true;
4095
+ }
4096
+ }
4097
+ }
4098
+ }
4099
+ return false;
4100
+ }
4101
+ visit(next.blocks);
4102
+ return next;
4103
+ }
4104
+ function updateDocument2TableMeta(document2, blockId, patch) {
4105
+ const next = cloneDocument(document2);
4106
+ function visit(blocks) {
4107
+ for (const block of blocks) {
4108
+ if (block.id === blockId && block.kind === "table") {
4109
+ applyFloatMetaPatch(block, patch);
4110
+ return true;
4111
+ }
4112
+ if (block.kind === "list") {
4113
+ for (const item of block.items) {
4114
+ if (visit(item.blocks)) {
4115
+ return true;
4116
+ }
4117
+ }
4118
+ }
4119
+ }
4120
+ return false;
4121
+ }
4122
+ visit(next.blocks);
4123
+ return next;
4124
+ }
4125
+ function updateMathTokenLabel(document2, tokenId, patch) {
4126
+ const next = cloneDocument(document2);
4127
+ visitFields(next.blocks, (field) => {
4128
+ const token = field.tokens.find((entry) => entry.id === tokenId && entry.kind === "math");
4129
+ if (!token) {
4130
+ return false;
4131
+ }
4132
+ if (typeof patch.labelEnabled === "boolean") {
4133
+ token.labelEnabled = patch.labelEnabled;
4134
+ }
4135
+ if (typeof patch.label === "string") {
4136
+ token.label = patch.label;
4137
+ }
4138
+ return true;
4139
+ });
4140
+ return next;
4141
+ }
4142
+ function refTokenFromKeys(keys, refCommand) {
4143
+ return { id: document2Id("ref"), kind: "ref", keys: [...keys], refCommand };
4144
+ }
4145
+ function insertRefTokenAtCaret(document2, fieldId, textTokenId, caretOffset, keys, refCommand = "ref") {
4146
+ if (keys.length === 0) {
4147
+ return document2;
4148
+ }
4149
+ const next = cloneDocument(document2);
4150
+ const refToken = refTokenFromKeys(keys, refCommand);
4151
+ visitFields(next.blocks, (field) => {
4152
+ if (field.id !== fieldId) {
4153
+ return false;
4154
+ }
4155
+ const textIndex = textTokenId ? field.tokens.findIndex((token) => token.id === textTokenId && token.kind === "text") : field.tokens.findIndex((token) => token.kind === "text");
4156
+ if (textIndex < 0) {
4157
+ field.tokens.push(refToken);
4158
+ field.tokens.push({ id: document2Id("text"), kind: "text", text: "" });
4159
+ return true;
4160
+ }
4161
+ const current = field.tokens[textIndex];
4162
+ const [before, after] = splitTextTokenAt(current, caretOffset);
4163
+ const parts = [...field.tokens.slice(0, textIndex)];
4164
+ if (before.text.length > 0) {
4165
+ parts.push(before);
4166
+ }
4167
+ parts.push(refToken);
4168
+ parts.push(after);
4169
+ parts.push(...field.tokens.slice(textIndex + 1));
4170
+ field.tokens = normalizeFieldTokens(parts);
4171
+ return true;
4172
+ });
4173
+ return next;
4174
+ }
4175
+ function updateRefTokenKeys(document2, tokenId, keys, refCommand) {
4176
+ if (keys.length === 0) {
4177
+ return document2;
4178
+ }
4179
+ const next = cloneDocument(document2);
4180
+ visitFields(next.blocks, (field) => {
4181
+ const token = field.tokens.find((entry) => entry.id === tokenId && entry.kind === "ref");
4182
+ if (!token) {
4183
+ return false;
4184
+ }
4185
+ token.keys = [...keys];
4186
+ if (refCommand) {
4187
+ token.refCommand = refCommand;
4188
+ }
4189
+ return true;
4190
+ });
4191
+ return next;
4192
+ }
4193
+ function removeRefTokenById(document2, tokenId) {
4194
+ const next = cloneDocument(document2);
4195
+ visitFields(next.blocks, (field) => {
4196
+ const index = field.tokens.findIndex((token) => token.id === tokenId && token.kind === "ref");
4197
+ if (index < 0) {
4198
+ return false;
4199
+ }
4200
+ field.tokens = normalizeFieldTokens(stitchTextAroundRemovedToken(field.tokens, index));
4201
+ return true;
4202
+ });
4203
+ return next;
4204
+ }
3582
4205
  function mutateListBlockById(blocks, listBlockId, mutator) {
3583
4206
  for (const block of blocks) {
3584
4207
  if (block.id === listBlockId && block.kind === "list") {
@@ -3742,12 +4365,24 @@ function setDocument2References(document2, references) {
3742
4365
  next.references = references.map((reference) => ({ ...reference }));
3743
4366
  return next;
3744
4367
  }
4368
+ function updateDocument2Meta(document2, patch) {
4369
+ const next = cloneDocument(document2);
4370
+ const current = next.meta ?? emptyDocument2Meta();
4371
+ next.meta = {
4372
+ title: typeof patch.title === "string" ? patch.title : current.title,
4373
+ authors: typeof patch.authors === "string" ? patch.authors : current.authors,
4374
+ abstract: typeof patch.abstract === "string" ? patch.abstract : current.abstract,
4375
+ date: patch.date ? normalizeDocument2HijriDate({ ...current.date, ...patch.date }) : { ...current.date }
4376
+ };
4377
+ return next;
4378
+ }
3745
4379
 
3746
4380
  // src/document2/arabicPreamble.ts
3747
- function arabicXeLatexPreamblePkg() {
3748
- return String.raw`
3749
- \documentclass[12pt,a4paper]{article}
3750
- \usepackage{amsmath,amsfonts,amssymb,mathrsfs,tikz,fancyhdr, mathtools}
4381
+ function arabicXeLatexPreamblePkg(twocolumn = false) {
4382
+ const classOptions = twocolumn ? "12pt,a4paper,twocolumn" : "12pt,a4paper";
4383
+ return `
4384
+ \\documentclass[${classOptions}]{article}
4385
+ ` + String.raw`\usepackage{amsmath,amsfonts,amssymb,mathrsfs,tikz,fancyhdr, mathtools}
3751
4386
  \usepackage{fontspec} % For loading OpenType fonts
3752
4387
  \usepackage{unicode-math} % For setting the math font
3753
4388
  \usepackage{polyglossia} % For Arabic support
@@ -3937,11 +4572,21 @@ function arabicXeLatexPreambleCmd() {
3937
4572
  \newcommand{\arbinom}[2]{\left(\begin{array}{c} #1 \\ #2 \end{array} \right)}
3938
4573
  `;
3939
4574
  }
3940
- function getArabicXeLatexPreamble(digitsMapping = "arabicdigits") {
3941
- return arabicXeLatexPreamblePkg() + arabicXeLatexPreambleFont(digitsMapping) + arabicXeLatexPreambleCmd();
4575
+ function getArabicXeLatexPreamble(digitsMappingOrOptions = "arabicdigits") {
4576
+ const options = typeof digitsMappingOrOptions === "string" ? { digitsMapping: digitsMappingOrOptions } : digitsMappingOrOptions;
4577
+ const digitsMapping = options.digitsMapping ?? "arabicdigits";
4578
+ const twocolumn = options.twocolumn === true;
4579
+ return arabicXeLatexPreamblePkg(twocolumn) + arabicXeLatexPreambleFont(digitsMapping) + arabicXeLatexPreambleCmd();
3942
4580
  }
3943
4581
 
3944
4582
  // src/document2/exportLatex.ts
4583
+ function mathBodyLatex(token) {
4584
+ if (!token.math || token.sourceOwner === "raw" || token.sourceOwner === "editor") {
4585
+ return stripDisplayMathDelimiters(token.source);
4586
+ }
4587
+ const rendered = renderMathNodeLatex(token.math);
4588
+ return stripDisplayMathDelimiters(rendered);
4589
+ }
3945
4590
  function tokenLatex(token) {
3946
4591
  if (token.kind === "text") {
3947
4592
  return token.text;
@@ -3949,6 +4594,16 @@ function tokenLatex(token) {
3949
4594
  if (token.kind === "cite") {
3950
4595
  return citeTokenLatex(token.keys);
3951
4596
  }
4597
+ if (token.kind === "ref") {
4598
+ return refTokenLatex(token.keys, token.refCommand);
4599
+ }
4600
+ if (token.display && token.labelEnabled && token.label && token.label.trim().length > 0) {
4601
+ const body = mathBodyLatex(token);
4602
+ return `\\begin{equation}
4603
+ ${body}
4604
+ \\label{${token.label.trim()}}
4605
+ \\end{equation}`;
4606
+ }
3952
4607
  if (!token.math || token.sourceOwner === "raw" || token.sourceOwner === "editor") {
3953
4608
  return token.source;
3954
4609
  }
@@ -3958,7 +4613,13 @@ function inlineFieldLatex(field) {
3958
4613
  return field.tokens.map((token) => tokenLatex(token)).join("");
3959
4614
  }
3960
4615
  function textBlockLatex(block) {
3961
- return `${block.command}{${inlineFieldLatex(block.field)}}`;
4616
+ const body = `${block.command}{${inlineFieldLatex(block.field)}}`;
4617
+ if (block.command === "\\paragraph" && block.centered) {
4618
+ return `\\begin{center}
4619
+ ${body}
4620
+ \\end{center}`;
4621
+ }
4622
+ return body;
3962
4623
  }
3963
4624
  function indent(value) {
3964
4625
  return value.split("\n").map((line) => ` ${line}`).join("\n");
@@ -3972,12 +4633,32 @@ function listBlockLatex(block, references) {
3972
4633
  ${items}
3973
4634
  ${block.closing}`;
3974
4635
  }
3975
- function tableBlockLatex(block) {
4636
+ function tabularInnerLatex(block) {
3976
4637
  const rows = block.rows.map((row) => ` ${row.map((cell) => inlineFieldLatex(cell)).join(" & ")}`).join(" \\\\\n");
3977
4638
  return `${block.command}{${block.columns}}
3978
4639
  ${rows}
3979
4640
  ${block.closing}`;
3980
4641
  }
4642
+ function floatCaptionLabelLines(block) {
4643
+ const lines = [];
4644
+ if (block.captionEnabled && block.caption.trim().length > 0) {
4645
+ lines.push(` \\caption{${block.caption.trim()}}`);
4646
+ }
4647
+ if (block.labelEnabled && block.label.trim().length > 0) {
4648
+ lines.push(` \\label{${block.label.trim()}}`);
4649
+ }
4650
+ return lines;
4651
+ }
4652
+ function tableBlockLatex(block) {
4653
+ const lines = ["\\begin{table}[htbp]"];
4654
+ if (block.centered) {
4655
+ lines.push(" \\centering");
4656
+ }
4657
+ lines.push(...floatCaptionLabelLines(block));
4658
+ lines.push(indent(tabularInnerLatex(block)));
4659
+ lines.push("\\end{table}");
4660
+ return lines.join("\n");
4661
+ }
3981
4662
  function imageOptionsLatex(block) {
3982
4663
  const entries = Object.entries(block.options);
3983
4664
  if (entries.length === 0) {
@@ -3985,6 +4666,16 @@ function imageOptionsLatex(block) {
3985
4666
  }
3986
4667
  return `[${entries.map(([key, value]) => `${key}=${value}`).join(",")}]`;
3987
4668
  }
4669
+ function imageBlockLatex(block) {
4670
+ const lines = ["\\begin{figure}[htbp]"];
4671
+ if (block.centered) {
4672
+ lines.push(" \\centering");
4673
+ }
4674
+ lines.push(` ${block.command}${imageOptionsLatex(block)}{${block.value}}`);
4675
+ lines.push(...floatCaptionLabelLines(block));
4676
+ lines.push("\\end{figure}");
4677
+ return lines.join("\n");
4678
+ }
3988
4679
  function blockLatex(block, references) {
3989
4680
  if (block.kind === "textBlock") {
3990
4681
  return textBlockLatex(block);
@@ -3996,7 +4687,7 @@ function blockLatex(block, references) {
3996
4687
  return tableBlockLatex(block);
3997
4688
  }
3998
4689
  if (block.kind === "image") {
3999
- return `${block.command}${imageOptionsLatex(block)}{${block.value}}`;
4690
+ return imageBlockLatex(block);
4000
4691
  }
4001
4692
  if (block.kind === "bibliography") {
4002
4693
  const width = String(Math.max(references.length, 9));
@@ -4007,13 +4698,42 @@ ${block.closing}`;
4007
4698
  }
4008
4699
  return block.value;
4009
4700
  }
4701
+ function authorLatex(authors) {
4702
+ return authors.split(/\n+/).map((line) => line.trim()).filter((line) => line.length > 0).join(" \\\\ ");
4703
+ }
4704
+ function articleFrontMatterLatex(meta) {
4705
+ const parts = [butexDiwaniDisplayLatex(BUTEX_BASMALA)];
4706
+ if (document2HasMaketitle(meta)) {
4707
+ if (meta.title.trim().length > 0) {
4708
+ parts.push(`\\title{${meta.title.trim()}}`);
4709
+ }
4710
+ if (meta.authors.trim().length > 0) {
4711
+ parts.push(`\\author{${authorLatex(meta.authors)}}`);
4712
+ }
4713
+ parts.push(`\\date{${formatHijriDate(meta.date, { locale: "ar", digitForm: "arabicIndic" })}}`);
4714
+ parts.push("\\maketitle");
4715
+ }
4716
+ if (document2HasAbstract(meta)) {
4717
+ parts.push(`\\begin{abstract}
4718
+ ${meta.abstract.trim()}
4719
+ \\end{abstract}`);
4720
+ }
4721
+ return parts;
4722
+ }
4010
4723
  function document2Latex(document2, options = {}) {
4011
- const body = document2.blocks.map((block) => blockLatex(block, document2.references)).join("\n\n");
4724
+ const meta = document2.meta ?? emptyDocument2Meta();
4725
+ const front = articleFrontMatterLatex(meta);
4726
+ const bodyBlocks = document2.blocks.map((block) => blockLatex(block, document2.references));
4727
+ const closing = butexDiwaniDisplayLatex(BUTEX_CLOSING_HAMDALA);
4728
+ const body = [...front, ...bodyBlocks, closing].filter((part) => part.length > 0).join("\n\n");
4012
4729
  const wrapDocument = options.wrapDocument !== false;
4013
4730
  if (!wrapDocument) {
4014
4731
  return body;
4015
4732
  }
4016
- const preamble = getArabicXeLatexPreamble(options.digitsMapping ?? "arabicdigits");
4733
+ const preamble = getArabicXeLatexPreamble({
4734
+ digitsMapping: options.digitsMapping ?? "arabicdigits",
4735
+ twocolumn: options.twocolumn === true
4736
+ });
4017
4737
  return `${preamble}
4018
4738
  \\begin{document}
4019
4739
 
@@ -4056,6 +4776,67 @@ function document2HistoryCanRedo(stacks) {
4056
4776
  return stacks.future.length > 0;
4057
4777
  }
4058
4778
 
4779
+ // src/document2/blockSelection.ts
4780
+ function emptyBlockSelection() {
4781
+ return { selection: null, anchor: null };
4782
+ }
4783
+ function normalizeBlockSelection(selection, blockCount) {
4784
+ if (!selection) {
4785
+ return null;
4786
+ }
4787
+ const { from, to } = selection;
4788
+ if (blockCount <= 0 || from < 0 || to < 0 || from >= blockCount || to >= blockCount || from > to) {
4789
+ return null;
4790
+ }
4791
+ return { from, to };
4792
+ }
4793
+ function inRange(selection, index) {
4794
+ return index >= selection.from && index <= selection.to;
4795
+ }
4796
+ function toggleBlockInSelection(state, index, blockCount) {
4797
+ if (index < 0 || index >= blockCount) {
4798
+ return state;
4799
+ }
4800
+ const selection = normalizeBlockSelection(state.selection, blockCount);
4801
+ if (!selection) {
4802
+ return { selection: { from: index, to: index }, anchor: index };
4803
+ }
4804
+ if (inRange(selection, index)) {
4805
+ if (selection.from === selection.to) {
4806
+ return emptyBlockSelection();
4807
+ }
4808
+ if (index === selection.from) {
4809
+ return { selection: { from: selection.from + 1, to: selection.to }, anchor: state.anchor };
4810
+ }
4811
+ if (index === selection.to) {
4812
+ return { selection: { from: selection.from, to: selection.to - 1 }, anchor: state.anchor };
4813
+ }
4814
+ return emptyBlockSelection();
4815
+ }
4816
+ if (index === selection.from - 1) {
4817
+ return { selection: { from: index, to: selection.to }, anchor: state.anchor ?? index };
4818
+ }
4819
+ if (index === selection.to + 1) {
4820
+ return { selection: { from: selection.from, to: index }, anchor: state.anchor ?? index };
4821
+ }
4822
+ return { selection: { from: index, to: index }, anchor: index };
4823
+ }
4824
+ function extendBlockSelectionFromAnchor(state, index, blockCount) {
4825
+ if (index < 0 || index >= blockCount) {
4826
+ return state;
4827
+ }
4828
+ const anchor = state.anchor ?? index;
4829
+ const from = Math.min(anchor, index);
4830
+ const to = Math.max(anchor, index);
4831
+ return {
4832
+ selection: { from, to },
4833
+ anchor: state.anchor ?? index
4834
+ };
4835
+ }
4836
+ function remapSelectionAfterRangeMove(selection, direction) {
4837
+ return { from: selection.from + direction, to: selection.to + direction };
4838
+ }
4839
+
4059
4840
  // src/document2/previewModel.ts
4060
4841
  function mathTex(token, equationSide) {
4061
4842
  return mathTokenSourceForSide(token, equationSide);
@@ -4076,6 +4857,18 @@ function previewInlines(field, islands, output, equationSide, document2, preview
4076
4857
  })
4077
4858
  };
4078
4859
  }
4860
+ if (token.kind === "ref") {
4861
+ return {
4862
+ kind: "ref",
4863
+ id: token.id,
4864
+ keys: [...token.keys],
4865
+ refCommand: token.refCommand,
4866
+ label: formatRefLabel(token.keys, document2, token.refCommand, {
4867
+ documentDirection: previewOptions.documentDirection,
4868
+ digitForm: previewOptions.digitForm
4869
+ })
4870
+ };
4871
+ }
4079
4872
  const island = {
4080
4873
  id: token.id,
4081
4874
  tex: mathTex(token, equationSide),
@@ -4098,7 +4891,28 @@ function textBlockPreview(block, islands, output, equationSide, document2, previ
4098
4891
  if (block.command === "\\subsubsection") {
4099
4892
  return { kind: "heading", id: block.id, level: 3, inlines };
4100
4893
  }
4101
- return { kind: "paragraph", id: block.id, inlines };
4894
+ return {
4895
+ kind: "paragraph",
4896
+ id: block.id,
4897
+ inlines,
4898
+ ...block.centered ? { centered: true } : {}
4899
+ };
4900
+ }
4901
+ function floatNumberLabel(document2, key, previewOptions) {
4902
+ if (!key || key.trim().length === 0) {
4903
+ return void 0;
4904
+ }
4905
+ const hit = labelNumberMap(document2).get(key.trim());
4906
+ if (!hit || hit.kind !== "fig" && hit.kind !== "tab") {
4907
+ return void 0;
4908
+ }
4909
+ const locale = previewOptions.uiLocale ?? "ar";
4910
+ const kindWord = hit.kind === "fig" ? locale === "ar" ? "\u0627\u0644\u0634\u0643\u0644" : "Figure" : locale === "ar" ? "\u0627\u0644\u062C\u062F\u0648\u0644" : "Table";
4911
+ const digits = formatBibliographyNumber(hit.number, {
4912
+ documentDirection: previewOptions.documentDirection,
4913
+ digitForm: previewOptions.digitForm
4914
+ });
4915
+ return `${kindWord} ${digits}`;
4102
4916
  }
4103
4917
  function blockPreview(block, islands, output, equationSide, document2, previewOptions) {
4104
4918
  if (block.kind === "textBlock") {
@@ -4117,19 +4931,25 @@ function blockPreview(block, islands, output, equationSide, document2, previewOp
4117
4931
  };
4118
4932
  }
4119
4933
  if (block.kind === "table") {
4934
+ const label = block.labelEnabled ? block.label.trim() : "";
4120
4935
  return {
4121
4936
  kind: "table",
4122
4937
  id: block.id,
4123
- rows: block.rows.map((row) => row.map((cell) => previewInlines(cell, islands, output, equationSide, document2, previewOptions)))
4938
+ rows: block.rows.map((row) => row.map((cell) => previewInlines(cell, islands, output, equationSide, document2, previewOptions))),
4939
+ ...block.captionEnabled && block.caption.trim().length > 0 ? { caption: block.caption.trim() } : {},
4940
+ ...label.length > 0 ? { label, numberLabel: floatNumberLabel(document2, label, previewOptions) } : {}
4124
4941
  };
4125
4942
  }
4126
4943
  if (block.kind === "image") {
4944
+ const label = block.labelEnabled ? block.label.trim() : "";
4127
4945
  return {
4128
4946
  kind: "image",
4129
4947
  id: block.id,
4130
4948
  src: block.value,
4131
4949
  ...block.assetId !== void 0 ? { assetId: block.assetId } : {},
4132
- options: block.options
4950
+ options: block.options,
4951
+ ...block.captionEnabled && block.caption.trim().length > 0 ? { caption: block.caption.trim() } : {},
4952
+ ...label.length > 0 ? { label, numberLabel: floatNumberLabel(document2, label, previewOptions) } : {}
4133
4953
  };
4134
4954
  }
4135
4955
  if (block.kind === "bibliography") {
@@ -4153,55 +4973,107 @@ function blockPreview(block, islands, output, equationSide, document2, previewOp
4153
4973
  }
4154
4974
  return { kind: "omit", id: block.id };
4155
4975
  }
4976
+ function articleChromePreview(document2, uiLocale, previewOptions) {
4977
+ const meta = document2.meta ?? emptyDocument2Meta();
4978
+ const blocks = [
4979
+ { kind: "basmala", id: "article-basmala", text: BUTEX_BASMALA, tex: butexDiwaniDisplayTex(BUTEX_BASMALA) }
4980
+ ];
4981
+ if (document2HasMaketitle(meta)) {
4982
+ blocks.push({
4983
+ kind: "titleBlock",
4984
+ id: "article-title",
4985
+ ...meta.title.trim().length > 0 ? { title: meta.title.trim() } : {},
4986
+ ...meta.authors.trim().length > 0 ? { authors: meta.authors.trim() } : {},
4987
+ date: formatHijriDate(meta.date, {
4988
+ locale: uiLocale,
4989
+ digitForm: previewOptions.digitForm
4990
+ })
4991
+ });
4992
+ }
4993
+ if (document2HasAbstract(meta)) {
4994
+ blocks.push({ kind: "abstract", id: "article-abstract", text: meta.abstract.trim() });
4995
+ }
4996
+ return blocks;
4997
+ }
4156
4998
  function document2Preview(document2, output = "svg", equationSide = "arabic", previewOptions = {}) {
4157
4999
  const mathIslands = [];
4158
5000
  const options = {
4159
5001
  documentDirection: previewOptions.documentDirection ?? "rtl",
4160
- digitForm: previewOptions.digitForm ?? (previewOptions.documentDirection === "ltr" ? "western" : "arabicIndic")
5002
+ digitForm: previewOptions.digitForm ?? (previewOptions.documentDirection === "ltr" ? "western" : "arabicIndic"),
5003
+ uiLocale: previewOptions.uiLocale ?? "ar"
4161
5004
  };
5005
+ const body = document2.blocks.map((block) => blockPreview(block, mathIslands, output, equationSide, document2, options));
4162
5006
  return {
4163
- blocks: document2.blocks.map((block) => blockPreview(block, mathIslands, output, equationSide, document2, options)),
5007
+ blocks: [
5008
+ ...articleChromePreview(document2, options.uiLocale ?? "ar", options),
5009
+ ...body,
5010
+ { kind: "closing", id: "article-closing", text: BUTEX_CLOSING_HAMDALA, tex: butexDiwaniDisplayTex(BUTEX_CLOSING_HAMDALA) }
5011
+ ],
4164
5012
  mathIslands
4165
5013
  };
4166
5014
  }
4167
5015
  // Annotate the CommonJS export names for ESM import in node:
4168
5016
  0 && (module.exports = {
5017
+ BUTEX_BASMALA,
5018
+ BUTEX_CLOSING_HAMDALA,
4169
5019
  DEFAULT_DOCUMENT2_HISTORY_MAX_DEPTH,
5020
+ DEFAULT_HIJRI_DATE,
5021
+ HIJRI_MONTH_IDS,
5022
+ HIJRI_YEAR_MAX,
5023
+ HIJRI_YEAR_MIN,
4170
5024
  addDocument2ImageBlock,
4171
5025
  addDocument2ListBlock,
4172
5026
  addDocument2ListItem,
4173
5027
  addDocument2Reference,
4174
5028
  addDocument2TableBlock,
4175
5029
  addDocument2TextBlock,
5030
+ applyFloatMetaPatch,
4176
5031
  arabicXeLatexPreambleCmd,
4177
5032
  arabicXeLatexPreambleFont,
4178
5033
  arabicXeLatexPreamblePkg,
4179
5034
  bibliographyEntryLatex,
5035
+ butexDiwaniDisplayLatex,
5036
+ butexDiwaniDisplayTex,
4180
5037
  citeTokenLatex,
5038
+ cloneDocument2Meta,
4181
5039
  cloneDocument2Node,
5040
+ collectDocument2Labels,
4182
5041
  createDocument2History,
4183
5042
  createEmptyArabicEquationSession,
4184
5043
  createEmptyDocument2,
4185
5044
  createEmptyEquationSession,
4186
5045
  createEmptyReference2,
4187
5046
  createInlineField2,
5047
+ defaultFloatMeta,
4188
5048
  detectInlineSpans2,
4189
5049
  detectMathSpans2,
5050
+ document2HasAbstract,
5051
+ document2HasMaketitle,
5052
+ document2HasTitleStack,
4190
5053
  document2HistoryCanRedo,
4191
5054
  document2HistoryCanUndo,
4192
5055
  document2Latex,
4193
5056
  document2Preview,
5057
+ emptyBlockSelection,
5058
+ emptyDocument2Meta,
4194
5059
  ensureDocument2BibliographyBlock,
5060
+ extendBlockSelectionFromAnchor,
5061
+ fillDocument2MetaGaps,
4195
5062
  findFirstInlineField,
4196
5063
  formatBibliographyNumber,
4197
5064
  formatCiteLabel,
5065
+ formatHijriDate,
5066
+ formatRefLabel,
4198
5067
  fromDocumentJson2,
4199
5068
  getArabicXeLatexPreamble,
5069
+ hijriMonthLabel,
4200
5070
  inlineFieldLatex,
4201
5071
  insertCiteTokenAtCaret,
4202
5072
  insertDocument2BlockAfter,
4203
5073
  insertMathToken,
4204
5074
  insertMathTokenAtCaret,
5075
+ insertRefTokenAtCaret,
5076
+ labelNumberMap,
4205
5077
  mathNodeFromArabicSession,
4206
5078
  mathNodeFromSession,
4207
5079
  mathSourceFromArabicSession,
@@ -4209,25 +5081,44 @@ function document2Preview(document2, output = "svg", equationSide = "arabic", pr
4209
5081
  mathTokenSourceForSide,
4210
5082
  mathTokenToArabicEditorSession,
4211
5083
  mathTokenToEditorSession,
5084
+ mergeDocument2Meta,
4212
5085
  moveDocument2BlockById,
5086
+ moveDocument2BlockRange,
4213
5087
  moveDocument2Reference,
5088
+ normalizeBlockSelection,
5089
+ normalizeDocument2HijriDate,
5090
+ normalizeDocument2Meta,
4214
5091
  parseCiteKeys,
5092
+ parseFloatMetaFromJson,
5093
+ parseRefKeys,
4215
5094
  pushDocument2Snapshot,
5095
+ refTokenLatex,
4216
5096
  referenceFromJson,
4217
5097
  referenceNumberMap,
5098
+ remapSelectionAfterRangeMove,
4218
5099
  removeCiteTokenById,
4219
5100
  removeDocument2BlockById,
5101
+ removeDocument2BlockRange,
4220
5102
  removeDocument2ListItem,
4221
5103
  removeDocument2Reference,
4222
5104
  removeMathTokenById,
5105
+ removeRefTokenById,
4223
5106
  replaceMathTokenFromSession,
4224
5107
  resolveCiteNumbers,
4225
5108
  restoreDocument2Redo,
4226
5109
  restoreDocument2Undo,
4227
5110
  setDocument2References,
5111
+ stripDisplayMathDelimiters,
5112
+ toggleBlockInSelection,
4228
5113
  updateCiteTokenKeys,
5114
+ updateDocument2ImageMeta,
4229
5115
  updateDocument2ImageValue,
5116
+ updateDocument2Meta,
4230
5117
  updateDocument2Reference,
5118
+ updateDocument2TableMeta,
5119
+ updateDocument2TextBlockCentered,
5120
+ updateMathTokenLabel,
5121
+ updateRefTokenKeys,
4231
5122
  updateTextToken
4232
5123
  });
4233
5124
  //# sourceMappingURL=document2.js.map