@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.
package/dist/document2.js CHANGED
@@ -20,42 +20,71 @@ 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_BASMALA_LINE1: () => BUTEX_BASMALA_LINE1,
25
+ BUTEX_BASMALA_SALAWAT: () => BUTEX_BASMALA_SALAWAT,
26
+ BUTEX_CLOSING_HAMDALA: () => BUTEX_CLOSING_HAMDALA,
23
27
  DEFAULT_DOCUMENT2_HISTORY_MAX_DEPTH: () => DEFAULT_DOCUMENT2_HISTORY_MAX_DEPTH,
28
+ DEFAULT_HIJRI_DATE: () => DEFAULT_HIJRI_DATE,
29
+ HIJRI_MONTH_IDS: () => HIJRI_MONTH_IDS,
30
+ HIJRI_YEAR_MAX: () => HIJRI_YEAR_MAX,
31
+ HIJRI_YEAR_MIN: () => HIJRI_YEAR_MIN,
24
32
  addDocument2ImageBlock: () => addDocument2ImageBlock,
25
33
  addDocument2ListBlock: () => addDocument2ListBlock,
26
34
  addDocument2ListItem: () => addDocument2ListItem,
27
35
  addDocument2Reference: () => addDocument2Reference,
28
36
  addDocument2TableBlock: () => addDocument2TableBlock,
29
37
  addDocument2TextBlock: () => addDocument2TextBlock,
38
+ applyFloatMetaPatch: () => applyFloatMetaPatch,
30
39
  arabicXeLatexPreambleCmd: () => arabicXeLatexPreambleCmd,
31
40
  arabicXeLatexPreambleFont: () => arabicXeLatexPreambleFont,
32
41
  arabicXeLatexPreamblePkg: () => arabicXeLatexPreamblePkg,
33
42
  bibliographyEntryLatex: () => bibliographyEntryLatex,
43
+ butexBasmalaDisplayLatex: () => butexBasmalaDisplayLatex,
44
+ butexBasmalaDisplayTex: () => butexBasmalaDisplayTex,
45
+ butexBasmalaTitlingPreamble: () => butexBasmalaTitlingPreamble,
46
+ butexDiwaniDisplayLatex: () => butexDiwaniDisplayLatex,
47
+ butexDiwaniDisplayTex: () => butexDiwaniDisplayTex,
34
48
  citeTokenLatex: () => citeTokenLatex,
49
+ cloneDocument2Meta: () => cloneDocument2Meta,
35
50
  cloneDocument2Node: () => cloneDocument2Node,
51
+ collectDocument2Labels: () => collectDocument2Labels,
36
52
  createDocument2History: () => createDocument2History,
37
53
  createEmptyArabicEquationSession: () => createEmptyArabicEquationSession,
38
54
  createEmptyDocument2: () => createEmptyDocument2,
39
55
  createEmptyEquationSession: () => createEmptyEquationSession,
40
56
  createEmptyReference2: () => createEmptyReference2,
41
57
  createInlineField2: () => createInlineField2,
58
+ defaultFloatMeta: () => defaultFloatMeta,
42
59
  detectInlineSpans2: () => detectInlineSpans2,
43
60
  detectMathSpans2: () => detectMathSpans2,
61
+ document2HasAbstract: () => document2HasAbstract,
62
+ document2HasMaketitle: () => document2HasMaketitle,
63
+ document2HasTitleStack: () => document2HasTitleStack,
44
64
  document2HistoryCanRedo: () => document2HistoryCanRedo,
45
65
  document2HistoryCanUndo: () => document2HistoryCanUndo,
46
66
  document2Latex: () => document2Latex,
47
67
  document2Preview: () => document2Preview,
68
+ emptyBlockSelection: () => emptyBlockSelection,
69
+ emptyDocument2Meta: () => emptyDocument2Meta,
48
70
  ensureDocument2BibliographyBlock: () => ensureDocument2BibliographyBlock,
71
+ extendBlockSelectionFromAnchor: () => extendBlockSelectionFromAnchor,
72
+ fillDocument2MetaGaps: () => fillDocument2MetaGaps,
49
73
  findFirstInlineField: () => findFirstInlineField,
50
74
  formatBibliographyNumber: () => formatBibliographyNumber,
51
75
  formatCiteLabel: () => formatCiteLabel,
76
+ formatHijriDate: () => formatHijriDate,
77
+ formatRefLabel: () => formatRefLabel,
52
78
  fromDocumentJson2: () => fromDocumentJson2,
53
79
  getArabicXeLatexPreamble: () => getArabicXeLatexPreamble,
80
+ hijriMonthLabel: () => hijriMonthLabel,
54
81
  inlineFieldLatex: () => inlineFieldLatex,
55
82
  insertCiteTokenAtCaret: () => insertCiteTokenAtCaret,
56
83
  insertDocument2BlockAfter: () => insertDocument2BlockAfter,
57
84
  insertMathToken: () => insertMathToken,
58
85
  insertMathTokenAtCaret: () => insertMathTokenAtCaret,
86
+ insertRefTokenAtCaret: () => insertRefTokenAtCaret,
87
+ labelNumberMap: () => labelNumberMap,
59
88
  mathNodeFromArabicSession: () => mathNodeFromArabicSession,
60
89
  mathNodeFromSession: () => mathNodeFromSession,
61
90
  mathSourceFromArabicSession: () => mathSourceFromArabicSession,
@@ -63,25 +92,44 @@ __export(document2_exports, {
63
92
  mathTokenSourceForSide: () => mathTokenSourceForSide,
64
93
  mathTokenToArabicEditorSession: () => mathTokenToArabicEditorSession,
65
94
  mathTokenToEditorSession: () => mathTokenToEditorSession,
95
+ mergeDocument2Meta: () => mergeDocument2Meta,
66
96
  moveDocument2BlockById: () => moveDocument2BlockById,
97
+ moveDocument2BlockRange: () => moveDocument2BlockRange,
67
98
  moveDocument2Reference: () => moveDocument2Reference,
99
+ normalizeBlockSelection: () => normalizeBlockSelection,
100
+ normalizeDocument2HijriDate: () => normalizeDocument2HijriDate,
101
+ normalizeDocument2Meta: () => normalizeDocument2Meta,
68
102
  parseCiteKeys: () => parseCiteKeys,
103
+ parseFloatMetaFromJson: () => parseFloatMetaFromJson,
104
+ parseRefKeys: () => parseRefKeys,
69
105
  pushDocument2Snapshot: () => pushDocument2Snapshot,
106
+ refTokenLatex: () => refTokenLatex,
70
107
  referenceFromJson: () => referenceFromJson,
71
108
  referenceNumberMap: () => referenceNumberMap,
109
+ remapSelectionAfterRangeMove: () => remapSelectionAfterRangeMove,
72
110
  removeCiteTokenById: () => removeCiteTokenById,
73
111
  removeDocument2BlockById: () => removeDocument2BlockById,
112
+ removeDocument2BlockRange: () => removeDocument2BlockRange,
74
113
  removeDocument2ListItem: () => removeDocument2ListItem,
75
114
  removeDocument2Reference: () => removeDocument2Reference,
76
115
  removeMathTokenById: () => removeMathTokenById,
116
+ removeRefTokenById: () => removeRefTokenById,
77
117
  replaceMathTokenFromSession: () => replaceMathTokenFromSession,
78
118
  resolveCiteNumbers: () => resolveCiteNumbers,
79
119
  restoreDocument2Redo: () => restoreDocument2Redo,
80
120
  restoreDocument2Undo: () => restoreDocument2Undo,
81
121
  setDocument2References: () => setDocument2References,
122
+ stripDisplayMathDelimiters: () => stripDisplayMathDelimiters,
123
+ toggleBlockInSelection: () => toggleBlockInSelection,
82
124
  updateCiteTokenKeys: () => updateCiteTokenKeys,
125
+ updateDocument2ImageMeta: () => updateDocument2ImageMeta,
83
126
  updateDocument2ImageValue: () => updateDocument2ImageValue,
127
+ updateDocument2Meta: () => updateDocument2Meta,
84
128
  updateDocument2Reference: () => updateDocument2Reference,
129
+ updateDocument2TableMeta: () => updateDocument2TableMeta,
130
+ updateDocument2TextBlockCentered: () => updateDocument2TextBlockCentered,
131
+ updateMathTokenLabel: () => updateMathTokenLabel,
132
+ updateRefTokenKeys: () => updateRefTokenKeys,
85
133
  updateTextToken: () => updateTextToken
86
134
  });
87
135
  module.exports = __toCommonJS(document2_exports);
@@ -122,6 +170,203 @@ function formatDigits(value, digitForm = "western") {
122
170
  }).join("");
