@drghaliasri/butex 4.4.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,29 +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,
33
+ addDocument2Reference: () => addDocument2Reference,
27
34
  addDocument2TableBlock: () => addDocument2TableBlock,
28
35
  addDocument2TextBlock: () => addDocument2TextBlock,
36
+ applyFloatMetaPatch: () => applyFloatMetaPatch,
37
+ arabicXeLatexPreambleCmd: () => arabicXeLatexPreambleCmd,
38
+ arabicXeLatexPreambleFont: () => arabicXeLatexPreambleFont,
39
+ arabicXeLatexPreamblePkg: () => arabicXeLatexPreamblePkg,
40
+ bibliographyEntryLatex: () => bibliographyEntryLatex,
41
+ butexDiwaniDisplayLatex: () => butexDiwaniDisplayLatex,
42
+ butexDiwaniDisplayTex: () => butexDiwaniDisplayTex,
43
+ citeTokenLatex: () => citeTokenLatex,
44
+ cloneDocument2Meta: () => cloneDocument2Meta,
29
45
  cloneDocument2Node: () => cloneDocument2Node,
46
+ collectDocument2Labels: () => collectDocument2Labels,
30
47
  createDocument2History: () => createDocument2History,
31
48
  createEmptyArabicEquationSession: () => createEmptyArabicEquationSession,
32
49
  createEmptyDocument2: () => createEmptyDocument2,
33
50
  createEmptyEquationSession: () => createEmptyEquationSession,
51
+ createEmptyReference2: () => createEmptyReference2,
34
52
  createInlineField2: () => createInlineField2,
53
+ defaultFloatMeta: () => defaultFloatMeta,
54
+ detectInlineSpans2: () => detectInlineSpans2,
35
55
  detectMathSpans2: () => detectMathSpans2,
56
+ document2HasAbstract: () => document2HasAbstract,
57
+ document2HasMaketitle: () => document2HasMaketitle,
58
+ document2HasTitleStack: () => document2HasTitleStack,
36
59
  document2HistoryCanRedo: () => document2HistoryCanRedo,
37
60
  document2HistoryCanUndo: () => document2HistoryCanUndo,
38
61
  document2Latex: () => document2Latex,
39
62
  document2Preview: () => document2Preview,
63
+ emptyBlockSelection: () => emptyBlockSelection,
64
+ emptyDocument2Meta: () => emptyDocument2Meta,
65
+ ensureDocument2BibliographyBlock: () => ensureDocument2BibliographyBlock,
66
+ extendBlockSelectionFromAnchor: () => extendBlockSelectionFromAnchor,
67
+ fillDocument2MetaGaps: () => fillDocument2MetaGaps,
40
68
  findFirstInlineField: () => findFirstInlineField,
69
+ formatBibliographyNumber: () => formatBibliographyNumber,
70
+ formatCiteLabel: () => formatCiteLabel,
71
+ formatHijriDate: () => formatHijriDate,
72
+ formatRefLabel: () => formatRefLabel,
41
73
  fromDocumentJson2: () => fromDocumentJson2,
74
+ getArabicXeLatexPreamble: () => getArabicXeLatexPreamble,
75
+ hijriMonthLabel: () => hijriMonthLabel,
42
76
  inlineFieldLatex: () => inlineFieldLatex,
77
+ insertCiteTokenAtCaret: () => insertCiteTokenAtCaret,
43
78
  insertDocument2BlockAfter: () => insertDocument2BlockAfter,
44
79
  insertMathToken: () => insertMathToken,
45
80
  insertMathTokenAtCaret: () => insertMathTokenAtCaret,
81
+ insertRefTokenAtCaret: () => insertRefTokenAtCaret,
82
+ labelNumberMap: () => labelNumberMap,
46
83
  mathNodeFromArabicSession: () => mathNodeFromArabicSession,
47
84
  mathNodeFromSession: () => mathNodeFromSession,
48
85
  mathSourceFromArabicSession: () => mathSourceFromArabicSession,
@@ -50,15 +87,44 @@ __export(document2_exports, {
50
87
  mathTokenSourceForSide: () => mathTokenSourceForSide,
51
88
  mathTokenToArabicEditorSession: () => mathTokenToArabicEditorSession,
52
89
  mathTokenToEditorSession: () => mathTokenToEditorSession,
90
+ mergeDocument2Meta: () => mergeDocument2Meta,
53
91
  moveDocument2BlockById: () => moveDocument2BlockById,
92
+ moveDocument2BlockRange: () => moveDocument2BlockRange,
93
+ moveDocument2Reference: () => moveDocument2Reference,
94
+ normalizeBlockSelection: () => normalizeBlockSelection,
95
+ normalizeDocument2HijriDate: () => normalizeDocument2HijriDate,
96
+ normalizeDocument2Meta: () => normalizeDocument2Meta,
97
+ parseCiteKeys: () => parseCiteKeys,
98
+ parseFloatMetaFromJson: () => parseFloatMetaFromJson,
99
+ parseRefKeys: () => parseRefKeys,
54
100
  pushDocument2Snapshot: () => pushDocument2Snapshot,
101
+ refTokenLatex: () => refTokenLatex,
102
+ referenceFromJson: () => referenceFromJson,
103
+ referenceNumberMap: () => referenceNumberMap,
104
+ remapSelectionAfterRangeMove: () => remapSelectionAfterRangeMove,
105
+ removeCiteTokenById: () => removeCiteTokenById,
55
106
  removeDocument2BlockById: () => removeDocument2BlockById,
107
+ removeDocument2BlockRange: () => removeDocument2BlockRange,
56
108
  removeDocument2ListItem: () => removeDocument2ListItem,
109
+ removeDocument2Reference: () => removeDocument2Reference,
57
110
  removeMathTokenById: () => removeMathTokenById,
111
+ removeRefTokenById: () => removeRefTokenById,
58
112
  replaceMathTokenFromSession: () => replaceMathTokenFromSession,
113
+ resolveCiteNumbers: () => resolveCiteNumbers,
59
114
  restoreDocument2Redo: () => restoreDocument2Redo,
60
115
  restoreDocument2Undo: () => restoreDocument2Undo,
116
+ setDocument2References: () => setDocument2References,
117
+ stripDisplayMathDelimiters: () => stripDisplayMathDelimiters,
118
+ toggleBlockInSelection: () => toggleBlockInSelection,
119
+ updateCiteTokenKeys: () => updateCiteTokenKeys,
120
+ updateDocument2ImageMeta: () => updateDocument2ImageMeta,
61
121
  updateDocument2ImageValue: () => updateDocument2ImageValue,
122
+ updateDocument2Meta: () => updateDocument2Meta,
123
+ updateDocument2Reference: () => updateDocument2Reference,
124
+ updateDocument2TableMeta: () => updateDocument2TableMeta,
125
+ updateDocument2TextBlockCentered: () => updateDocument2TextBlockCentered,
126
+ updateMathTokenLabel: () => updateMathTokenLabel,
127
+ updateRefTokenKeys: () => updateRefTokenKeys,
62
128
  updateTextToken: () => updateTextToken
63
129
  });
64
130
  module.exports = __toCommonJS(document2_exports);
@@ -71,6 +137,273 @@ function document2Id(prefix) {
71
137
  return id;
72
138
  }
73
139
 
140
+ // src/editor/digits.ts
141
+ var WESTERN = "0123456789";
142
+ var ARABIC_INDIC = "\u0660\u0661\u0662\u0663\u0664\u0665\u0666\u0667\u0668\u0669";
143
+ var PERSIAN_INDIC = "\u06F0\u06F1\u06F2\u06F3\u06F4\u06F5\u06F6\u06F7\u06F8\u06F9";
144
+ var digitSets = {
145
+ western: WESTERN,
146
+ arabicIndic: ARABIC_INDIC,
147
+ persianIndic: PERSIAN_INDIC
148
+ };
149
+ function digitValue(char) {
150
+ const western = WESTERN.indexOf(char);
151
+ if (western >= 0) {
152
+ return western;
153
+ }
154
+ const arabicIndic = ARABIC_INDIC.indexOf(char);
155
+ if (arabicIndic >= 0) {
156
+ return arabicIndic;
157
+ }
158
+ return PERSIAN_INDIC.indexOf(char);
159
+ }
160
+ function formatDigits(value, digitForm = "western") {
161
+ const target = digitSets[digitForm];
162
+ return Array.from(value, (char) => {
163
+ const value2 = digitValue(char);
164
+ return value2 >= 0 ? target[value2] : char;
165
+ }).join("");
166
+ }
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
+
341
+ // src/document2/citations.ts
342
+ function referenceNumberMap(references) {
343
+ const map = /* @__PURE__ */ new Map();
344
+ references.forEach((reference, index) => {
345
+ if (!map.has(reference.key)) {
346
+ map.set(reference.key, index + 1);
347
+ }
348
+ });
349
+ return map;
350
+ }
351
+ function resolveCiteNumbers(keys, references) {
352
+ const map = referenceNumberMap(references);
353
+ return keys.map((key) => map.get(key) ?? null);
354
+ }
355
+ function formatCiteLabel(keys, references, options = {}) {
356
+ const documentDirection = options.documentDirection ?? "rtl";
357
+ const digitForm = options.digitForm ?? (documentDirection === "rtl" ? "arabicIndic" : "western");
358
+ const numbers = resolveCiteNumbers(keys, references).map((value) => value === null ? "?" : String(value));
359
+ const display = documentDirection === "rtl" ? [...numbers].reverse() : numbers;
360
+ const separator = documentDirection === "rtl" ? "\u060C" : ", ";
361
+ const body = display.map((part) => formatDigits(part, digitForm)).join(separator);
362
+ return `[${body}]`;
363
+ }
364
+ function formatBibliographyNumber(index, options = {}) {
365
+ const documentDirection = options.documentDirection ?? "rtl";
366
+ const digitForm = options.digitForm ?? (documentDirection === "rtl" ? "arabicIndic" : "western");
367
+ return formatDigits(String(index), digitForm);
368
+ }
369
+ function parseCiteKeys(source) {
370
+ const match = /^\\cite\{([^}]*)\}$/.exec(source.trim());
371
+ if (!match) {
372
+ return [];
373
+ }
374
+ return (match[1] ?? "").split(",").map((key) => key.trim()).filter((key) => key.length > 0);
375
+ }
376
+ function citeTokenLatex(keys) {
377
+ return `\\cite{${keys.join(",")}}`;
378
+ }
379
+ function referenceFromJson(json) {
380
+ return {
381
+ id: document2Id("ref"),
382
+ key: json.key,
383
+ authors: typeof json.authors === "string" ? json.authors : "",
384
+ title: typeof json.title === "string" ? json.title : "",
385
+ year: typeof json.year === "string" ? json.year : "",
386
+ url: typeof json.url === "string" ? json.url : "",
387
+ venue: typeof json.venue === "string" ? json.venue : ""
388
+ };
389
+ }
390
+ function createEmptyReference2(partial = {}) {
391
+ return referenceFromJson({
392
+ key: partial.key ?? `ref${String(Date.now()).slice(-4)}`,
393
+ authors: partial.authors,
394
+ title: partial.title,
395
+ year: partial.year,
396
+ url: partial.url,
397
+ venue: partial.venue
398
+ });
399
+ }
400
+ function bibliographyEntryLatex(reference) {
401
+ const parts = [reference.authors, reference.title, reference.venue, reference.year].filter((part) => part.trim().length > 0);
402
+ const body = parts.join(", ");
403
+ const url = reference.url.trim().length > 0 ? ` \\url{${reference.url}}` : "";
404
+ return `\\bibitem{${reference.key}} ${body}${url}`.trim();
405
+ }
406
+
74
407
  // src/ast/commands/index.ts