123
171
  }
124
172
 
173
+ // src/document2/articleMeta.ts
174
+ 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";
175
+ 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";
176
+ var BUTEX_BASMALA = `${BUTEX_BASMALA_LINE1}
177
+ ${BUTEX_BASMALA_SALAWAT}`;
178
+ 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";
179
+ function escapeButexTextArg(text) {
180
+ return text.replace(/\\/g, "\\textbackslash{}").replace(/[{}]/g, "\\$&");
181
+ }
182
+ function butexDiwaniDisplayTex(text) {
183
+ return `\\butexdiwani{${escapeButexTextArg(text)}}`;
184
+ }
185
+ function butexDiwaniDisplayLatex(text) {
186
+ return `\\[
187
+ ${butexDiwaniDisplayTex(text)}
188
+ \\]`;
189
+ }
190
+ function butexBasmalaDisplayTex() {
191
+ return `${butexDiwaniDisplayTex(BUTEX_BASMALA_LINE1)} \\\\ ${butexDiwaniDisplayTex(BUTEX_BASMALA_SALAWAT)}`;
192
+ }
193
+ function butexBasmalaDisplayLatex() {
194
+ return `\\[
195
+ ${butexBasmalaDisplayTex()}
196
+ \\]`;
197
+ }
198
+ function butexBasmalaTitlingPreamble() {
199
+ return [
200
+ "\\usepackage{titling}",
201
+ "\\pretitle{%",
202
+ " \\begin{center}",
203
+ ` ${butexDiwaniDisplayTex(BUTEX_BASMALA_LINE1)}\\\\`,
204
+ ` ${butexDiwaniDisplayTex(BUTEX_BASMALA_SALAWAT)}`,
205
+ " \\par\\vspace{1.5em}",
206
+ " \\LARGE",
207
+ "}",
208
+ "\\posttitle{\\par\\end{center}}"
209
+ ].join("\n");
210
+ }
211
+ var HIJRI_YEAR_MIN = 1400;
212
+ var HIJRI_YEAR_MAX = 1500;
213
+ var HIJRI_MONTH_IDS = [
214
+ "\u0645\u062D\u0631\u0645",
215
+ "\u0635\u0641\u0631",
216
+ "\u0631\u0628\u064A\u0639 \u0627\u0644\u0623\u0648\u0644",
217
+ "\u0631\u0628\u064A\u0639 \u0627\u0644\u0622\u062E\u0631",
218
+ "\u062C\u0645\u0627\u062F\u0649 \u0627\u0644\u0623\u0648\u0644\u0649",
219
+ "\u062C\u0645\u0627\u062F\u0649 \u0627\u0644\u0622\u062E\u0631\u0629",
220
+ "\u0631\u062C\u0628",
221
+ "\u0634\u0639\u0628\u0627\u0646",
222
+ "\u0631\u0645\u0636\u0627\u0646",
223
+ "\u0634\u0648\u0627\u0644",
224
+ "\u0630\u0648 \u0627\u0644\u0642\u0639\u062F\u0629",
225
+ "\u0630\u0648 \u0627\u0644\u062D\u062C\u0629"
226
+ ];
227
+ var HIJRI_MONTH_LABELS_EN = {
228
+ \u0645\u062D\u0631\u0645: "Muharram",
229
+ \u0635\u0641\u0631: "Safar",
230
+ "\u0631\u0628\u064A\u0639 \u0627\u0644\u0623\u0648\u0644": "Rabi\u02BB I",
231
+ "\u0631\u0628\u064A\u0639 \u0627\u0644\u0622\u062E\u0631": "Rabi\u02BB II",
232
+ "\u062C\u0645\u0627\u062F\u0649 \u0627\u0644\u0623\u0648\u0644\u0649": "Jumada I",
233
+ "\u062C\u0645\u0627\u062F\u0649 \u0627\u0644\u0622\u062E\u0631\u0629": "Jumada II",
234
+ \u0631\u062C\u0628: "Rajab",
235
+ \u0634\u0639\u0628\u0627\u0646: "Sha\u02BBban",
236
+ \u0631\u0645\u0636\u0627\u0646: "Ramadan",
237
+ \u0634\u0648\u0627\u0644: "Shawwal",
238
+ "\u0630\u0648 \u0627\u0644\u0642\u0639\u062F\u0629": "Dhul Qa\u02BBdah",
239
+ "\u0630\u0648 \u0627\u0644\u062D\u062C\u0629": "Dhul Hijjah"
240
+ };
241
+ var LEGACY_LATIN_MONTH = {
242
+ muharram: "\u0645\u062D\u0631\u0645",
243
+ safar: "\u0635\u0641\u0631",
244
+ rabiAwwal: "\u0631\u0628\u064A\u0639 \u0627\u0644\u0623\u0648\u0644",
245
+ rabiThani: "\u0631\u0628\u064A\u0639 \u0627\u0644\u0622\u062E\u0631",
246
+ jumadaAwwal: "\u062C\u0645\u0627\u062F\u0649 \u0627\u0644\u0623\u0648\u0644\u0649",
247
+ jumadaThani: "\u062C\u0645\u0627\u062F\u0649 \u0627\u0644\u0622\u062E\u0631\u0629",
248
+ rajab: "\u0631\u062C\u0628",
249
+ shaban: "\u0634\u0639\u0628\u0627\u0646",
250
+ ramadan: "\u0631\u0645\u0636\u0627\u0646",
251
+ shawwal: "\u0634\u0648\u0627\u0644",
252
+ dhulQadah: "\u0630\u0648 \u0627\u0644\u0642\u0639\u062F\u0629",
253
+ dhulHijjah: "\u0630\u0648 \u0627\u0644\u062D\u062C\u0629"
254
+ };
255
+ var DEFAULT_HIJRI_DATE = {
256
+ day: 1,
257
+ month: "\u0645\u062D\u0631\u0645",
258
+ year: 1448
259
+ };
260
+ function hijriMonthLabel(month, locale = "ar") {
261
+ return locale === "en" ? HIJRI_MONTH_LABELS_EN[month] : month;
262
+ }
263
+ function emptyDocument2Meta() {
264
+ return {
265
+ title: "",
266
+ authors: "",
267
+ date: { ...DEFAULT_HIJRI_DATE },
268
+ abstract: ""
269
+ };
270
+ }
271
+ function clampInt(value, min, max) {
272
+ if (!Number.isFinite(value)) {
273
+ return min;
274
+ }
275
+ return Math.min(max, Math.max(min, Math.trunc(value)));
276
+ }
277
+ function isHijriMonthId(value) {
278
+ return typeof value === "string" && HIJRI_MONTH_IDS.includes(value);
279
+ }
280
+ function resolveHijriMonthId(value) {
281
+ if (isHijriMonthId(value)) {
282
+ return value;
283
+ }
284
+ if (typeof value === "string" && value in LEGACY_LATIN_MONTH) {
285
+ return LEGACY_LATIN_MONTH[value];
286
+ }
287
+ return DEFAULT_HIJRI_DATE.month;
288
+ }
289
+ function normalizeDocument2HijriDate(value) {
290
+ if (typeof value !== "object" || value === null) {
291
+ return { ...DEFAULT_HIJRI_DATE };
292
+ }
293
+ const raw = value;
294
+ const day = clampInt(typeof raw.day === "number" ? raw.day : Number(raw.day), 1, 30);
295
+ const year = clampInt(typeof raw.year === "number" ? raw.year : Number(raw.year), HIJRI_YEAR_MIN, HIJRI_YEAR_MAX);
296
+ const month = resolveHijriMonthId(raw.month);
297
+ return { day, month, year };
298
+ }
299
+ function normalizeDocument2Meta(value) {
300
+ if (typeof value !== "object" || value === null) {
301
+ return emptyDocument2Meta();
302
+ }
303
+ const raw = value;
304
+ return {
305
+ title: typeof raw.title === "string" ? raw.title : "",
306
+ authors: typeof raw.authors === "string" ? raw.authors : "",
307
+ date: normalizeDocument2HijriDate(raw.date),
308
+ abstract: typeof raw.abstract === "string" ? raw.abstract : ""
309
+ };
310
+ }
311
+ function mergeDocument2Meta(fromJson, fromProp) {
312
+ const fromPropMeta = {
313
+ title: typeof fromProp?.title === "string" ? fromProp.title : "",
314
+ authors: typeof fromProp?.authors === "string" ? fromProp.authors : "",
315
+ abstract: typeof fromProp?.abstract === "string" ? fromProp.abstract : "",
316
+ date: fromProp?.date ? normalizeDocument2HijriDate({ ...DEFAULT_HIJRI_DATE, ...fromProp.date }) : { ...DEFAULT_HIJRI_DATE }
317
+ };
318
+ if (typeof fromJson !== "object" || fromJson === null) {
319
+ return fromPropMeta;
320
+ }
321
+ const json = fromJson;
322
+ return {
323
+ title: "title" in json && typeof json.title === "string" ? json.title : fromPropMeta.title,
324
+ authors: "authors" in json && typeof json.authors === "string" ? json.authors : fromPropMeta.authors,
325
+ abstract: "abstract" in json && typeof json.abstract === "string" ? json.abstract : fromPropMeta.abstract,
326
+ date: "date" in json ? normalizeDocument2HijriDate(json.date) : fromPropMeta.date
327
+ };
328
+ }
329
+ function fillDocument2MetaGaps(current, fromProp) {
330
+ if (!fromProp) {
331
+ return cloneDocument2Meta(current);
332
+ }
333
+ return {
334
+ title: current.title.trim().length > 0 ? current.title : typeof fromProp.title === "string" ? fromProp.title : current.title,
335
+ authors: current.authors.trim().length > 0 ? current.authors : typeof fromProp.authors === "string" ? fromProp.authors : current.authors,
336
+ abstract: current.abstract.trim().length > 0 ? current.abstract : typeof fromProp.abstract === "string" ? fromProp.abstract : current.abstract,
337
+ date: fromProp.date ? normalizeDocument2HijriDate({ ...current.date, ...fromProp.date }) : { ...current.date }
338
+ };
339
+ }
340
+ function document2HasMaketitle(meta) {
341
+ return meta.title.trim().length > 0 || meta.authors.trim().length > 0;
342
+ }
343
+ function document2HasAbstract(meta) {
344
+ return meta.abstract.trim().length > 0;
345
+ }
346
+ function document2HasTitleStack(meta) {
347
+ return document2HasMaketitle(meta) || document2HasAbstract(meta);
348
+ }
349
+ function formatHijriDate(date, options = "ar") {
350
+ const opts = typeof options === "string" ? { locale: options } : options;
351
+ const locale = opts.locale ?? "ar";
352
+ const digitForm = opts.digitForm ?? (locale === "en" ? "western" : "arabicIndic");
353
+ const month = hijriMonthLabel(date.month, locale);
354
+ const day = formatDigits(String(date.day), digitForm);
355
+ const year = formatDigits(String(date.year), digitForm);
356
+ if (locale === "en") {
357
+ return `${day} ${month} ${year} AH`;
358
+ }
359
+ return `${day} ${month} ${year}\u0647\u0640`;
360
+ }
361
+ function cloneDocument2Meta(meta) {
362
+ return {
363
+ title: meta.title,
364
+ authors: meta.authors,
365
+ date: { ...meta.date },
366
+ abstract: meta.abstract
367
+ };
368
+ }
369
+
125
370
  // src/document2/citations.ts