75
408
  function renderCommandCore(context) {
76
409
  const optional = context.optionalArgs.map((arg) => `[${context.renderChain(arg)}]`).join("");
@@ -440,6 +773,168 @@ ${lines.map((line) => ` ${line}`).join(" \\\\\n")}
440
773
  ${math.closing}`;
441
774
  }
442
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
+
443
938
  // src/document2/inlineScanner.ts
444
939
  var MATH_ENVIRONMENTS = /* @__PURE__ */ new Set([
445
940
  "equation",
@@ -489,53 +984,127 @@ function environmentSpan(value, start) {
489
984
  const end = closingStart + closing.length;
490
985
  return { start, end, source: value.slice(start, end), opening, closing, display: true };
491
986
  }
492
- function detectMathSpans2(value) {
493
- const spans = [];
494
- let i = 0;
495
- while (i < value.length) {
496
- if (value.startsWith("\\begin{", i)) {
497
- const span = environmentSpan(value, i);
498
- if (span) {
499
- spans.push(span);
500
- i = span.end;
501
- continue;
502
- }
987
+ function citeSpan(value, start) {
988
+ if (!value.startsWith("\\cite{", start) || isEscaped(value, start)) {
989
+ return null;
990
+ }
991
+ const openBrace = start + "\\cite".length;
992
+ if (value[openBrace] !== "{") {
993
+ return null;
994
+ }
995
+ let depth = 0;
996
+ for (let i = openBrace; i < value.length; i += 1) {
997
+ const char = value[i];
998
+ if (char === "{" && !isEscaped(value, i)) {
999
+ depth += 1;
1000
+ continue;
503
1001
  }
504
- if (value.startsWith("\\(", i)) {
505
- const close = value.indexOf("\\)", i + 2);
506
- if (close >= 0) {
507
- const end = close + 2;
508
- spans.push({ start: i, end, source: value.slice(i, end), opening: "\\(", closing: "\\)", display: false });
509
- i = end;
510
- continue;
1002
+ if (char === "}" && !isEscaped(value, i)) {
1003
+ depth -= 1;
1004
+ if (depth === 0) {
1005
+ const end = i + 1;
1006
+ const source = value.slice(start, end);
1007
+ return { kind: "cite", start, end, source, keys: parseCiteKeys(source) };
511
1008
  }
512
1009
  }
513
- if (value.startsWith("\\[", i)) {
514
- const close = value.indexOf("\\]", i + 2);
515
- if (close >= 0) {
516
- const end = close + 2;
517
- spans.push({ start: i, end, source: value.slice(i, end), opening: "\\[", closing: "\\]", display: true });
518
- i = end;
519
- continue;
520
- }
1010
+ }
1011
+ return null;
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;
521
1037
  }
522
- if (value.startsWith("$$", i) && !isEscaped(value, i)) {
523
- const close = value.indexOf("$$", i + 2);
524
- if (close >= 0) {
525
- const end = close + 2;
526
- spans.push({ start: i, end, source: value.slice(i, end), opening: "$$", closing: "$$", display: true });
527
- i = end;
528
- continue;
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 };
529
1045
  }
530
1046
  }
531
- if (value[i] === "$" && !isEscaped(value, i)) {
532
- const close = findClosingDollar(value, i + 1);
533
- if (close >= 0) {
534
- const end = close + 1;
535
- spans.push({ start: i, end, source: value.slice(i, end), opening: "$", closing: "$", display: false });
536
- i = end;
537
- continue;
538
- }
1047
+ }
1048
+ return null;
1049
+ }
1050
+ function mathSpanAt(value, i) {
1051
+ if (value.startsWith("\\begin{", i)) {
1052
+ return environmentSpan(value, i);
1053
+ }
1054
+ if (value.startsWith("\\(", i)) {
1055
+ const close = value.indexOf("\\)", i + 2);
1056
+ if (close >= 0) {
1057
+ const end = close + 2;
1058
+ return { start: i, end, source: value.slice(i, end), opening: "\\(", closing: "\\)", display: false };
1059
+ }
1060
+ }
1061
+ if (value.startsWith("\\[", i)) {
1062
+ const close = value.indexOf("\\]", i + 2);
1063
+ if (close >= 0) {
1064
+ const end = close + 2;
1065
+ return { start: i, end, source: value.slice(i, end), opening: "\\[", closing: "\\]", display: true };
1066
+ }
1067
+ }
1068
+ if (value.startsWith("$$", i) && !isEscaped(value, i)) {
1069
+ const close = value.indexOf("$$", i + 2);
1070
+ if (close >= 0) {
1071
+ const end = close + 2;
1072
+ return { start: i, end, source: value.slice(i, end), opening: "$$", closing: "$$", display: true };
1073
+ }
1074
+ }
1075
+ if (value[i] === "$" && !isEscaped(value, i)) {
1076
+ const close = findClosingDollar(value, i + 1);
1077
+ if (close >= 0) {
1078
+ const end = close + 1;
1079
+ return { start: i, end, source: value.slice(i, end), opening: "$", closing: "$", display: false };
1080
+ }
1081
+ }
1082
+ return null;
1083
+ }
1084
+ function detectMathSpans2(value) {
1085
+ return detectInlineSpans2(value).filter((span) => span.kind === "math").map(({ kind: _kind, ...span }) => span);
1086
+ }
1087
+ function detectInlineSpans2(value) {
1088
+ const spans = [];
1089
+ let i = 0;
1090
+ while (i < value.length) {
1091
+ const cite = citeSpan(value, i);
1092
+ if (cite) {
1093
+ spans.push(cite);
1094
+ i = cite.end;
1095
+ continue;
1096
+ }
1097
+ const ref = refSpan(value, i);
1098
+ if (ref) {
1099
+ spans.push(ref);
1100
+ i = ref.end;
1101
+ continue;
1102
+ }
1103
+ const math = mathSpanAt(value, i);
1104
+ if (math) {
1105
+ spans.push({ kind: "math", ...math });
1106
+ i = math.end;
1107
+ continue;
539
1108
  }
540
1109
  i += 1;
541
1110
  }
@@ -572,19 +1141,63 @@ function pushDiagnostic(diagnostics, options, path, message) {
572
1141
  }
573
1142
  diagnostics.push({ code: "math_alignment", message, path });
574
1143
  }
1144
+ function parseReferences(json) {
1145
+ if (!Array.isArray(json)) {
1146
+ return [];
1147
+ }
1148
+ const references = [];
1149
+ for (const entry of json) {
1150
+ if (!isObject2(entry) || typeof entry.key !== "string" || entry.key.trim().length === 0) {
1151
+ continue;
1152
+ }
1153
+ references.push(
1154
+ referenceFromJson({
1155
+ key: entry.key.trim(),
1156
+ authors: typeof entry.authors === "string" ? entry.authors : void 0,
1157
+ title: typeof entry.title === "string" ? entry.title : void 0,
1158
+ year: typeof entry.year === "string" ? entry.year : void 0,
1159
+ url: typeof entry.url === "string" ? entry.url : void 0,
1160
+ venue: typeof entry.venue === "string" ? entry.venue : void 0
1161
+ })
1162
+ );
1163
+ }
1164
+ return references;
1165
+ }
575
1166
  function createInlineField2(value = "", mathObjects = [], options = {}, path = "$", diagnostics = []) {
576
1167
  const mode = options.mode ?? "english";
577
- const spans = detectMathSpans2(value);
1168
+ const spans = detectInlineSpans2(value);
1169
+ const mathSpans = spans.filter((span) => span.kind === "math");
578
1170
  const tokens = [];
579
1171
  let index = 0;
580
- if (mathObjects.length > 0 && mathObjects.length !== spans.length) {
581
- pushDiagnostic(diagnostics, options, path, `math_objects count mismatch: detected ${String(spans.length)}, got ${String(mathObjects.length)}`);
1172
+ let mathObjectIndex = 0;
1173
+ if (mathObjects.length > 0 && mathObjects.length !== mathSpans.length) {
1174
+ pushDiagnostic(diagnostics, options, path, `math_objects count mismatch: detected ${String(mathSpans.length)}, got ${String(mathObjects.length)}`);
582
1175
  }
583
- spans.forEach((span, spanIndex) => {
1176
+ for (const span of spans) {
584
1177
  if (span.start > index) {
585
1178
  tokens.push({ id: document2Id("text"), kind: "text", text: value.slice(index, span.start) });
586
1179
  }
587
- const mathJson = mathObjects[spanIndex];
1180
+ if (span.kind === "cite") {
1181
+ tokens.push({
1182
+ id: document2Id("cite"),
1183
+ kind: "cite",
1184
+ keys: span.keys.length > 0 ? span.keys : []
1185
+ });
1186
+ index = span.end;
1187
+ continue;
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
+ }
1199
+ const mathJson = mathObjects[mathObjectIndex];
1200
+ mathObjectIndex += 1;
588
1201
  if (mathJson && (mathJson.math_mode !== span.opening || mathJson.closing !== span.closing)) {
589
1202
  pushDiagnostic(diagnostics, options, path, "math_objects order mismatch");
590
1203
  }
@@ -616,7 +1229,7 @@ function createInlineField2(value = "", mathObjects = [], options = {}, path = "
616
1229
  });
617
1230
  }
618
1231
  index = span.end;
619
- });
1232
+ }
620
1233
  if (index < value.length || tokens.length === 0) {
621
1234
  tokens.push({ id: document2Id("text"), kind: "text", text: value.slice(index) });
622
1235
  }
@@ -631,7 +1244,8 @@ function parseTextBlock(json, options, path, diagnostics) {
631
1244
  id: document2Id("block"),
632
1245
  kind: "textBlock",
633
1246
  command: json.command,
634
- 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 } : {}
635
1249
  };
636
1250
  }
637
1251
  function parseListItem(json, options, path, diagnostics) {
@@ -684,16 +1298,21 @@ function parseTableBlock(json, options, path, diagnostics) {
684
1298
  command: "\\begin{tabular}",
685
1299
  closing: "\\end{tabular}",
686
1300
  columns: typeof json.columns === "string" ? json.columns : "",
687
- rows
1301
+ rows,
1302
+ ...parseFloatMetaFromJson(json)
688
1303
  };
689
1304
  }
690
1305
  function parseImageBlock(json) {
1306
+ const assetId = typeof json.asset_id === "string" && json.asset_id.length > 0 ? json.asset_id : void 0;
1307
+ const value = assetId !== void 0 ? typeof json.value === "string" ? json.value : "" : requireString(json.value, "\\includegraphics requires string value");
691
1308
  return {
692
1309
  id: document2Id("block"),
693
1310
  kind: "image",
694
1311
  command: "\\includegraphics",
695
- value: requireString(json.value, "\\includegraphics requires string value"),
696
- options: isRecordOfStrings(json.options) ? json.options : {}
1312
+ value,
1313
+ ...assetId !== void 0 ? { assetId } : {},
1314
+ options: isRecordOfStrings(json.options) ? json.options : {},
1315
+ ...parseFloatMetaFromJson(json)
697
1316
  };
698
1317
  }
699
1318
  function parseRawBlock(json) {
@@ -704,6 +1323,14 @@ function parseRawBlock(json) {
704
1323
  value: typeof json.value === "string" ? json.value : ""
705
1324
  };
706
1325
  }
1326
+ function parseBibliographyBlock() {
1327
+ return {
1328
+ id: document2Id("block"),
1329
+ kind: "bibliography",
1330
+ command: "\\begin{thebibliography}",
1331
+ closing: "\\end{thebibliography}"
1332
+ };
1333
+ }
707
1334
  function parseBlock(json, options, path, diagnostics) {
708
1335
  if (TEXT_COMMANDS.has(json.command)) {
709
1336
  return parseTextBlock(json, options, path, diagnostics);
@@ -717,6 +1344,9 @@ function parseBlock(json, options, path, diagnostics) {
717
1344
  if (json.command === "\\includegraphics") {
718
1345
  return parseImageBlock(json);
719
1346
  }
1347
+ if (json.command === "\\begin{thebibliography}" || json.command === "\\bibliography") {
1348
+ return parseBibliographyBlock();
1349
+ }
720
1350
  if (json.command === "\\raw") {
721
1351
  return parseRawBlock(json);
722
1352
  }
@@ -737,12 +1367,20 @@ function fromDocumentJson2(json, options = {}) {
737
1367
  const diagnostics = [];
738
1368
  return {
739
1369
  nodeType: "DocumentObject",
1370
+ meta: normalizeDocument2Meta(json.meta),
1371
+ references: parseReferences(json.references),
740
1372
  blocks: json.blocks.map((block, index) => parseBlock(asBlockJson(block), options, `$.blocks[${String(index)}]`, diagnostics)),
741
1373
  diagnostics
742
1374
  };
743
1375
  }
744
1376
  function createEmptyDocument2() {
745
- return { nodeType: "DocumentObject", blocks: [], diagnostics: [] };
1377
+ return {
1378
+ nodeType: "DocumentObject",
1379
+ meta: emptyDocument2Meta(),
1380
+ references: [],
1381
+ blocks: [],
1382
+ diagnostics: []
1383
+ };
746
1384
  }
747
1385
 
748
1386
  // src/editor/atomicCommands.ts
@@ -1765,34 +2403,6 @@ function renderDivideOperatorLatex(side) {
1765
2403
  return side === "arabic" ? "\\backslash" : DIVIDE_OPERATOR_EN;
1766
2404
  }
1767
2405
 
1768
- // src/editor/digits.ts
1769
- var WESTERN = "0123456789";
1770
- var ARABIC_INDIC = "\u0660\u0661\u0662\u0663\u0664\u0665\u0666\u0667\u0668\u0669";
1771
- var PERSIAN_INDIC = "\u06F0\u06F1\u06F2\u06F3\u06F4\u06F5\u06F6\u06F7\u06F8\u06F9";
1772
- var digitSets = {
1773
- western: WESTERN,
1774
- arabicIndic: ARABIC_INDIC,
1775
- persianIndic: PERSIAN_INDIC
1776
- };
1777
- function digitValue(char) {
1778
- const western = WESTERN.indexOf(char);
1779
- if (western >= 0) {
1780
- return western;
1781
- }
1782
- const arabicIndic = ARABIC_INDIC.indexOf(char);
1783
- if (arabicIndic >= 0) {
1784
- return arabicIndic;
1785
- }
1786
- return PERSIAN_INDIC.indexOf(char);
1787
- }
1788
- function formatDigits(value, digitForm = "western") {
1789
- const target = digitSets[digitForm];
1790
- return Array.from(value, (char) => {
1791
- const value2 = digitValue(char);
1792
- return value2 >= 0 ? target[value2] : char;
1793
- }).join("");
1794
- }
1795
-
1796
2406
  // src/editor/render.ts
1797
2407
  function selectedDigitForm(options) {
1798
2408
  return options?.digitForm ?? "western";
@@ -3125,6 +3735,8 @@ function cloneBlock(block) {
3125
3735
  function cloneDocument(document2) {
3126
3736
  return {
3127
3737
  nodeType: "DocumentObject",
3738
+ meta: cloneDocument2Meta(document2.meta ?? emptyDocument2Meta()),
3739
+ references: document2.references.map((reference) => ({ ...reference })),
3128
3740
  blocks: document2.blocks.map(cloneBlock),
3129
3741
  diagnostics: document2.diagnostics.map((diagnostic) => ({ ...diagnostic }))
3130
3742
  };
@@ -3234,6 +3846,29 @@ function moveDocument2BlockById(document2, blockId, direction) {
3234
3846
  next.blocks.splice(targetIndex, 0, block);
3235
3847
  return next;
3236
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
+ }
3237
3872
  function normalizeFieldTokens(tokens) {
3238
3873
  if (tokens.length === 0) {
3239
3874
  return [{ id: document2Id("text"), kind: "text", text: "" }];
@@ -3328,16 +3963,50 @@ function replaceMathTokenFromSession(document2, tokenId, session, opening, closi
3328
3963
  const current = field.tokens[index];
3329
3964
  const open = opening ?? current.opening;
3330
3965
  const close = closing ?? current.closing;
3331
- field.tokens[index] = mathTokenFromSession(session, open, close, side);
3332
- field.tokens[index].id = current.id;
3333
- return true;
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;
3975
+ return true;
3334
3976
  });
3335
3977
  return next;
3336
3978
  }
3337
3979
  function addDocument2TextBlock(document2, command = "\\paragraph", afterBlockId) {
3338
- 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
+ };
3339
3987
  return insertDocument2BlockAfter(document2, afterBlockId, block);
3340
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
+ }
3341
4010
  function removeDocument2BlockById(document2, blockId) {
3342
4011
  const next = cloneDocument(document2);
3343
4012
  next.blocks = next.blocks.filter((block) => block.id !== blockId);
@@ -3374,7 +4043,8 @@ function addDocument2TableBlock(document2, columns = "lll", rowCount = 3, colCou
3374
4043
  command: "\\begin{tabular}",
3375
4044
  closing: "\\end{tabular}",
3376
4045
  columns: columns || "l".repeat(cols),
3377
- rows: tableRows
4046
+ rows: tableRows,
4047
+ ...defaultFloatMeta()
3378
4048
  };
3379
4049
  return insertDocument2BlockAfter(document2, afterBlockId, block);
3380
4050
  }
@@ -3384,7 +4054,8 @@ function addDocument2ImageBlock(document2, src = "", afterBlockId) {
3384
4054
  kind: "image",
3385
4055
  command: "\\includegraphics",
3386
4056
  value: src,
3387
- options: {}
4057
+ options: { width: "0.8\\textwidth" },
4058
+ ...defaultFloatMeta()
3388
4059
  };
3389
4060
  return insertDocument2BlockAfter(document2, afterBlockId, block);
3390
4061
  }
@@ -3409,6 +4080,128 @@ function updateDocument2ImageValue(document2, blockId, value) {
3409
4080
  visit(next.blocks);
3410
4081
  return next;
3411
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
+ }
3412
4205
  function mutateListBlockById(blocks, listBlockId, mutator) {
3413
4206
  for (const block of blocks) {
3414
4207
  if (block.id === listBlockId && block.kind === "list") {
@@ -3445,12 +4238,372 @@ function removeDocument2ListItem(document2, listBlockId, itemId) {
3445
4238
  });
3446
4239
  return next;
3447
4240
  }
4241
+ function citeTokenFromKeys(keys) {
4242
+ return { id: document2Id("cite"), kind: "cite", keys: [...keys] };
4243
+ }
4244
+ function insertCiteTokenAtCaret(document2, fieldId, textTokenId, caretOffset, keys) {
4245
+ if (keys.length === 0) {
4246
+ return document2;
4247
+ }
4248
+ const next = cloneDocument(document2);
4249
+ const citeToken = citeTokenFromKeys(keys);
4250
+ visitFields(next.blocks, (field) => {
4251
+ if (field.id !== fieldId) {
4252
+ return false;
4253
+ }
4254
+ const textIndex = textTokenId ? field.tokens.findIndex((token) => token.id === textTokenId && token.kind === "text") : field.tokens.findIndex((token) => token.kind === "text");
4255
+ if (textIndex < 0) {
4256
+ field.tokens.push(citeToken);
4257
+ field.tokens.push({ id: document2Id("text"), kind: "text", text: "" });
4258
+ return true;
4259
+ }
4260
+ const current = field.tokens[textIndex];
4261
+ const [before, after] = splitTextTokenAt(current, caretOffset);
4262
+ const parts = [...field.tokens.slice(0, textIndex)];
4263
+ if (before.text.length > 0) {
4264
+ parts.push(before);
4265
+ }
4266
+ parts.push(citeToken);
4267
+ parts.push(after);
4268
+ parts.push(...field.tokens.slice(textIndex + 1));
4269
+ field.tokens = normalizeFieldTokens(parts);
4270
+ return true;
4271
+ });
4272
+ return next;
4273
+ }
4274
+ function updateCiteTokenKeys(document2, tokenId, keys) {
4275
+ if (keys.length === 0) {
4276
+ return document2;
4277
+ }
4278
+ const next = cloneDocument(document2);
4279
+ visitFields(next.blocks, (field) => {
4280
+ const token = field.tokens.find((entry) => entry.id === tokenId && entry.kind === "cite");
4281
+ if (!token) {
4282
+ return false;
4283
+ }
4284
+ token.keys = [...keys];
4285
+ return true;
4286
+ });
4287
+ return next;
4288
+ }
4289
+ function removeCiteTokenById(document2, tokenId) {
4290
+ const next = cloneDocument(document2);
4291
+ visitFields(next.blocks, (field) => {
4292
+ const index = field.tokens.findIndex((token) => token.id === tokenId && token.kind === "cite");
4293
+ if (index < 0) {
4294
+ return false;
4295
+ }
4296
+ field.tokens = normalizeFieldTokens(stitchTextAroundRemovedToken(field.tokens, index));
4297
+ return true;
4298
+ });
4299
+ return next;
4300
+ }
4301
+ function ensureDocument2BibliographyBlock(document2, afterBlockId) {
4302
+ if (document2.blocks.some((block2) => block2.kind === "bibliography")) {
4303
+ return document2;
4304
+ }
4305
+ const block = {
4306
+ id: document2Id("block"),
4307
+ kind: "bibliography",
4308
+ command: "\\begin{thebibliography}",
4309
+ closing: "\\end{thebibliography}"
4310
+ };
4311
+ return insertDocument2BlockAfter(document2, afterBlockId ?? null, block);
4312
+ }
4313
+ function addDocument2Reference(document2, partial = {}) {
4314
+ const next = cloneDocument(document2);
4315
+ next.references.push(createEmptyReference2(partial));
4316
+ return next;
4317
+ }
4318
+ function updateDocument2Reference(document2, referenceId, patch) {
4319
+ const next = cloneDocument(document2);
4320
+ const reference = next.references.find((entry) => entry.id === referenceId);
4321
+ if (!reference) {
4322
+ return document2;
4323
+ }
4324
+ if (typeof patch.key === "string" && patch.key.trim().length > 0) {
4325
+ reference.key = patch.key.trim();
4326
+ }
4327
+ if (typeof patch.authors === "string") {
4328
+ reference.authors = patch.authors;
4329
+ }
4330
+ if (typeof patch.title === "string") {
4331
+ reference.title = patch.title;
4332
+ }
4333
+ if (typeof patch.year === "string") {
4334
+ reference.year = patch.year;
4335
+ }
4336
+ if (typeof patch.url === "string") {
4337
+ reference.url = patch.url;
4338
+ }
4339
+ if (typeof patch.venue === "string") {
4340
+ reference.venue = patch.venue;
4341
+ }
4342
+ return next;
4343
+ }
4344
+ function removeDocument2Reference(document2, referenceId) {
4345
+ const next = cloneDocument(document2);
4346
+ next.references = next.references.filter((reference) => reference.id !== referenceId);
4347
+ return next;
4348
+ }
4349
+ function moveDocument2Reference(document2, referenceId, direction) {
4350
+ const next = cloneDocument(document2);
4351
+ const index = next.references.findIndex((reference2) => reference2.id === referenceId);
4352
+ const targetIndex = index + direction;
4353
+ if (index < 0 || targetIndex < 0 || targetIndex >= next.references.length) {
4354
+ return document2;
4355
+ }
4356
+ const [reference] = next.references.splice(index, 1);
4357
+ if (!reference) {
4358
+ return document2;
4359
+ }
4360
+ next.references.splice(targetIndex, 0, reference);
4361
+ return next;
4362
+ }
4363
+ function setDocument2References(document2, references) {
4364
+ const next = cloneDocument(document2);
4365
+ next.references = references.map((reference) => ({ ...reference }));
4366
+ return next;
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
+ }
4379
+
4380
+ // src/document2/arabicPreamble.ts
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}
4386
+ \usepackage{fontspec} % For loading OpenType fonts
4387
+ \usepackage{unicode-math} % For setting the math font
4388
+ \usepackage{polyglossia} % For Arabic support
4389
+ \usepackage{array}
4390
+ \usepackage{cancel}
4391
+ \usepackage{bidi} % For bidirectional text handling
4392
+ \usepackage{multirow}
4393
+ \usepackage{booktabs}
4394
+ \usepackage{graphicx} % For \reflectbox
4395
+ \usepackage{xcolor}
4396
+ \usepackage{tikz}
4397
+ \usepackage{tcolorbox} % For tcolorbox environment
4398
+ \usepackage{textcomp} % For \textrightarrow command
4399
+
4400
+
4401
+ % visit : https://www.symbolcopy.com/punctuation-symbol.html
4402
+ % for inversed punctuation
4403
+
4404
+ % save inverted comma for use - copy paste -> ،
4405
+ % save back tick for use - copy paste -> ` + "`";
4406
+ }
4407
+ function arabicXeLatexPreambleFont(digitsMapping = "arabicdigits") {
4408
+ return `
4409
+ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4410
+ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4411
+ % Toggle between eastern and western digits by changing the Mapping in the below fonts (inside math font as well) between "arabicdigits" and "digits"
4412
+ \\setdefaultlanguage[calendar=gregorian]{arabic} % from polyglossia
4413
+ \\setmainfont[Script=Arabic , Mapping=${digitsMapping}]{Amiri}
4414
+ \\newfontfamily\\diwani[Script=Arabic,Mapping=${digitsMapping}]{Diwani Letter}
4415
+ \\newfontfamily\\diwanioutlineshaded[Script=Arabic,Mapping=${digitsMapping}]{Diwani Outline Shaded}
4416
+ \\newfontfamily\\takween[Script=Arabic,Mapping=${digitsMapping}]{Takween}
4417
+ \\newfontfamily\\boldarabic[Script=Arabic,Mapping=${digitsMapping}]{Amiri Bold}
4418
+ \\newfontfamily\\italicarabic[Script=Arabic,Mapping=${digitsMapping}]{Amiri Italic}
4419
+
4420
+ % Explicitly set math font to XITS Math
4421
+ % Remove "Script=Arabic" to cancel reflected symbols and other RTL symbols
4422
+ \\setmathfont[Script=Arabic , Mapping=${digitsMapping}]{XITS Math} % Ensure XITS Math is correctly installed and available
4423
+ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4424
+ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4425
+ `;
4426
+ }
4427
+ function arabicXeLatexPreambleCmd() {
4428
+ return String.raw`
4429
+ % Run with XeLaTeX compiler
4430
+ \newcommand{\butextakween}[1]{\text{\takween{#1}}}
4431
+ \newcommand{\butexdiwani}[1]{\text{\diwani{#1}}}
4432
+ \newcommand{\butexdiwanioutline}[1]{\text{\diwanioutlineshaded{#1}}}
4433
+
4434
+ \newcommand{\arabN}{\text{\diwanioutlineshaded{ط}}}
4435
+ \newcommand{\arabZ}{\text{\diwanioutlineshaded{ص}}}
4436
+ \newcommand{\arabQ}{\text{\diwanioutlineshaded{ن}}}
4437
+ \newcommand{\arabR}{\text{\diwanioutlineshaded{ح}}}
4438
+ \newcommand{\arabC}{\text{\diwanioutlineshaded{ع}}}
4439
+ \newcommand{\arabH}{\text{\diwanioutlineshaded{ر}}}
4440
+
4441
+
4442
+ \newcommand{\lowerscript}[1]{\raisebox{-4pt}{\scriptsize #1}}
4443
+ \newcommand{\llowerscript}[1]{\raisebox{-8pt}{\scriptsize #1}}
4444
+ \newcommand{\lllowerscript}[2]{\raisebox{#2pt}{\scriptsize #1}}
4445
+
4446
+ \newcommand{\arabdsub}[3]{\prescript{}{#1}{\prescript{}{#2}{#3}}} % arabic double subscript
4447
+ \newcommand{\arabsub}[2]{\prescript{}{#1}{#2}} % arabic single subscript
4448
+
4449
+
4450
+
4451
+ \newcommand{\upperscript}[1]{\raisebox{8pt}{\scriptsize #1}}
4452
+ \newcommand{\uupperscript}[2]{\raisebox{#2pt}{\scriptsize #1}}
4453
+ \newcommand{\vertbar}{\rule[-1ex]{0.5pt}{2.5ex}}
4454
+ \newcommand{\horzbar}{\rule[.5ex]{2.5ex}{0.5pt}}
4455
+
4456
+ \newcommand{\arabsqrt}[2]{\reflectbox{\(\sqrt[\reflectbox{\(#1\)}]{\reflectbox{\(#2\)}}\)}}
4457
+ \newcommand{\arabvec}[1]{\reflectbox{$\vec{\reflectbox{$#1$}}$}}
4458
+
4459
+ \newcommand{\arabexp}[1]{{}^{#1}\!\raisebox{-4.5pt}{\text{\diwani{ه}}}}
4460
+ \newcommand{\arablog}[2]{\left(\text{#2}\right)\!\prescript{}{\text{#1}}{\text{\diwani{لو}}}}
4461
+ \newcommand{\arabnlog}[1]{\left(\text{#1}\right)\!\prescript{}{\text{\diwani{ه}}}{\text{\diwani{لو}}}}
4462
+
4463
+ \newcommand{\arabcos}[1]{\left(#1\right)\!\!\raisebox{-2.5pt}{\text{\diwani{جتا}}}}
4464
+ \newcommand{\arabsin}[1]{\left(#1\right)\!\!\raisebox{-2.5pt}{\text{\diwani{جا}}}}
4465
+ \newcommand{\arabtan}[1]{\left(#1\right)\!\!\raisebox{-2.5pt}{\text{\diwani{ظا}}}}
4466
+ \newcommand{\arabcot}[1]{\left(#1\right)\!\!\raisebox{-2.5pt}{\text{\diwani{ظتا}}}}
4467
+ \newcommand{\arabsec}[1]{\left(#1\right)\!\!\raisebox{-2.5pt}{\text{\diwani{قا}}}}
4468
+ \newcommand{\arabcsc}[1]{\left(#1\right)\!\!\raisebox{-2.5pt}{\text{\diwani{قتا}}}}
4469
+
4470
+ \newcommand{\arabacos}[1]{\left(#1\right)\!\!\raisebox{-2.5pt}{\text{\diwani{قجتا}}}}
4471
+ \newcommand{\arabasin}[1]{\left(#1\right)\!\!\raisebox{-2.5pt}{\text{\diwani{قجا}}}}
4472
+ \newcommand{\arabatan}[1]{\left(#1\right)\!\!\raisebox{-2.5pt}{\text{\diwani{قظا}}}}
4473
+ \newcommand{\arabacot}[1]{\left(#1\right)\!\!\raisebox{-2.5pt}{\text{\diwani{قظتا}}}}
4474
+ \newcommand{\arabasec}[1]{\left(#1\right)\!\!\raisebox{-2.5pt}{\text{\diwani{ققا}}}}
4475
+ \newcommand{\arabacsc}[1]{\left(#1\right)\!\!\raisebox{-2.5pt}{\text{\diwani{ققتا}}}}
4476
+
4477
+
4478
+ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4479
+ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4480
+ % summation
4481
+ \newcommand{\arablim}[2]{\underset{#2 \leftarrow #1}{\text{نـــــها}}}
4482
+ \newcommand{\arabsum}[2]{\underset{#1}{\overset{#2}{\text{مجـــ}}}}
4483
+ \newcommand{\arabprod}[2]{\underset{#1}{\overset{#2}{\text{جـــذ}}}}
4484
+ \newcommand{\arabint}[2]{\prescript{#2}{#1\!\!\!}\int}
4485
+
4486
+
4487
+ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4488
+ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4489
+ % we define this command to make it ease for change of notation later
4490
+ \newcommand{\ad}[0]{\text{ء}} % "ad" for arabic differentiation or derivative (ء" لإشتقاق")
4491
+ \newcommand{\arpi}[0]{\!\text{\diwani{ط}}}
4492
+
4493
+ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4494
+ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4495
+ % differentials
4496
+
4497
+ \newcommand{\arabdiff}[2]{\frac{#1\ad}{#2\ad}} % arabic differential
4498
+ \newcommand{\arabddiff}[2]{\frac{#1{}^{2}\ad}{{}^{2}#2\ad}} % arabic second differential
4499
+
4500
+ \newcommand{\arabode}[2][-5pt]{\stackrel{\raisebox{#1}{$\cdot$}}{\text{#2}}}
4501
+ \newcommand{\arabodde}[2][-5pt]{\stackrel{\raisebox{#1}{$\vcenter{\hbox{$\cdot\!\cdot$}}$}}{\text{#2}}}
4502
+ \newcommand{\araboddde}[2][-5pt]{\stackrel{\raisebox{#1}{$\vcenter{\hbox{$\cdot\!\cdot\!\cdot$}}$}}{\text{#2}}}
4503
+ \newcommand{\arabpde}[1]{\prescript{}{#1\!\!}{\nabla}} % arabic partial differential equation (PDE)
4504
+
4505
+ \newcommand{\arabprime}[0]{\reflectbox{$\prime$}\!} % arabic prime notation
4506
+ \newcommand{\arabpprime}[0]{\reflectbox{$\prime$}\reflectbox{$\prime$}\!} % arabic prime notation
4507
+ \newcommand{\arabppprime}[0]{\reflectbox{$\prime$}\reflectbox{$\prime$}\reflectbox{$\prime$}\!} % arabic prime notation
4508
+
4509
+ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4510
+ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4511
+ % probability
4512
+ \newcommand{\ap}[0]{\!\text{\diwani{حـ}}} % arabic Probability - used a lot so short form
4513
+ \newcommand{\arabExpct}[0]{\text{\diwani{توقـ}}} % arabic Expectation
4514
+ \newcommand{\arabVar}[0]{\!\text{\diwani{با}}} % arabic Variance
4515
+ \newcommand{\arabCov}[0]{\!\text{\diwani{ت}}} % arabic Covariance
4516
+
4517
+
4518
+ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4519
+ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4520
+ % custom commands specific to lesson
4521
+ \newcommand{\lagrange}[2]{\left(#2\right)\!\prescript{}{#1}{\text{\diwani{لا}}}}
4522
+ \newcommand{\xii}[0]{\left(\prescript{}{\text{يـ}}{\text{س}}\right)\!}
4523
+ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4524
+ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4525
+
4526
+
4527
+ \newcommand{\arsqrt}[2][]{\reflectbox{\(\sqrt[\reflectbox{\(#1\)}]{\reflectbox{\(#2\)}}\)}}
4528
+ \newcommand{\arexp}[0]{\!\raisebox{-4.5pt}{\text{\diwani{ه}}}}
4529
+ \newcommand{\arlog}[0]{\!\!\text{\diwani{لو}}}
4530
+ \newcommand{\arln}[0]{\!\!\prescript{}{\text{\diwani{ه}}}{\text{\diwani{لو}}}}
4531
+ \newcommand{\ardet}[0]{\!\!\text{\diwani{محدد}}}
4532
+
4533
+ \newcommand{\arsum}[0]{\text{مجـــ}}
4534
+ \newcommand{\arprod}[0]{\text{جـــذ}}
4535
+
4536
+ \newcommand{\arlim}[0]{\text{نـــــها}}
4537
+ \newcommand{\armax}[0]{\text{أكبر}}
4538
+ \newcommand{\armin}[0]{\text{أصغر}}
4539
+ \newcommand{\arsup}[0]{\text{أعلى}}
4540
+ \newcommand{\arinf}[0]{\text{أدنى}}
4541
+
4542
+ \newcommand{\arsin}[0]{\!\!\raisebox{-2.5pt}{\text{\diwani{جا}}}}
4543
+ \newcommand{\arcos}[0]{\!\!\raisebox{-2.5pt}{\text{\diwani{جتا}}}}
4544
+ \newcommand{\artan}[0]{\!\!\raisebox{-2.5pt}{\text{\diwani{ظا}}}}
4545
+ \newcommand{\arcot}[0]{\!\!\raisebox{-2.5pt}{\text{\diwani{ظتا}}}}
4546
+ \newcommand{\arsec}[0]{\!\!\raisebox{-2.5pt}{\text{\diwani{قا}}}}
4547
+ \newcommand{\arcsc}[0]{\!\!\raisebox{-2.5pt}{\text{\diwani{قتا}}}}
4548
+
4549
+ \newcommand{\arasin}[0]{\!\!\raisebox{-2.5pt}{\text{\diwani{قجا}}}}
4550
+ \newcommand{\aracos}[0]{\!\!\raisebox{-2.5pt}{\text{\diwani{قجتا}}}}
4551
+ \newcommand{\aratan}[0]{\!\!\raisebox{-2.5pt}{\text{\diwani{قظا}}}}
4552
+ \newcommand{\aracot}[0]{\!\!\raisebox{-2.5pt}{\text{\diwani{قظتا}}}}
4553
+ \newcommand{\arasec}[0]{\!\!\raisebox{-2.5pt}{\text{\diwani{ققا}}}}
4554
+ \newcommand{\aracsc}[0]{\!\!\raisebox{-2.5pt}{\text{\diwani{ققتا}}}}
4555
+
4556
+ \newcommand{\arsinh}[0]{\!\!\raisebox{-2.5pt}{\text{\diwani{جزا}}}}
4557
+ \newcommand{\arcosh}[0]{\!\!\raisebox{-2.5pt}{\text{\diwani{جتزا}}}}
4558
+ \newcommand{\artanh}[0]{\!\!\raisebox{-2.5pt}{\text{\diwani{ظزا}}}}
4559
+ \newcommand{\arcoth}[0]{\!\!\raisebox{-2.5pt}{\text{\diwani{ظتزا}}}}
4560
+ \newcommand{\arsech}[0]{\!\!\raisebox{-2.5pt}{\text{\diwani{قزا}}}}
4561
+ \newcommand{\arcsch}[0]{\!\!\raisebox{-2.5pt}{\text{\diwani{قتزا}}}}
4562
+
4563
+ \newcommand{\arasinh}[0]{\!\!\raisebox{-2.5pt}{\text{\diwani{قجزا}}}}
4564
+ \newcommand{\aracosh}[0]{\!\!\raisebox{-2.5pt}{\text{\diwani{قجتزا}}}}
4565
+ \newcommand{\aratanh}[0]{\!\!\raisebox{-2.5pt}{\text{\diwani{قظزا}}}}
4566
+ \newcommand{\aracoth}[0]{\!\!\raisebox{-2.5pt}{\text{\diwani{قظتزا}}}}
4567
+ \newcommand{\arasech}[0]{\!\!\raisebox{-2.5pt}{\text{\diwani{ققزا}}}}
4568
+ \newcommand{\aracsch}[0]{\!\!\raisebox{-2.5pt}{\text{\diwani{ققتزا}}}}
4569
+
4570
+ \newcommand{\unit}[1]{\text{#1}}
4571
+ \newcommand{\idx}[1]{#1}
4572
+ \newcommand{\arbinom}[2]{\left(\begin{array}{c} #1 \\ #2 \end{array} \right)}
4573
+ `;
4574
+ }
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();
4580
+ }
3448
4581
 
3449
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
+ }
3450
4590
  function tokenLatex(token) {
3451
4591
  if (token.kind === "text") {
3452
4592
  return token.text;
3453
4593
  }
4594
+ if (token.kind === "cite") {
4595
+ return citeTokenLatex(token.keys);
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
+ }
3454
4607
  if (!token.math || token.sourceOwner === "raw" || token.sourceOwner === "editor") {
3455
4608
  return token.source;
3456
4609
  }
@@ -3460,26 +4613,52 @@ function inlineFieldLatex(field) {
3460
4613
  return field.tokens.map((token) => tokenLatex(token)).join("");
3461
4614
  }
3462
4615
  function textBlockLatex(block) {
3463
- 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;
3464
4623
  }
3465
4624
  function indent(value) {
3466
4625
  return value.split("\n").map((line) => ` ${line}`).join("\n");
3467
4626
  }
3468
- function listBlockLatex(block) {
4627
+ function listBlockLatex(block, references) {
3469
4628
  const items = block.items.map((item) => {
3470
- const nested = item.blocks.map((child) => "\n" + indent(blockLatex(child))).join("");
4629
+ const nested = item.blocks.map((child) => "\n" + indent(blockLatex(child, references))).join("");
3471
4630
  return ` \\item ${inlineFieldLatex(item.field)}${nested}`;
3472
4631
  }).join("\n");
3473
4632
  return `${block.command}
3474
4633
  ${items}
3475
4634
  ${block.closing}`;
3476
4635
  }
3477
- function tableBlockLatex(block) {
4636
+ function tabularInnerLatex(block) {
3478
4637
  const rows = block.rows.map((row) => ` ${row.map((cell) => inlineFieldLatex(cell)).join(" & ")}`).join(" \\\\\n");
3479
4638
  return `${block.command}{${block.columns}}
3480
4639
  ${rows}
3481
4640
  ${block.closing}`;
3482
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
+ }
3483
4662
  function imageOptionsLatex(block) {
3484
4663
  const entries = Object.entries(block.options);
3485
4664
  if (entries.length === 0) {
@@ -3487,23 +4666,81 @@ function imageOptionsLatex(block) {
3487
4666
  }
3488
4667
  return `[${entries.map(([key, value]) => `${key}=${value}`).join(",")}]`;
3489
4668
  }
3490
- function blockLatex(block) {
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
+ }
4679
+ function blockLatex(block, references) {
3491
4680
  if (block.kind === "textBlock") {
3492
4681
  return textBlockLatex(block);
3493
4682
  }
3494
4683
  if (block.kind === "list") {
3495
- return listBlockLatex(block);
4684
+ return listBlockLatex(block, references);
3496
4685
  }
3497
4686
  if (block.kind === "table") {
3498
4687
  return tableBlockLatex(block);
3499
4688
  }
3500
4689
  if (block.kind === "image") {
3501
- return `${block.command}${imageOptionsLatex(block)}{${block.value}}`;
4690
+ return imageBlockLatex(block);
4691
+ }
4692
+ if (block.kind === "bibliography") {
4693
+ const width = String(Math.max(references.length, 9));
4694
+ const items = references.map((reference) => ` ${bibliographyEntryLatex(reference)}`).join("\n");
4695
+ return `${block.command}{${width}}
4696
+ ${items}
4697
+ ${block.closing}`;
3502
4698
  }
3503
4699
  return block.value;
3504
4700
  }
3505
- function document2Latex(document2) {
3506
- return document2.blocks.map((block) => blockLatex(block)).join("\n\n");
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
+ }
4723
+ function document2Latex(document2, options = {}) {
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");
4729
+ const wrapDocument = options.wrapDocument !== false;
4730
+ if (!wrapDocument) {
4731
+ return body;
4732
+ }
4733
+ const preamble = getArabicXeLatexPreamble({
4734
+ digitsMapping: options.digitsMapping ?? "arabicdigits",
4735
+ twocolumn: options.twocolumn === true
4736
+ });
4737
+ return `${preamble}
4738
+ \\begin{document}
4739
+
4740
+ ${body}
4741
+
4742
+ \\end{document}
4743
+ `;
3507
4744
  }
3508
4745
 
3509
4746
  // src/document2/history.ts
@@ -3539,18 +4776,99 @@ function document2HistoryCanRedo(stacks) {
3539
4776
  return stacks.future.length > 0;
3540
4777
  }
3541
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
+
3542
4840
  // src/document2/previewModel.ts
3543
4841
  function mathTex(token, equationSide) {
3544
- if (token.kind === "text") {
3545
- return token.text;
3546
- }
3547
4842
  return mathTokenSourceForSide(token, equationSide);
3548
4843
  }
3549
- function previewInlines(field, islands, output, equationSide) {
4844
+ function previewInlines(field, islands, output, equationSide, document2, previewOptions) {
3550
4845
  return field.tokens.map((token) => {
3551
4846
  if (token.kind === "text") {
3552
4847
  return { kind: "text", text: token.text };
3553
4848
  }
4849
+ if (token.kind === "cite") {
4850
+ return {
4851
+ kind: "cite",
4852
+ id: token.id,
4853
+ keys: [...token.keys],
4854
+ label: formatCiteLabel(token.keys, document2.references, {
4855
+ documentDirection: previewOptions.documentDirection,
4856
+ digitForm: previewOptions.digitForm
4857
+ })
4858
+ };
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
+ }
3554
4872
  const island = {
3555
4873
  id: token.id,
3556
4874
  tex: mathTex(token, equationSide),
@@ -3562,21 +4880,43 @@ function previewInlines(field, islands, output, equationSide) {
3562
4880
  return { kind: "math", ...island };
3563
4881
  });
3564
4882
  }
3565
- function textBlockPreview(block, islands, output, equationSide) {
4883
+ function textBlockPreview(block, islands, output, equationSide, document2, previewOptions) {
4884
+ const inlines = previewInlines(block.field, islands, output, equationSide, document2, previewOptions);
3566
4885
  if (block.command === "\\section") {
3567
- return { kind: "heading", id: block.id, level: 1, inlines: previewInlines(block.field, islands, output, equationSide) };
4886
+ return { kind: "heading", id: block.id, level: 1, inlines };
3568
4887
  }
3569
4888
  if (block.command === "\\subsection") {
3570
- return { kind: "heading", id: block.id, level: 2, inlines: previewInlines(block.field, islands, output, equationSide) };
4889
+ return { kind: "heading", id: block.id, level: 2, inlines };
3571
4890
  }
3572
4891
  if (block.command === "\\subsubsection") {
3573
- return { kind: "heading", id: block.id, level: 3, inlines: previewInlines(block.field, islands, output, equationSide) };
4892
+ return { kind: "heading", id: block.id, level: 3, inlines };
4893
+ }
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;
3574
4908
  }
3575
- return { kind: "paragraph", id: block.id, inlines: previewInlines(block.field, islands, output, equationSide) };
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}`;
3576
4916
  }
3577
- function blockPreview(block, islands, output, equationSide) {
4917
+ function blockPreview(block, islands, output, equationSide, document2, previewOptions) {
3578
4918
  if (block.kind === "textBlock") {
3579
- return textBlockPreview(block, islands, output, equationSide);
4919
+ return textBlockPreview(block, islands, output, equationSide, document2, previewOptions);
3580
4920
  }
3581
4921
  if (block.kind === "list") {
3582
4922
  return {
@@ -3585,51 +4925,155 @@ function blockPreview(block, islands, output, equationSide) {
3585
4925
  ordered: block.command === "\\begin{enumerate}",
3586
4926
  items: block.items.map((item) => ({
3587
4927
  id: item.id,
3588
- inlines: previewInlines(item.field, islands, output, equationSide),
3589
- blocks: item.blocks.map((child) => blockPreview(child, islands, output, equationSide))
4928
+ inlines: previewInlines(item.field, islands, output, equationSide, document2, previewOptions),
4929
+ blocks: item.blocks.map((child) => blockPreview(child, islands, output, equationSide, document2, previewOptions))
3590
4930
  }))
3591
4931
  };
3592
4932
  }
3593
4933
  if (block.kind === "table") {
3594
- return { kind: "table", id: block.id, rows: block.rows.map((row) => row.map((cell) => previewInlines(cell, islands, output, equationSide))) };
4934
+ const label = block.labelEnabled ? block.label.trim() : "";
4935
+ return {
4936
+ kind: "table",
4937
+ id: block.id,
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) } : {}
4941
+ };
3595
4942
  }
3596
4943
  if (block.kind === "image") {
3597
- return { kind: "image", id: block.id, src: block.value, options: block.options };
4944
+ const label = block.labelEnabled ? block.label.trim() : "";
4945
+ return {
4946
+ kind: "image",
4947
+ id: block.id,
4948
+ src: block.value,
4949
+ ...block.assetId !== void 0 ? { assetId: block.assetId } : {},
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) } : {}
4953
+ };
4954
+ }
4955
+ if (block.kind === "bibliography") {
4956
+ return {
4957
+ kind: "bibliography",
4958
+ id: block.id,
4959
+ items: document2.references.map((reference, index) => ({
4960
+ id: reference.id,
4961
+ numberLabel: formatBibliographyNumber(index + 1, {
4962
+ documentDirection: previewOptions.documentDirection,
4963
+ digitForm: previewOptions.digitForm
4964
+ }),
4965
+ key: reference.key,
4966
+ authors: reference.authors,
4967
+ title: reference.title,
4968
+ year: reference.year,
4969
+ url: reference.url,
4970
+ venue: reference.venue
4971
+ }))
4972
+ };
3598
4973
  }
3599
4974
  return { kind: "omit", id: block.id };
3600
4975
  }
3601
- function document2Preview(document2, output = "svg", equationSide = "arabic") {
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
+ }
4998
+ function document2Preview(document2, output = "svg", equationSide = "arabic", previewOptions = {}) {
3602
4999
  const mathIslands = [];
5000
+ const options = {
5001
+ documentDirection: previewOptions.documentDirection ?? "rtl",
5002
+ digitForm: previewOptions.digitForm ?? (previewOptions.documentDirection === "ltr" ? "western" : "arabicIndic"),
5003
+ uiLocale: previewOptions.uiLocale ?? "ar"
5004
+ };
5005
+ const body = document2.blocks.map((block) => blockPreview(block, mathIslands, output, equationSide, document2, options));
3603
5006
  return {
3604
- blocks: document2.blocks.map((block) => blockPreview(block, mathIslands, output, equationSide)),
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
+ ],
3605
5012
  mathIslands
3606
5013
  };
3607
5014
  }
3608
5015
  // Annotate the CommonJS export names for ESM import in node:
3609
5016
  0 && (module.exports = {
5017
+ BUTEX_BASMALA,
5018
+ BUTEX_CLOSING_HAMDALA,
3610
5019
  DEFAULT_DOCUMENT2_HISTORY_MAX_DEPTH,
5020
+ DEFAULT_HIJRI_DATE,
5021
+ HIJRI_MONTH_IDS,
5022
+ HIJRI_YEAR_MAX,
5023
+ HIJRI_YEAR_MIN,
3611
5024
  addDocument2ImageBlock,
3612
5025
  addDocument2ListBlock,
3613
5026
  addDocument2ListItem,
5027
+ addDocument2Reference,
3614
5028
  addDocument2TableBlock,
3615
5029
  addDocument2TextBlock,
5030
+ applyFloatMetaPatch,
5031
+ arabicXeLatexPreambleCmd,
5032
+ arabicXeLatexPreambleFont,
5033
+ arabicXeLatexPreamblePkg,
5034
+ bibliographyEntryLatex,
5035
+ butexDiwaniDisplayLatex,
5036
+ butexDiwaniDisplayTex,
5037
+ citeTokenLatex,
5038
+ cloneDocument2Meta,
3616
5039
  cloneDocument2Node,
5040
+ collectDocument2Labels,
3617
5041
  createDocument2History,
3618
5042
  createEmptyArabicEquationSession,
3619
5043
  createEmptyDocument2,
3620
5044
  createEmptyEquationSession,
5045
+ createEmptyReference2,
3621
5046
  createInlineField2,
5047
+ defaultFloatMeta,
5048
+ detectInlineSpans2,
3622
5049
  detectMathSpans2,
5050
+ document2HasAbstract,
5051
+ document2HasMaketitle,
5052
+ document2HasTitleStack,
3623
5053
  document2HistoryCanRedo,
3624
5054
  document2HistoryCanUndo,
3625
5055
  document2Latex,
3626
5056
  document2Preview,
5057
+ emptyBlockSelection,
5058
+ emptyDocument2Meta,
5059
+ ensureDocument2BibliographyBlock,
5060
+ extendBlockSelectionFromAnchor,
5061
+ fillDocument2MetaGaps,
3627
5062
  findFirstInlineField,
5063
+ formatBibliographyNumber,
5064
+ formatCiteLabel,
5065
+ formatHijriDate,
5066
+ formatRefLabel,
3628
5067
  fromDocumentJson2,
5068
+ getArabicXeLatexPreamble,
5069
+ hijriMonthLabel,
3629
5070
  inlineFieldLatex,
5071
+ insertCiteTokenAtCaret,
3630
5072
  insertDocument2BlockAfter,
3631
5073
  insertMathToken,
3632
5074
  insertMathTokenAtCaret,
5075
+ insertRefTokenAtCaret,
5076
+ labelNumberMap,
3633
5077
  mathNodeFromArabicSession,
3634
5078
  mathNodeFromSession,
3635
5079
  mathSourceFromArabicSession,
@@ -3637,15 +5081,44 @@ function document2Preview(document2, output = "svg", equationSide = "arabic") {
3637
5081
  mathTokenSourceForSide,
3638
5082
  mathTokenToArabicEditorSession,
3639
5083
  mathTokenToEditorSession,
5084
+ mergeDocument2Meta,
3640
5085
  moveDocument2BlockById,
5086
+ moveDocument2BlockRange,
5087
+ moveDocument2Reference,
5088
+ normalizeBlockSelection,
5089
+ normalizeDocument2HijriDate,
5090
+ normalizeDocument2Meta,
5091
+ parseCiteKeys,
5092
+ parseFloatMetaFromJson,
5093
+ parseRefKeys,
3641
5094
  pushDocument2Snapshot,
5095
+ refTokenLatex,
5096
+ referenceFromJson,
5097
+ referenceNumberMap,
5098
+ remapSelectionAfterRangeMove,
5099
+ removeCiteTokenById,
3642
5100
  removeDocument2BlockById,
5101
+ removeDocument2BlockRange,
3643
5102
  removeDocument2ListItem,
5103
+ removeDocument2Reference,
3644
5104
  removeMathTokenById,
5105
+ removeRefTokenById,
3645
5106
  replaceMathTokenFromSession,
5107
+ resolveCiteNumbers,
3646
5108
  restoreDocument2Redo,
3647
5109
  restoreDocument2Undo,
5110
+ setDocument2References,
5111
+ stripDisplayMathDelimiters,
5112
+ toggleBlockInSelection,
5113
+ updateCiteTokenKeys,
5114
+ updateDocument2ImageMeta,
3648
5115
  updateDocument2ImageValue,
5116
+ updateDocument2Meta,
5117
+ updateDocument2Reference,
5118
+ updateDocument2TableMeta,
5119
+ updateDocument2TextBlockCentered,
5120
+ updateMathTokenLabel,
5121
+ updateRefTokenKeys,
3649
5122
  updateTextToken
3650
5123
  });
3651
5124
  //# sourceMappingURL=document2.js.map