126
371
  function referenceNumberMap(references) {
127
372
  const map = /* @__PURE__ */ new Map();
@@ -557,6 +802,168 @@ ${lines.map((line) => ` ${line}`).join(" \\\\\n")}
557
802
  ${math.closing}`;
558
803
  }
559
804
 
805
+ // src/document2/labels.ts
806
+ function defaultFloatMeta() {
807
+ return {
808
+ centered: true,
809
+ captionEnabled: false,
810
+ caption: "",
811
+ labelEnabled: false,
812
+ label: ""
813
+ };
814
+ }
815
+ function parseFloatMetaFromJson(json) {
816
+ const caption = typeof json.caption === "string" ? json.caption : "";
817
+ const label = typeof json.label === "string" ? json.label : "";
818
+ return {
819
+ centered: json.centered !== false,
820
+ captionEnabled: json.caption_enabled === true || typeof json.caption_enabled !== "boolean" && caption.length > 0,
821
+ caption,
822
+ labelEnabled: json.label_enabled === true || typeof json.label_enabled !== "boolean" && label.trim().length > 0,
823
+ label
824
+ };
825
+ }
826
+ function parseRefKeys(source) {
827
+ const eqref = /^\\eqref\{([^}]*)\}$/.exec(source.trim());
828
+ if (eqref) {
829
+ return {
830
+ refCommand: "eqref",
831
+ keys: (eqref[1] ?? "").split(",").map((key) => key.trim()).filter((key) => key.length > 0)
832
+ };
833
+ }
834
+ const ref = /^\\ref\{([^}]*)\}$/.exec(source.trim());
835
+ if (ref) {
836
+ return {
837
+ refCommand: "ref",
838
+ keys: (ref[1] ?? "").split(",").map((key) => key.trim()).filter((key) => key.length > 0)
839
+ };
840
+ }
841
+ return { keys: [], refCommand: "ref" };
842
+ }
843
+ function refTokenLatex(keys, refCommand) {
844
+ const command = refCommand === "eqref" ? "\\eqref" : "\\ref";
845
+ return `${command}{${keys.join(",")}}`;
846
+ }
847
+ function collectDocument2Labels(document2) {
848
+ const entries = [];
849
+ const counters = { fig: 0, tab: 0, eq: 0 };
850
+ function pushEqFromField(tokens) {
851
+ for (const token of tokens) {
852
+ if (token.kind !== "math") {
853
+ continue;
854
+ }
855
+ if (token.display && token.labelEnabled && token.label && token.label.trim().length > 0) {
856
+ counters.eq += 1;
857
+ entries.push({
858
+ key: token.label.trim(),
859
+ kind: "eq",
860
+ caption: "",
861
+ ownerId: token.id,
862
+ number: counters.eq
863
+ });
864
+ }
865
+ }
866
+ }
867
+ function walk(blocks) {
868
+ for (const block of blocks) {
869
+ if (block.kind === "image" && block.labelEnabled && block.label.trim().length > 0) {
870
+ counters.fig += 1;
871
+ entries.push({
872
+ key: block.label.trim(),
873
+ kind: "fig",
874
+ caption: block.captionEnabled ? block.caption : "",
875
+ ownerId: block.id,
876
+ number: counters.fig
877
+ });
878
+ }
879
+ if (block.kind === "table") {
880
+ if (block.labelEnabled && block.label.trim().length > 0) {
881
+ counters.tab += 1;
882
+ entries.push({
883
+ key: block.label.trim(),
884
+ kind: "tab",
885
+ caption: block.captionEnabled ? block.caption : "",
886
+ ownerId: block.id,
887
+ number: counters.tab
888
+ });
889
+ }
890
+ for (const row of block.rows) {
891
+ for (const cell of row) {
892
+ pushEqFromField(cell.tokens);
893
+ }
894
+ }
895
+ }
896
+ if (block.kind === "textBlock") {
897
+ pushEqFromField(block.field.tokens);
898
+ }
899
+ if (block.kind === "list") {
900
+ for (const item of block.items) {
901
+ pushEqFromField(item.field.tokens);
902
+ walk(item.blocks);
903
+ }
904
+ }
905
+ }
906
+ }
907
+ walk(document2.blocks);
908
+ return entries;
909
+ }
910
+ function labelNumberMap(document2) {
911
+ const map = /* @__PURE__ */ new Map();
912
+ for (const entry of collectDocument2Labels(document2)) {
913
+ if (!map.has(entry.key)) {
914
+ map.set(entry.key, { kind: entry.kind, number: entry.number });
915
+ }
916
+ }
917
+ return map;
918
+ }
919
+ function formatRefLabel(keys, document2, refCommand, options = {}) {
920
+ const documentDirection = options.documentDirection ?? "rtl";
921
+ const digitForm = options.digitForm ?? (documentDirection === "rtl" ? "arabicIndic" : "western");
922
+ const map = labelNumberMap(document2);
923
+ const parts = keys.map((key) => {
924
+ const hit = map.get(key);
925
+ if (!hit) {
926
+ return "?";
927
+ }
928
+ return formatDigits(String(hit.number), digitForm);
929
+ });
930
+ const body = parts.join(documentDirection === "rtl" ? "\u060C" : ", ");
931
+ if (refCommand === "eqref") {
932
+ return `(${body})`;
933
+ }
934
+ return body;
935
+ }
936
+ function stripDisplayMathDelimiters(source) {
937
+ const trimmed = source.trim();
938
+ if (trimmed.startsWith("\\[") && trimmed.endsWith("\\]")) {
939
+ return trimmed.slice(2, -2).trim();
940
+ }
941
+ if (trimmed.startsWith("$$") && trimmed.endsWith("$$")) {
942
+ return trimmed.slice(2, -2).trim();
943
+ }
944
+ if (trimmed.startsWith("\\begin{equation}") && trimmed.endsWith("\\end{equation}")) {
945
+ return trimmed.slice("\\begin{equation}".length, -"\\end{equation}".length).trim();
946
+ }
947
+ return trimmed;
948
+ }
949
+ function applyFloatMetaPatch(block, patch) {
950
+ if (typeof patch.centered === "boolean") {
951
+ block.centered = patch.centered;
952
+ }
953
+ if (typeof patch.captionEnabled === "boolean") {
954
+ block.captionEnabled = patch.captionEnabled;
955
+ }
956
+ if (typeof patch.caption === "string") {
957
+ block.caption = patch.caption;
958
+ }
959
+ if (typeof patch.labelEnabled === "boolean") {
960
+ block.labelEnabled = patch.labelEnabled;
961
+ }
962
+ if (typeof patch.label === "string") {
963
+ block.label = patch.label;
964
+ }
965
+ }
966
+
560
967
  // src/document2/inlineScanner.ts
561
968
  var MATH_ENVIRONMENTS = /* @__PURE__ */ new Set([
562
969
  "equation",
@@ -632,6 +1039,43 @@ function citeSpan(value, start) {
632
1039
  }
633
1040
  return null;
634
1041
  }
1042
+ function refSpan(value, start) {
1043
+ const eqrefPrefix = "\\eqref{";
1044
+ const refPrefix = "\\ref{";
1045
+ let refCommand = "ref";
1046
+ let prefix = refPrefix;
1047
+ if (value.startsWith(eqrefPrefix, start) && !isEscaped(value, start)) {
1048
+ refCommand = "eqref";
1049
+ prefix = eqrefPrefix;
1050
+ } else if (value.startsWith(refPrefix, start) && !isEscaped(value, start)) {
1051
+ refCommand = "ref";
1052
+ prefix = refPrefix;
1053
+ } else {
1054
+ return null;
1055
+ }
1056
+ const openBrace = start + prefix.length - 1;
1057
+ if (value[openBrace] !== "{") {
1058
+ return null;
1059
+ }
1060
+ let depth = 0;
1061
+ for (let i = openBrace; i < value.length; i += 1) {
1062
+ const char = value[i];
1063
+ if (char === "{" && !isEscaped(value, i)) {
1064
+ depth += 1;
1065
+ continue;
1066
+ }
1067
+ if (char === "}" && !isEscaped(value, i)) {
1068
+ depth -= 1;
1069
+ if (depth === 0) {
1070
+ const end = i + 1;
1071
+ const source = value.slice(start, end);
1072
+ const parsed = parseRefKeys(source);
1073
+ return { kind: "ref", start, end, source, keys: parsed.keys, refCommand };
1074
+ }
1075
+ }
1076
+ }
1077
+ return null;
1078
+ }
635
1079
  function mathSpanAt(value, i) {
636
1080
  if (value.startsWith("\\begin{", i)) {
637
1081
  return environmentSpan(value, i);
@@ -679,6 +1123,12 @@ function detectInlineSpans2(value) {
679
1123
  i = cite.end;
680
1124
  continue;
681
1125
  }
1126
+ const ref = refSpan(value, i);
1127
+ if (ref) {
1128
+ spans.push(ref);
1129
+ i = ref.end;
1130
+ continue;
1131
+ }
682
1132
  const math = mathSpanAt(value, i);
683
1133
  if (math) {
684
1134
  spans.push({ kind: "math", ...math });
@@ -765,6 +1215,16 @@ function createInlineField2(value = "", mathObjects = [], options = {}, path = "
765
1215
  index = span.end;
766
1216
  continue;
767
1217
  }
1218
+ if (span.kind === "ref") {
1219
+ tokens.push({
1220
+ id: document2Id("ref"),
1221
+ kind: "ref",
1222
+ keys: span.keys.length > 0 ? span.keys : [],
1223
+ refCommand: span.refCommand
1224
+ });
1225
+ index = span.end;
1226
+ continue;
1227
+ }
768
1228
  const mathJson = mathObjects[mathObjectIndex];
769
1229
  mathObjectIndex += 1;
770
1230
  if (mathJson && (mathJson.math_mode !== span.opening || mathJson.closing !== span.closing)) {
@@ -813,7 +1273,8 @@ function parseTextBlock(json, options, path, diagnostics) {
813
1273
  id: document2Id("block"),
814
1274
  kind: "textBlock",
815
1275
  command: json.command,
816
- field: createInlineField2(value, json.math_objects ?? [], options, `${path}.value`, diagnostics)
1276
+ field: createInlineField2(value, json.math_objects ?? [], options, `${path}.value`, diagnostics),
1277
+ ...json.centered === true ? { centered: true } : {}
817
1278
  };
818
1279
  }
819
1280
  function parseListItem(json, options, path, diagnostics) {
@@ -866,7 +1327,8 @@ function parseTableBlock(json, options, path, diagnostics) {
866
1327
  command: "\\begin{tabular}",
867
1328
  closing: "\\end{tabular}",
868
1329
  columns: typeof json.columns === "string" ? json.columns : "",
869
- rows
1330
+ rows,
1331
+ ...parseFloatMetaFromJson(json)
870
1332
  };
871
1333
  }
872
1334
  function parseImageBlock(json) {
@@ -878,7 +1340,8 @@ function parseImageBlock(json) {
878
1340
  command: "\\includegraphics",
879
1341
  value,
880
1342
  ...assetId !== void 0 ? { assetId } : {},
881
- options: isRecordOfStrings(json.options) ? json.options : {}
1343
+ options: isRecordOfStrings(json.options) ? json.options : {},
1344
+ ...parseFloatMetaFromJson(json)
882
1345
  };
883
1346
  }
884
1347
  function parseRawBlock(json) {
@@ -933,13 +1396,20 @@ function fromDocumentJson2(json, options = {}) {
933
1396
  const diagnostics = [];
934
1397
  return {
935
1398
  nodeType: "DocumentObject",
1399
+ meta: normalizeDocument2Meta(json.meta),
936
1400
  references: parseReferences(json.references),
937
1401
  blocks: json.blocks.map((block, index) => parseBlock(asBlockJson(block), options, `$.blocks[${String(index)}]`, diagnostics)),
938
1402
  diagnostics
939
1403
  };
940
1404
  }
941
1405
  function createEmptyDocument2() {
942
- return { nodeType: "DocumentObject", references: [], blocks: [], diagnostics: [] };
1406
+ return {
1407
+ nodeType: "DocumentObject",
1408
+ meta: emptyDocument2Meta(),
1409
+ references: [],
1410
+ blocks: [],
1411
+ diagnostics: []
1412
+ };
943
1413
  }
944
1414
 
945
1415
  // src/editor/atomicCommands.ts
@@ -3294,6 +3764,7 @@ function cloneBlock(block) {
3294
3764
  function cloneDocument(document2) {
3295
3765
  return {
3296
3766
  nodeType: "DocumentObject",
3767
+ meta: cloneDocument2Meta(document2.meta ?? emptyDocument2Meta()),
3297
3768
  references: document2.references.map((reference) => ({ ...reference })),
3298
3769
  blocks: document2.blocks.map(cloneBlock),
3299
3770
  diagnostics: document2.diagnostics.map((diagnostic) => ({ ...diagnostic }))
@@ -3404,6 +3875,29 @@ function moveDocument2BlockById(document2, blockId, direction) {
3404
3875
  next.blocks.splice(targetIndex, 0, block);
3405
3876
  return next;
3406
3877
  }
3878
+ function moveDocument2BlockRange(document2, from, to, direction) {
3879
+ const count = document2.blocks.length;
3880
+ if (from < 0 || to < 0 || from >= count || to >= count || from > to) {
3881
+ return document2;
3882
+ }
3883
+ const insertAt = from + direction;
3884
+ if (insertAt < 0 || to + direction >= count) {
3885
+ return document2;
3886
+ }
3887
+ const next = cloneDocument(document2);
3888
+ const slice = next.blocks.splice(from, to - from + 1);
3889
+ next.blocks.splice(insertAt, 0, ...slice);
3890
+ return next;
3891
+ }
3892
+ function removeDocument2BlockRange(document2, from, to) {
3893
+ const count = document2.blocks.length;
3894
+ if (from < 0 || to < 0 || from >= count || to >= count || from > to) {
3895
+ return document2;
3896
+ }
3897
+ const next = cloneDocument(document2);
3898
+ next.blocks = [...next.blocks.slice(0, from), ...next.blocks.slice(to + 1)];
3899
+ return next;
3900
+ }
3407
3901
  function normalizeFieldTokens(tokens) {
3408
3902
  if (tokens.length === 0) {
3409
3903
  return [{ id: document2Id("text"), kind: "text", text: "" }];
@@ -3498,16 +3992,50 @@ function replaceMathTokenFromSession(document2, tokenId, session, opening, closi
3498
3992
  const current = field.tokens[index];
3499
3993
  const open = opening ?? current.opening;
3500
3994
  const close = closing ?? current.closing;
3501
- field.tokens[index] = mathTokenFromSession(session, open, close, side);
3502
- field.tokens[index].id = current.id;
3995
+ const replaced = mathTokenFromSession(session, open, close, side);
3996
+ replaced.id = current.id;
3997
+ if (current.labelEnabled !== void 0) {
3998
+ replaced.labelEnabled = current.labelEnabled;
3999
+ }
4000
+ if (current.label !== void 0) {
4001
+ replaced.label = current.label;
4002
+ }
4003
+ field.tokens[index] = replaced;
3503
4004
  return true;
3504
4005
  });
3505
4006
  return next;
3506
4007
  }
3507
4008
  function addDocument2TextBlock(document2, command = "\\paragraph", afterBlockId) {
3508
- const block = { id: document2Id("block"), kind: "textBlock", command, field: createInlineField2("") };
4009
+ const block = {
4010
+ id: document2Id("block"),
4011
+ kind: "textBlock",
4012
+ command,
4013
+ field: createInlineField2(""),
4014
+ ...command === "\\paragraph" ? { centered: false } : {}
4015
+ };
3509
4016
  return insertDocument2BlockAfter(document2, afterBlockId, block);
3510
4017
  }
4018
+ function updateDocument2TextBlockCentered(document2, blockId, centered) {
4019
+ const next = cloneDocument(document2);
4020
+ function visit(blocks) {
4021
+ for (const block of blocks) {
4022
+ if (block.id === blockId && block.kind === "textBlock" && block.command === "\\paragraph") {
4023
+ block.centered = centered;
4024
+ return true;
4025
+ }
4026
+ if (block.kind === "list") {
4027
+ for (const item of block.items) {
4028
+ if (visit(item.blocks)) {
4029
+ return true;
4030
+ }
4031
+ }
4032
+ }
4033
+ }
4034
+ return false;
4035
+ }
4036
+ visit(next.blocks);
4037
+ return next;
4038
+ }
3511
4039
  function removeDocument2BlockById(document2, blockId) {
3512
4040
  const next = cloneDocument(document2);
3513
4041
  next.blocks = next.blocks.filter((block) => block.id !== blockId);
@@ -3544,7 +4072,8 @@ function addDocument2TableBlock(document2, columns = "lll", rowCount = 3, colCou
3544
4072
  command: "\\begin{tabular}",
3545
4073
  closing: "\\end{tabular}",
3546
4074
  columns: columns || "l".repeat(cols),
3547
- rows: tableRows
4075
+ rows: tableRows,
4076
+ ...defaultFloatMeta()
3548
4077
  };
3549
4078
  return insertDocument2BlockAfter(document2, afterBlockId, block);
3550
4079
  }
@@ -3554,7 +4083,8 @@ function addDocument2ImageBlock(document2, src = "", afterBlockId) {
3554
4083
  kind: "image",
3555
4084
  command: "\\includegraphics",
3556
4085
  value: src,
3557
- options: {}
4086
+ options: { width: "0.8\\textwidth" },
4087
+ ...defaultFloatMeta()
3558
4088
  };
3559
4089
  return insertDocument2BlockAfter(document2, afterBlockId, block);
3560
4090
  }
@@ -3579,6 +4109,128 @@ function updateDocument2ImageValue(document2, blockId, value) {
3579
4109
  visit(next.blocks);
3580
4110
  return next;
3581
4111
  }
4112
+ function updateDocument2ImageMeta(document2, blockId, patch) {
4113
+ const next = cloneDocument(document2);
4114
+ function visit(blocks) {
4115
+ for (const block of blocks) {
4116
+ if (block.id === blockId && block.kind === "image") {
4117
+ applyFloatMetaPatch(block, patch);
4118
+ return true;
4119
+ }
4120
+ if (block.kind === "list") {
4121
+ for (const item of block.items) {
4122
+ if (visit(item.blocks)) {
4123
+ return true;
4124
+ }
4125
+ }
4126
+ }
4127
+ }
4128
+ return false;
4129
+ }
4130
+ visit(next.blocks);
4131
+ return next;
4132
+ }
4133
+ function updateDocument2TableMeta(document2, blockId, patch) {
4134
+ const next = cloneDocument(document2);
4135
+ function visit(blocks) {
4136
+ for (const block of blocks) {
4137
+ if (block.id === blockId && block.kind === "table") {
4138
+ applyFloatMetaPatch(block, patch);
4139
+ return true;
4140
+ }
4141
+ if (block.kind === "list") {
4142
+ for (const item of block.items) {
4143
+ if (visit(item.blocks)) {
4144
+ return true;
4145
+ }
4146
+ }
4147
+ }
4148
+ }
4149
+ return false;
4150
+ }
4151
+ visit(next.blocks);
4152
+ return next;
4153
+ }
4154
+ function updateMathTokenLabel(document2, tokenId, patch) {
4155
+ const next = cloneDocument(document2);
4156
+ visitFields(next.blocks, (field) => {
4157
+ const token = field.tokens.find((entry) => entry.id === tokenId && entry.kind === "math");
4158
+ if (!token) {
4159
+ return false;
4160
+ }
4161
+ if (typeof patch.labelEnabled === "boolean") {
4162
+ token.labelEnabled = patch.labelEnabled;
4163
+ }
4164
+ if (typeof patch.label === "string") {
4165
+ token.label = patch.label;
4166
+ }
4167
+ return true;
4168
+ });
4169
+ return next;
4170
+ }
4171
+ function refTokenFromKeys(keys, refCommand) {
4172
+ return { id: document2Id("ref"), kind: "ref", keys: [...keys], refCommand };
4173
+ }
4174
+ function insertRefTokenAtCaret(document2, fieldId, textTokenId, caretOffset, keys, refCommand = "ref") {
4175
+ if (keys.length === 0) {
4176
+ return document2;
4177
+ }
4178
+ const next = cloneDocument(document2);
4179
+ const refToken = refTokenFromKeys(keys, refCommand);
4180
+ visitFields(next.blocks, (field) => {
4181
+ if (field.id !== fieldId) {
4182
+ return false;
4183
+ }
4184
+ const textIndex = textTokenId ? field.tokens.findIndex((token) => token.id === textTokenId && token.kind === "text") : field.tokens.findIndex((token) => token.kind === "text");
4185
+ if (textIndex < 0) {
4186
+ field.tokens.push(refToken);
4187
+ field.tokens.push({ id: document2Id("text"), kind: "text", text: "" });
4188
+ return true;
4189
+ }
4190
+ const current = field.tokens[textIndex];
4191
+ const [before, after] = splitTextTokenAt(current, caretOffset);
4192
+ const parts = [...field.tokens.slice(0, textIndex)];
4193
+ if (before.text.length > 0) {
4194
+ parts.push(before);
4195
+ }
4196
+ parts.push(refToken);
4197
+ parts.push(after);
4198
+ parts.push(...field.tokens.slice(textIndex + 1));
4199
+ field.tokens = normalizeFieldTokens(parts);
4200
+ return true;
4201
+ });
4202
+ return next;
4203
+ }
4204
+ function updateRefTokenKeys(document2, tokenId, keys, refCommand) {
4205
+ if (keys.length === 0) {
4206
+ return document2;
4207
+ }
4208
+ const next = cloneDocument(document2);
4209
+ visitFields(next.blocks, (field) => {
4210
+ const token = field.tokens.find((entry) => entry.id === tokenId && entry.kind === "ref");
4211
+ if (!token) {
4212
+ return false;
4213
+ }
4214
+ token.keys = [...keys];
4215
+ if (refCommand) {
4216
+ token.refCommand = refCommand;
4217
+ }
4218
+ return true;
4219
+ });
4220
+ return next;
4221
+ }
4222
+ function removeRefTokenById(document2, tokenId) {
4223
+ const next = cloneDocument(document2);
4224
+ visitFields(next.blocks, (field) => {
4225
+ const index = field.tokens.findIndex((token) => token.id === tokenId && token.kind === "ref");
4226
+ if (index < 0) {
4227
+ return false;
4228
+ }
4229
+ field.tokens = normalizeFieldTokens(stitchTextAroundRemovedToken(field.tokens, index));
4230
+ return true;
4231
+ });
4232
+ return next;
4233
+ }
3582
4234
  function mutateListBlockById(blocks, listBlockId, mutator) {
3583
4235
  for (const block of blocks) {
3584
4236
  if (block.id === listBlockId && block.kind === "list") {
@@ -3742,12 +4394,24 @@ function setDocument2References(document2, references) {
3742
4394
  next.references = references.map((reference) => ({ ...reference }));
3743
4395
  return next;
3744
4396
  }
4397
+ function updateDocument2Meta(document2, patch) {
4398
+ const next = cloneDocument(document2);
4399
+ const current = next.meta ?? emptyDocument2Meta();
4400
+ next.meta = {
4401
+ title: typeof patch.title === "string" ? patch.title : current.title,
4402
+ authors: typeof patch.authors === "string" ? patch.authors : current.authors,
4403
+ abstract: typeof patch.abstract === "string" ? patch.abstract : current.abstract,
4404
+ date: patch.date ? normalizeDocument2HijriDate({ ...current.date, ...patch.date }) : { ...current.date }
4405
+ };
4406
+ return next;
4407
+ }
3745
4408
 
3746
4409
  // 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}
4410
+ function arabicXeLatexPreamblePkg(twocolumn = false) {
4411
+ const classOptions = twocolumn ? "12pt,a4paper,notitlepage,twocolumn" : "12pt,a4paper,notitlepage";
4412
+ return `
4413
+ \\documentclass[${classOptions}]{article}
4414
+ ` + String.raw`\usepackage{amsmath,amsfonts,amssymb,mathrsfs,tikz,fancyhdr, mathtools}
3751
4415
  \usepackage{fontspec} % For loading OpenType fonts
3752
4416
  \usepackage{unicode-math} % For setting the math font
3753
4417
  \usepackage{polyglossia} % For Arabic support
@@ -3937,11 +4601,21 @@ function arabicXeLatexPreambleCmd() {
3937
4601
  \newcommand{\arbinom}[2]{\left(\begin{array}{c} #1 \\ #2 \end{array} \right)}
3938
4602
  `;
3939
4603
  }
3940
- function getArabicXeLatexPreamble(digitsMapping = "arabicdigits") {
3941
- return arabicXeLatexPreamblePkg() + arabicXeLatexPreambleFont(digitsMapping) + arabicXeLatexPreambleCmd();
4604
+ function getArabicXeLatexPreamble(digitsMappingOrOptions = "arabicdigits") {
4605
+ const options = typeof digitsMappingOrOptions === "string" ? { digitsMapping: digitsMappingOrOptions } : digitsMappingOrOptions;
4606
+ const digitsMapping = options.digitsMapping ?? "arabicdigits";
4607
+ const twocolumn = options.twocolumn === true;
4608
+ return arabicXeLatexPreamblePkg(twocolumn) + arabicXeLatexPreambleFont(digitsMapping) + arabicXeLatexPreambleCmd();
3942
4609
  }
3943
4610
 
3944
4611
  // src/document2/exportLatex.ts
4612
+ function mathBodyLatex(token) {
4613
+ if (!token.math || token.sourceOwner === "raw" || token.sourceOwner === "editor") {
4614
+ return stripDisplayMathDelimiters(token.source);
4615
+ }
4616
+ const rendered = renderMathNodeLatex(token.math);
4617
+ return stripDisplayMathDelimiters(rendered);
4618
+ }
3945
4619
  function tokenLatex(token) {
3946
4620
  if (token.kind === "text") {
3947
4621
  return token.text;
@@ -3949,6 +4623,16 @@ function tokenLatex(token) {
3949
4623
  if (token.kind === "cite") {
3950
4624
  return citeTokenLatex(token.keys);
3951
4625
  }
4626
+ if (token.kind === "ref") {
4627
+ return refTokenLatex(token.keys, token.refCommand);
4628
+ }
4629
+ if (token.display && token.labelEnabled && token.label && token.label.trim().length > 0) {
4630
+ const body = mathBodyLatex(token);
4631
+ return `\\begin{equation}
4632
+ ${body}
4633
+ \\label{${token.label.trim()}}
4634
+ \\end{equation}`;
4635
+ }
3952
4636
  if (!token.math || token.sourceOwner === "raw" || token.sourceOwner === "editor") {
3953
4637
  return token.source;
3954
4638
  }
@@ -3958,7 +4642,13 @@ function inlineFieldLatex(field) {
3958
4642
  return field.tokens.map((token) => tokenLatex(token)).join("");
3959
4643
  }
3960
4644
  function textBlockLatex(block) {
3961
- return `${block.command}{${inlineFieldLatex(block.field)}}`;
4645
+ const body = `${block.command}{${inlineFieldLatex(block.field)}}`;
4646
+ if (block.command === "\\paragraph" && block.centered) {
4647
+ return `\\begin{center}
4648
+ ${body}
4649
+ \\end{center}`;
4650
+ }
4651
+ return body;
3962
4652
  }
3963
4653
  function indent(value) {
3964
4654
  return value.split("\n").map((line) => ` ${line}`).join("\n");
@@ -3972,12 +4662,32 @@ function listBlockLatex(block, references) {
3972
4662
  ${items}
3973
4663
  ${block.closing}`;
3974
4664
  }
3975
- function tableBlockLatex(block) {
4665
+ function tabularInnerLatex(block) {
3976
4666
  const rows = block.rows.map((row) => ` ${row.map((cell) => inlineFieldLatex(cell)).join(" & ")}`).join(" \\\\\n");
3977
4667
  return `${block.command}{${block.columns}}
3978
4668
  ${rows}
3979
4669
  ${block.closing}`;
3980
4670
  }
4671
+ function floatCaptionLabelLines(block) {
4672
+ const lines = [];
4673
+ if (block.captionEnabled && block.caption.trim().length > 0) {
4674
+ lines.push(` \\caption{${block.caption.trim()}}`);
4675
+ }
4676
+ if (block.labelEnabled && block.label.trim().length > 0) {
4677
+ lines.push(` \\label{${block.label.trim()}}`);
4678
+ }
4679
+ return lines;
4680
+ }
4681
+ function tableBlockLatex(block) {
4682
+ const lines = ["\\begin{table}[htbp]"];
4683
+ if (block.centered) {
4684
+ lines.push(" \\centering");
4685
+ }
4686
+ lines.push(...floatCaptionLabelLines(block));
4687
+ lines.push(indent(tabularInnerLatex(block)));
4688
+ lines.push("\\end{table}");
4689
+ return lines.join("\n");
4690
+ }
3981
4691
  function imageOptionsLatex(block) {
3982
4692
  const entries = Object.entries(block.options);
3983
4693
  if (entries.length === 0) {
@@ -3985,6 +4695,16 @@ function imageOptionsLatex(block) {
3985
4695
  }
3986
4696
  return `[${entries.map(([key, value]) => `${key}=${value}`).join(",")}]`;
3987
4697
  }
4698
+ function imageBlockLatex(block) {
4699
+ const lines = ["\\begin{figure}[htbp]"];
4700
+ if (block.centered) {
4701
+ lines.push(" \\centering");
4702
+ }
4703
+ lines.push(` ${block.command}${imageOptionsLatex(block)}{${block.value}}`);
4704
+ lines.push(...floatCaptionLabelLines(block));
4705
+ lines.push("\\end{figure}");
4706
+ return lines.join("\n");
4707
+ }
3988
4708
  function blockLatex(block, references) {
3989
4709
  if (block.kind === "textBlock") {
3990
4710
  return textBlockLatex(block);
@@ -3996,7 +4716,7 @@ function blockLatex(block, references) {
3996
4716
  return tableBlockLatex(block);
3997
4717
  }
3998
4718
  if (block.kind === "image") {
3999
- return `${block.command}${imageOptionsLatex(block)}{${block.value}}`;
4719
+ return imageBlockLatex(block);
4000
4720
  }
4001
4721
  if (block.kind === "bibliography") {
4002
4722
  const width = String(Math.max(references.length, 9));
@@ -4007,13 +4727,54 @@ ${block.closing}`;
4007
4727
  }
4008
4728
  return block.value;
4009
4729
  }
4730
+ function authorLatex(authors) {
4731
+ return authors.split(/\n+/).map((line) => line.trim()).filter((line) => line.length > 0).join(" \\\\ ");
4732
+ }
4733
+ function articleMaketitlePreamble(meta) {
4734
+ if (!document2HasMaketitle(meta)) {
4735
+ return "";
4736
+ }
4737
+ const parts = [butexBasmalaTitlingPreamble()];
4738
+ if (meta.title.trim().length > 0) {
4739
+ parts.push(`\\title{${meta.title.trim()}}`);
4740
+ }
4741
+ if (meta.authors.trim().length > 0) {
4742
+ parts.push(`\\author{${authorLatex(meta.authors)}}`);
4743
+ }
4744
+ parts.push(`\\date{${formatHijriDate(meta.date, { locale: "ar", digitForm: "arabicIndic" })}}`);
4745
+ return parts.join("\n");
4746
+ }
4747
+ function articleFrontMatterLatex(meta) {
4748
+ const parts = [];
4749
+ if (document2HasMaketitle(meta)) {
4750
+ parts.push("\\maketitle");
4751
+ } else {
4752
+ parts.push(butexBasmalaDisplayLatex());
4753
+ }
4754
+ if (document2HasAbstract(meta)) {
4755
+ parts.push(`\\begin{abstract}
4756
+ ${meta.abstract.trim()}
4757
+ \\end{abstract}`);
4758
+ }
4759
+ return parts;
4760
+ }
4010
4761
  function document2Latex(document2, options = {}) {
4011
- const body = document2.blocks.map((block) => blockLatex(block, document2.references)).join("\n\n");
4762
+ const meta = document2.meta ?? emptyDocument2Meta();
4763
+ const front = articleFrontMatterLatex(meta);
4764
+ const bodyBlocks = document2.blocks.map((block) => blockLatex(block, document2.references));
4765
+ const closing = butexDiwaniDisplayLatex(BUTEX_CLOSING_HAMDALA);
4766
+ const body = [...front, ...bodyBlocks, closing].filter((part) => part.length > 0).join("\n\n");
4012
4767
  const wrapDocument = options.wrapDocument !== false;
4013
4768
  if (!wrapDocument) {
4014
- return body;
4769
+ const metaPreamble = articleMaketitlePreamble(meta);
4770
+ return metaPreamble.length > 0 ? `${metaPreamble}
4771
+
4772
+ ${body}` : body;
4015
4773
  }
4016
- const preamble = getArabicXeLatexPreamble(options.digitsMapping ?? "arabicdigits");
4774
+ const preamble = getArabicXeLatexPreamble({
4775
+ digitsMapping: options.digitsMapping ?? "arabicdigits",
4776
+ twocolumn: options.twocolumn === true
4777
+ }) + "\n" + articleMaketitlePreamble(meta);
4017
4778
  return `${preamble}
4018
4779
  \\begin{document}
4019
4780
 
@@ -4056,6 +4817,67 @@ function document2HistoryCanRedo(stacks) {
4056
4817
  return stacks.future.length > 0;
4057
4818
  }
4058
4819
 
4820
+ // src/document2/blockSelection.ts
4821
+ function emptyBlockSelection() {
4822
+ return { selection: null, anchor: null };
4823
+ }
4824
+ function normalizeBlockSelection(selection, blockCount) {
4825
+ if (!selection) {
4826
+ return null;
4827
+ }
4828
+ const { from, to } = selection;
4829
+ if (blockCount <= 0 || from < 0 || to < 0 || from >= blockCount || to >= blockCount || from > to) {
4830
+ return null;
4831
+ }
4832
+ return { from, to };
4833
+ }
4834
+ function inRange(selection, index) {
4835
+ return index >= selection.from && index <= selection.to;
4836
+ }
4837
+ function toggleBlockInSelection(state, index, blockCount) {
4838
+ if (index < 0 || index >= blockCount) {
4839
+ return state;
4840
+ }
4841
+ const selection = normalizeBlockSelection(state.selection, blockCount);
4842
+ if (!selection) {
4843
+ return { selection: { from: index, to: index }, anchor: index };
4844
+ }
4845
+ if (inRange(selection, index)) {
4846
+ if (selection.from === selection.to) {
4847
+ return emptyBlockSelection();
4848
+ }
4849
+ if (index === selection.from) {
4850
+ return { selection: { from: selection.from + 1, to: selection.to }, anchor: state.anchor };
4851
+ }
4852
+ if (index === selection.to) {
4853
+ return { selection: { from: selection.from, to: selection.to - 1 }, anchor: state.anchor };
4854
+ }
4855
+ return emptyBlockSelection();
4856
+ }
4857
+ if (index === selection.from - 1) {
4858
+ return { selection: { from: index, to: selection.to }, anchor: state.anchor ?? index };
4859
+ }
4860
+ if (index === selection.to + 1) {
4861
+ return { selection: { from: selection.from, to: index }, anchor: state.anchor ?? index };
4862
+ }
4863
+ return { selection: { from: index, to: index }, anchor: index };
4864
+ }
4865
+ function extendBlockSelectionFromAnchor(state, index, blockCount) {
4866
+ if (index < 0 || index >= blockCount) {
4867
+ return state;
4868
+ }
4869
+ const anchor = state.anchor ?? index;
4870
+ const from = Math.min(anchor, index);
4871
+ const to = Math.max(anchor, index);
4872
+ return {
4873
+ selection: { from, to },
4874
+ anchor: state.anchor ?? index
4875
+ };
4876
+ }
4877
+ function remapSelectionAfterRangeMove(selection, direction) {
4878
+ return { from: selection.from + direction, to: selection.to + direction };
4879
+ }
4880
+
4059
4881
  // src/document2/previewModel.ts
4060
4882
  function mathTex(token, equationSide) {
4061
4883
  return mathTokenSourceForSide(token, equationSide);
@@ -4076,6 +4898,18 @@ function previewInlines(field, islands, output, equationSide, document2, preview
4076
4898
  })
4077
4899
  };
4078
4900
  }
4901
+ if (token.kind === "ref") {
4902
+ return {
4903
+ kind: "ref",
4904
+ id: token.id,
4905
+ keys: [...token.keys],
4906
+ refCommand: token.refCommand,
4907
+ label: formatRefLabel(token.keys, document2, token.refCommand, {
4908
+ documentDirection: previewOptions.documentDirection,
4909
+ digitForm: previewOptions.digitForm
4910
+ })
4911
+ };
4912
+ }
4079
4913
  const island = {
4080
4914
  id: token.id,
4081
4915
  tex: mathTex(token, equationSide),
@@ -4098,7 +4932,28 @@ function textBlockPreview(block, islands, output, equationSide, document2, previ
4098
4932
  if (block.command === "\\subsubsection") {
4099
4933
  return { kind: "heading", id: block.id, level: 3, inlines };
4100
4934
  }
4101
- return { kind: "paragraph", id: block.id, inlines };
4935
+ return {
4936
+ kind: "paragraph",
4937
+ id: block.id,
4938
+ inlines,
4939
+ ...block.centered ? { centered: true } : {}
4940
+ };
4941
+ }
4942
+ function floatNumberLabel(document2, key, previewOptions) {
4943
+ if (!key || key.trim().length === 0) {
4944
+ return void 0;
4945
+ }
4946
+ const hit = labelNumberMap(document2).get(key.trim());
4947
+ if (!hit || hit.kind !== "fig" && hit.kind !== "tab") {
4948
+ return void 0;
4949
+ }
4950
+ const locale = previewOptions.uiLocale ?? "ar";
4951
+ const kindWord = hit.kind === "fig" ? locale === "ar" ? "\u0627\u0644\u0634\u0643\u0644" : "Figure" : locale === "ar" ? "\u0627\u0644\u062C\u062F\u0648\u0644" : "Table";
4952
+ const digits = formatBibliographyNumber(hit.number, {
4953
+ documentDirection: previewOptions.documentDirection,
4954
+ digitForm: previewOptions.digitForm
4955
+ });
4956
+ return `${kindWord} ${digits}`;
4102
4957
  }
4103
4958
  function blockPreview(block, islands, output, equationSide, document2, previewOptions) {
4104
4959
  if (block.kind === "textBlock") {
@@ -4117,19 +4972,25 @@ function blockPreview(block, islands, output, equationSide, document2, previewOp
4117
4972
  };
4118
4973
  }
4119
4974
  if (block.kind === "table") {
4975
+ const label = block.labelEnabled ? block.label.trim() : "";
4120
4976
  return {
4121
4977
  kind: "table",
4122
4978
  id: block.id,
4123
- rows: block.rows.map((row) => row.map((cell) => previewInlines(cell, islands, output, equationSide, document2, previewOptions)))
4979
+ rows: block.rows.map((row) => row.map((cell) => previewInlines(cell, islands, output, equationSide, document2, previewOptions))),
4980
+ ...block.captionEnabled && block.caption.trim().length > 0 ? { caption: block.caption.trim() } : {},
4981
+ ...label.length > 0 ? { label, numberLabel: floatNumberLabel(document2, label, previewOptions) } : {}
4124
4982
  };
4125
4983
  }
4126
4984
  if (block.kind === "image") {
4985
+ const label = block.labelEnabled ? block.label.trim() : "";
4127
4986
  return {
4128
4987
  kind: "image",
4129
4988
  id: block.id,
4130
4989
  src: block.value,
4131
4990
  ...block.assetId !== void 0 ? { assetId: block.assetId } : {},
4132
- options: block.options
4991
+ options: block.options,
4992
+ ...block.captionEnabled && block.caption.trim().length > 0 ? { caption: block.caption.trim() } : {},
4993
+ ...label.length > 0 ? { label, numberLabel: floatNumberLabel(document2, label, previewOptions) } : {}
4133
4994
  };
4134
4995
  }
4135
4996
  if (block.kind === "bibliography") {
@@ -4153,55 +5014,112 @@ function blockPreview(block, islands, output, equationSide, document2, previewOp
4153
5014
  }
4154
5015
  return { kind: "omit", id: block.id };
4155
5016
  }
5017
+ function articleChromePreview(document2, uiLocale, previewOptions) {
5018
+ const meta = document2.meta ?? emptyDocument2Meta();
5019
+ const blocks = [
5020
+ { kind: "basmala", id: "article-basmala", text: BUTEX_BASMALA, tex: butexBasmalaDisplayTex() }
5021
+ ];
5022
+ if (document2HasMaketitle(meta)) {
5023
+ blocks.push({
5024
+ kind: "titleBlock",
5025
+ id: "article-title",
5026
+ ...meta.title.trim().length > 0 ? { title: meta.title.trim() } : {},
5027
+ ...meta.authors.trim().length > 0 ? { authors: meta.authors.trim() } : {},
5028
+ date: formatHijriDate(meta.date, {
5029
+ locale: uiLocale,
5030
+ digitForm: previewOptions.digitForm
5031
+ })
5032
+ });
5033
+ }
5034
+ if (document2HasAbstract(meta)) {
5035
+ blocks.push({ kind: "abstract", id: "article-abstract", text: meta.abstract.trim() });
5036
+ }
5037
+ return blocks;
5038
+ }
4156
5039
  function document2Preview(document2, output = "svg", equationSide = "arabic", previewOptions = {}) {
4157
5040
  const mathIslands = [];
4158
5041
  const options = {
4159
5042
  documentDirection: previewOptions.documentDirection ?? "rtl",
4160
- digitForm: previewOptions.digitForm ?? (previewOptions.documentDirection === "ltr" ? "western" : "arabicIndic")
5043
+ digitForm: previewOptions.digitForm ?? (previewOptions.documentDirection === "ltr" ? "western" : "arabicIndic"),
5044
+ uiLocale: previewOptions.uiLocale ?? "ar"
4161
5045
  };
5046
+ const body = document2.blocks.map((block) => blockPreview(block, mathIslands, output, equationSide, document2, options));
4162
5047
  return {
4163
- blocks: document2.blocks.map((block) => blockPreview(block, mathIslands, output, equationSide, document2, options)),
5048
+ blocks: [
5049
+ ...articleChromePreview(document2, options.uiLocale ?? "ar", options),
5050
+ ...body,
5051
+ { kind: "closing", id: "article-closing", text: BUTEX_CLOSING_HAMDALA, tex: butexDiwaniDisplayTex(BUTEX_CLOSING_HAMDALA) }
5052
+ ],
4164
5053
  mathIslands
4165
5054
  };
4166
5055
  }
4167
5056
  // Annotate the CommonJS export names for ESM import in node:
4168
5057
  0 && (module.exports = {
5058
+ BUTEX_BASMALA,
5059
+ BUTEX_BASMALA_LINE1,
5060
+ BUTEX_BASMALA_SALAWAT,
5061
+ BUTEX_CLOSING_HAMDALA,
4169
5062
  DEFAULT_DOCUMENT2_HISTORY_MAX_DEPTH,
5063
+ DEFAULT_HIJRI_DATE,
5064
+ HIJRI_MONTH_IDS,
5065
+ HIJRI_YEAR_MAX,
5066
+ HIJRI_YEAR_MIN,
4170
5067
  addDocument2ImageBlock,
4171
5068
  addDocument2ListBlock,
4172
5069
  addDocument2ListItem,
4173
5070
  addDocument2Reference,
4174
5071
  addDocument2TableBlock,
4175
5072
  addDocument2TextBlock,
5073
+ applyFloatMetaPatch,
4176
5074
  arabicXeLatexPreambleCmd,
4177
5075
  arabicXeLatexPreambleFont,
4178
5076
  arabicXeLatexPreamblePkg,
4179
5077
  bibliographyEntryLatex,
5078
+ butexBasmalaDisplayLatex,
5079
+ butexBasmalaDisplayTex,
5080
+ butexBasmalaTitlingPreamble,
5081
+ butexDiwaniDisplayLatex,
5082
+ butexDiwaniDisplayTex,
4180
5083
  citeTokenLatex,
5084
+ cloneDocument2Meta,
4181
5085
  cloneDocument2Node,
5086
+ collectDocument2Labels,
4182
5087
  createDocument2History,
4183
5088
  createEmptyArabicEquationSession,
4184
5089
  createEmptyDocument2,
4185
5090
  createEmptyEquationSession,
4186
5091
  createEmptyReference2,
4187
5092
  createInlineField2,
5093
+ defaultFloatMeta,
4188
5094
  detectInlineSpans2,
4189
5095
  detectMathSpans2,
5096
+ document2HasAbstract,
5097
+ document2HasMaketitle,
5098
+ document2HasTitleStack,
4190
5099
  document2HistoryCanRedo,
4191
5100
  document2HistoryCanUndo,
4192
5101
  document2Latex,
4193
5102
  document2Preview,
5103
+ emptyBlockSelection,
5104
+ emptyDocument2Meta,
4194
5105
  ensureDocument2BibliographyBlock,
5106
+ extendBlockSelectionFromAnchor,
5107
+ fillDocument2MetaGaps,
4195
5108
  findFirstInlineField,
4196
5109
  formatBibliographyNumber,
4197
5110
  formatCiteLabel,
5111
+ formatHijriDate,
5112
+ formatRefLabel,
4198
5113
  fromDocumentJson2,
4199
5114
  getArabicXeLatexPreamble,
5115
+ hijriMonthLabel,
4200
5116
  inlineFieldLatex,
4201
5117
  insertCiteTokenAtCaret,
4202
5118
  insertDocument2BlockAfter,
4203
5119
  insertMathToken,
4204
5120
  insertMathTokenAtCaret,
5121
+ insertRefTokenAtCaret,
5122
+ labelNumberMap,
4205
5123
  mathNodeFromArabicSession,
4206
5124
  mathNodeFromSession,
4207
5125
  mathSourceFromArabicSession,
@@ -4209,25 +5127,44 @@ function document2Preview(document2, output = "svg", equationSide = "arabic", pr
4209
5127
  mathTokenSourceForSide,
4210
5128
  mathTokenToArabicEditorSession,
4211
5129
  mathTokenToEditorSession,
5130
+ mergeDocument2Meta,
4212
5131
  moveDocument2BlockById,
5132
+ moveDocument2BlockRange,
4213
5133
  moveDocument2Reference,
5134
+ normalizeBlockSelection,
5135
+ normalizeDocument2HijriDate,
5136
+ normalizeDocument2Meta,
4214
5137
  parseCiteKeys,
5138
+ parseFloatMetaFromJson,
5139
+ parseRefKeys,
4215
5140
  pushDocument2Snapshot,
5141
+ refTokenLatex,
4216
5142
  referenceFromJson,
4217
5143
  referenceNumberMap,
5144
+ remapSelectionAfterRangeMove,
4218
5145
  removeCiteTokenById,
4219
5146
  removeDocument2BlockById,
5147
+ removeDocument2BlockRange,
4220
5148
  removeDocument2ListItem,
4221
5149
  removeDocument2Reference,
4222
5150
  removeMathTokenById,
5151
+ removeRefTokenById,
4223
5152
  replaceMathTokenFromSession,
4224
5153
  resolveCiteNumbers,
4225
5154
  restoreDocument2Redo,
4226
5155
  restoreDocument2Undo,
4227
5156
  setDocument2References,
5157
+ stripDisplayMathDelimiters,
5158
+ toggleBlockInSelection,
4228
5159
  updateCiteTokenKeys,
5160
+ updateDocument2ImageMeta,
4229
5161
  updateDocument2ImageValue,
5162
+ updateDocument2Meta,
4230
5163
  updateDocument2Reference,
5164
+ updateDocument2TableMeta,
5165
+ updateDocument2TextBlockCentered,
5166
+ updateMathTokenLabel,
5167
+ updateRefTokenKeys,
4231
5168
  updateTextToken
4232
5169
  });
4233
5170
  //# sourceMappingURL=document2.js.map