@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/README.md CHANGED
@@ -253,6 +253,10 @@ const latex = document2Latex(documentNode);
253
253
  const preview = document2Preview(documentNode, 'svg');
254
254
  ```
255
255
 
256
+ `document2Latex(doc)` emits a **complete Arabic XeLaTeX document** by default (preamble + `\begin{document}` + body + `\end{document}`). Pass `{ wrapDocument: false }` for body-only blocks. Optional `digitsMapping: 'arabicdigits' | 'digits'` controls eastern vs western digit font mapping in the preamble (default `'arabicdigits'`). Pass `{ twocolumn: true }` for `\documentclass[12pt,a4paper,twocolumn]{article}`. See [`examples/example-latex-export.tex`](examples/example-latex-export.tex) for a full sample (regenerated by the wrap vitest). Hosts can also call `getArabicXeLatexPreamble()` alone.
257
+
258
+ Figures and tables always export as centered `figure` / `table` floats. Optional caption/label fields (checkbox-driven in the editor) emit `\caption` / `\label`. Display math stays `\[…\]` unless an equation label is enabled, then export uses `\begin{equation}…\label{…}\end{equation}`. Internal cross-refs use `\ref` / `\eqref` tokens (separate from bibliography `\cite`).
259
+
256
260
  V2 exports Arabic TeX by default for equations saved from the embedded editor. Imported raw-only math is preserved as raw source and marked non-editable until a structured equation object is attached.
257
261
 
258
262
  ### Document JSON contract (`value` + `math_objects`)
@@ -332,7 +336,7 @@ export default function Page() {
332
336
  }
333
337
  ```
334
338
 
335
- Host apps still register BuTeX with MathJax before preview rendering. `uiLocale` selects Arabic (`"ar"`, the default) or English (`"en"`) document-editor chrome and is passed to the embedded equation editor. `documentDirection` independently controls prose inputs and preview flow without creating a second document tree. `equationSide` independently controls whether structured equations open, render, and save from the `"english"` or `"arabic"` side. Set `editableEquations={false}` to show math islands without equation insertion, deletion, or editor access. Set `previewOnly={true}` to render only the read-only document preview with no toolbar, editor panel, or equation drawer. Raw-only equations keep their original source because the browser does not parse raw LaTeX into equation ASTs. Document editor v2 defaults to **`tex-svg.js`**; use `tex-chtml.js` only if you pass `mathOutput="chtml"`.
339
+ Host apps still register BuTeX with MathJax before preview rendering. `uiLocale` selects Arabic (`"ar"`, the default) or English (`"en"`) document-editor chrome and is passed to the embedded equation editor. `documentDirection` independently controls prose inputs and preview flow without creating a second document tree. `equationSide` independently controls whether structured equations open, render, and save from the `"english"` or `"arabic"` side. Set `editableEquations={false}` to show math islands without equation insertion, deletion, or editor access. Set `previewOnly={true}` to render only the read-only document preview with no toolbar, editor panel, or equation drawer. Optional `\includegraphics` `asset_id` is preserved on import; pass `resolveImageUrl={({ assetId, value }) => …})` so preview can load S3/CDN/local assets while plain `value` URLs keep working without a resolver. Document JSON may include root `meta` (`title`, `authors`, structured Hijri `date` `{ day, month, year }` with Arabic month names such as `"محرم"`, `abstract`) plus a `references` catalog (`key`, `authors`, `title`, `year`, `url`, `venue`) and `\cite{key1,key2}` spans in text values. Pass `documentMeta` to seed missing meta fields from the host; JSON keys win when present. The editor always centers a fixed basmala line before the title block and a closing ḥamdala after the body (preview + LaTeX). Hijri date uses day/month/year dropdowns (no calendar conversion; default year 1448). The editor shows numeric cite chips and a bibliography block. RTL preview/chips render reversed labels such as `[٣،٢،١]` (Arabic comma); LTR stays `[1, 2, 3]`. Pass `digitForm` (`western` / `arabicIndic` / `persianIndic`, defaulting from `documentDirection`) or use the toolbar digit control. LaTeX export keeps logical `\cite{…}` key order for XeLaTeX; digit shaping and bidi display in PDF belong in the host preamble (`bidi`/polyglossia + font `Mapping`, see `references/commands.py`). Range compression (`[3–7]`) is not implemented yet. Raw-only equations keep their original source because the browser does not parse raw LaTeX into equation ASTs. Document editor v2 defaults to **`tex-svg.js`**; use `tex-chtml.js` only if you pass `mathOutput="chtml"`.
336
340
 
337
341
  ### Styling/theming contract
338
342
 
@@ -46,6 +46,82 @@ declare class ChainNode {
46
46
  toArabicLatex(): string;
47
47
  }
48
48
 
49
+ type EditorSide = 'english' | 'arabic';
50
+ type EditorNodeKind = 'char' | 'number' | 'operator' | 'delimiter' | 'frac' | 'sqrt' | 'env' | 'atomicCommand' | 'atomicOperatorCommand';
51
+ type MatrixEnvStyle = 'matrix' | 'pmatrix' | 'bmatrix' | 'Bmatrix' | 'vmatrix' | 'Vmatrix';
52
+ type GridEnvName = MatrixEnvStyle | 'array' | 'aligned';
53
+ type EnvColumnAlignment = 'l' | 'c' | 'r';
54
+ type CharacterFontId = 'default' | 'takween' | 'diwani' | 'diwaniOutline';
55
+ type DigitFormId = 'western' | 'arabicIndic' | 'persianIndic';
56
+ type EditorSyncState = 'synced' | 'diverged';
57
+ type EditorChain = {
58
+ id: string;
59
+ nodes: EditorNode[];
60
+ };
61
+ type EditorNode = {
62
+ id: string;
63
+ kind: EditorNodeKind;
64
+ expr: string;
65
+ characterFont?: CharacterFontId;
66
+ superscript: EditorChain | null;
67
+ subscript: EditorChain | null;
68
+ leftDelimExpr: string;
69
+ rightDelimExpr: string;
70
+ innerExpr: EditorChain | null;
71
+ numerator: EditorChain | null;
72
+ denominator: EditorChain | null;
73
+ radicand: EditorChain | null;
74
+ rootIndex: EditorChain | null;
75
+ envName: GridEnvName | null;
76
+ matrixStyle: MatrixEnvStyle | null;
77
+ matrixRows: EditorChain[][] | null;
78
+ columnAlignments: EnvColumnAlignment[] | null;
79
+ };
80
+ type EditorCaret = {
81
+ chainId: string;
82
+ index: number;
83
+ };
84
+ type EditorSelectionRange = {
85
+ chainId: string;
86
+ anchorIndex: number;
87
+ focusIndex: number;
88
+ };
89
+ type EditorSyncMap = Record<string, {
90
+ expr: EditorSyncState;
91
+ }>;
92
+ type EditorDebugEntry = {
93
+ timestamp: string;
94
+ command: string;
95
+ activeSide: EditorSide;
96
+ englishLatex: string;
97
+ arabicLatex: string;
98
+ renderOk: boolean;
99
+ renderError: string | null;
100
+ note: string;
101
+ };
102
+ type EditorSession = {
103
+ englishTree: EditorChain;
104
+ arabicTree: EditorChain;
105
+ activeSide: EditorSide;
106
+ /** When true, each typed letter/digit inserts a new char/number leaf; when false, merge into adjacent same-kind leaves. */
107
+ splitLeafTyping: boolean;
108
+ /** One-shot: when true, the next insertChar/insertNumber bypasses leaf-merge (used by Space in merge mode). */
109
+ forceSplitNextLeaf: boolean;
110
+ /** When true and active side is Arabic, merged number typing prepends digits (RTL-style); still mirrored on EN/AR. */
111
+ arabicReverseNumberTyping: boolean;
112
+ /** Font applied to newly typed char nodes. */
113
+ currentCharacterFont: CharacterFontId;
114
+ /** Digit glyph form used for editor display and LaTeX preview output. */
115
+ currentDigitForm: DigitFormId;
116
+ caret: EditorCaret;
117
+ /** Whole-node range selection inside a single chain (anchor==focus means no selection). */
118
+ selection: EditorSelectionRange | null;
119
+ selectedNodeId: string | null;
120
+ selectedStructureNodeId: string | null;
121
+ sync: EditorSyncMap;
122
+ debugLog: EditorDebugEntry[];
123
+ };
124
+
49
125
  type MathNode = {
50
126
  nodeType: 'MathObject';
51
127
  mathMode: string;
@@ -55,25 +131,55 @@ type MathNode = {
55
131
 
56
132
  type Document2ParseMode = 'english' | 'arabic';
57
133
  type Document2MathOutput = 'svg' | 'chtml';
134
+ type Document2Direction = 'rtl' | 'ltr';
58
135
  type MathObject2Json = {
59
136
  node_type: 'MathObject';
60
137
  math_mode: string;
61
138
  lines: ChainJson[];
62
139
  closing: string;
63
140
  };
141
+ type Reference2Json = {
142
+ key: string;
143
+ authors?: string;
144
+ title?: string;
145
+ year?: string;
146
+ url?: string;
147
+ venue?: string;
148
+ };
149
+ type HijriMonthId = 'محرم' | 'صفر' | 'ربيع الأول' | 'ربيع الآخر' | 'جمادى الأولى' | 'جمادى الآخرة' | 'رجب' | 'شعبان' | 'رمضان' | 'شوال' | 'ذو القعدة' | 'ذو الحجة';
150
+ type Document2HijriDate = {
151
+ day: number;
152
+ month: HijriMonthId;
153
+ year: number;
154
+ };
155
+ type Document2Meta = {
156
+ title: string;
157
+ authors: string;
158
+ date: Document2HijriDate;
159
+ abstract: string;
160
+ };
64
161
  type Document2Json = {
65
162
  node_type: 'DocumentObject';
163
+ meta?: Document2Meta;
164
+ references?: Reference2Json[];
66
165
  blocks: Document2BlockJson[];
67
166
  };
68
167
  type Document2BlockJson = {
69
168
  command: string;
70
169
  value?: string;
170
+ /** Stable host asset identity (e.g. S3 key / UUID); optional for \\includegraphics. */
171
+ asset_id?: string;
71
172
  math_objects?: MathObject2Json[];
72
173
  options?: Record<string, string>;
73
174
  closing?: string;
74
175
  items?: Document2ListItemJson[];
75
176
  rows?: string[][];
76
177
  columns?: string;
178
+ caption?: string;
179
+ label?: string;
180
+ caption_enabled?: boolean;
181
+ label_enabled?: boolean;
182
+ centered?: boolean;
77
183
  };
78
184
  type Document2ListItemJson = {
79
185
  value: string;
@@ -85,6 +191,15 @@ type Document2Diagnostic = {
85
191
  message: string;
86
192
  path: string;
87
193
  };
194
+ type Reference2 = {
195
+ id: string;
196
+ key: string;
197
+ authors: string;
198
+ title: string;
199
+ year: string;
200
+ url: string;
201
+ venue: string;
202
+ };
88
203
  type TextToken2 = {
89
204
  id: string;
90
205
  kind: 'text';
@@ -102,8 +217,22 @@ type MathToken2 = {
102
217
  editable: boolean;
103
218
  reason?: string;
104
219
  sourceOwner: 'imported-structured' | 'raw' | 'editor';
220
+ /** When true on display math, export as equation env with \\label. */
221
+ labelEnabled?: boolean;
222
+ label?: string;
223
+ };
224
+ type CiteToken2 = {
225
+ id: string;
226
+ kind: 'cite';
227
+ keys: string[];
228
+ };
229
+ type RefToken2 = {
230
+ id: string;
231
+ kind: 'ref';
232
+ keys: string[];
233
+ refCommand: 'ref' | 'eqref';
105
234
  };
106
- type InlineToken2 = TextToken2 | MathToken2;
235
+ type InlineToken2 = TextToken2 | MathToken2 | CiteToken2 | RefToken2;
107
236
  type InlineField2 = {
108
237
  id: string;
109
238
  tokens: InlineToken2[];
@@ -113,6 +242,8 @@ type TextBlock2 = {
113
242
  kind: 'textBlock';
114
243
  command: '\\section' | '\\subsection' | '\\subsubsection' | '\\paragraph';
115
244
  field: InlineField2;
245
+ /** When true on \\paragraph, center in preview and LaTeX export. */
246
+ centered?: boolean;
116
247
  };
117
248
  type ListItem2 = {
118
249
  id: string;
@@ -126,6 +257,13 @@ type ListBlock2 = {
126
257
  closing: '\\end{itemize}' | '\\end{enumerate}';
127
258
  items: ListItem2[];
128
259
  };
260
+ type FloatMeta2 = {
261
+ centered: boolean;
262
+ captionEnabled: boolean;
263
+ caption: string;
264
+ labelEnabled: boolean;
265
+ label: string;
266
+ };
129
267
  type TableBlock2 = {
130
268
  id: string;
131
269
  kind: 'table';
@@ -133,13 +271,21 @@ type TableBlock2 = {
133
271
  closing: '\\end{tabular}';
134
272
  columns: string;
135
273
  rows: InlineField2[][];
136
- };
274
+ } & FloatMeta2;
137
275
  type ImageBlock2 = {
138
276
  id: string;
139
277
  kind: 'image';
140
278
  command: '\\includegraphics';
141
279
  value: string;
280
+ /** Host asset identity from wire `asset_id`; preserved across value edits. */
281
+ assetId?: string;
142
282
  options: Record<string, string>;
283
+ } & FloatMeta2;
284
+ type BibliographyBlock2 = {
285
+ id: string;
286
+ kind: 'bibliography';
287
+ command: '\\begin{thebibliography}';
288
+ closing: '\\end{thebibliography}';
143
289
  };
144
290
  type RawBlock2 = {
145
291
  id: string;
@@ -147,9 +293,11 @@ type RawBlock2 = {
147
293
  command: '\\raw';
148
294
  value: string;
149
295
  };
150
- type Document2Block = TextBlock2 | ListBlock2 | TableBlock2 | ImageBlock2 | RawBlock2;
296
+ type Document2Block = TextBlock2 | ListBlock2 | TableBlock2 | ImageBlock2 | BibliographyBlock2 | RawBlock2;
151
297
  type Document2Node = {
152
298
  nodeType: 'DocumentObject';
299
+ meta: Document2Meta;
300
+ references: Reference2[];
153
301
  blocks: Document2Block[];
154
302
  diagnostics: Document2Diagnostic[];
155
303
  };
@@ -165,6 +313,24 @@ type DetectedMathSpan2 = {
165
313
  closing: string;
166
314
  display: boolean;
167
315
  };
316
+ type DetectedCiteSpan2 = {
317
+ kind: 'cite';
318
+ start: number;
319
+ end: number;
320
+ source: string;
321
+ keys: string[];
322
+ };
323
+ type DetectedRefSpan2 = {
324
+ kind: 'ref';
325
+ start: number;
326
+ end: number;
327
+ source: string;
328
+ keys: string[];
329
+ refCommand: 'ref' | 'eqref';
330
+ };
331
+ type DetectedInlineSpan2 = ({
332
+ kind: 'math';
333
+ } & DetectedMathSpan2) | DetectedCiteSpan2 | DetectedRefSpan2;
168
334
  type PreviewInline2 = {
169
335
  kind: 'text';
170
336
  text: string;
@@ -175,8 +341,39 @@ type PreviewInline2 = {
175
341
  display: boolean;
176
342
  output: Document2MathOutput;
177
343
  editable: boolean;
344
+ } | {
345
+ kind: 'cite';
346
+ id: string;
347
+ keys: string[];
348
+ label: string;
349
+ } | {
350
+ kind: 'ref';
351
+ id: string;
352
+ keys: string[];
353
+ refCommand: 'ref' | 'eqref';
354
+ label: string;
178
355
  };
179
356
  type PreviewBlock2 = {
357
+ kind: 'basmala';
358
+ id: string;
359
+ text: string;
360
+ tex: string;
361
+ } | {
362
+ kind: 'titleBlock';
363
+ id: string;
364
+ title?: string;
365
+ authors?: string;
366
+ date?: string;
367
+ } | {
368
+ kind: 'abstract';
369
+ id: string;
370
+ text: string;
371
+ } | {
372
+ kind: 'closing';
373
+ id: string;
374
+ text: string;
375
+ tex: string;
376
+ } | {
180
377
  kind: 'heading';
181
378
  id: string;
182
379
  level: 1 | 2 | 3;
@@ -185,6 +382,7 @@ type PreviewBlock2 = {
185
382
  kind: 'paragraph';
186
383
  id: string;
187
384
  inlines: PreviewInline2[];
385
+ centered?: boolean;
188
386
  } | {
189
387
  kind: 'list';
190
388
  id: string;
@@ -198,11 +396,31 @@ type PreviewBlock2 = {
198
396
  kind: 'table';
199
397
  id: string;
200
398
  rows: PreviewInline2[][][];
399
+ caption?: string;
400
+ label?: string;
401
+ numberLabel?: string;
201
402
  } | {
202
403
  kind: 'image';
203
404
  id: string;
204
405
  src: string;
406
+ assetId?: string;
205
407
  options: Record<string, string>;
408
+ caption?: string;
409
+ label?: string;
410
+ numberLabel?: string;
411
+ } | {
412
+ kind: 'bibliography';
413
+ id: string;
414
+ items: Array<{
415
+ id: string;
416
+ numberLabel: string;
417
+ key: string;
418
+ authors: string;
419
+ title: string;
420
+ year: string;
421
+ url: string;
422
+ venue: string;
423
+ }>;
206
424
  }
207
425
  /** Preview-only: raw / unknown blocks are omitted from preview until we add proper rendering. */
208
426
  | {
@@ -220,103 +438,171 @@ type Document2Preview = {
220
438
  blocks: PreviewBlock2[];
221
439
  mathIslands: MathIsland2[];
222
440
  };
223
-
224
- type EditorSide = 'english' | 'arabic';
225
- type EditorNodeKind = 'char' | 'number' | 'operator' | 'delimiter' | 'frac' | 'sqrt' | 'env' | 'atomicCommand' | 'atomicOperatorCommand';
226
- type MatrixEnvStyle = 'matrix' | 'pmatrix' | 'bmatrix' | 'Bmatrix' | 'vmatrix' | 'Vmatrix';
227
- type GridEnvName = MatrixEnvStyle | 'array' | 'aligned';
228
- type EnvColumnAlignment = 'l' | 'c' | 'r';
229
- type CharacterFontId = 'default' | 'takween' | 'diwani' | 'diwaniOutline';
230
- type DigitFormId = 'western' | 'arabicIndic' | 'persianIndic';
231
- type EditorSyncState = 'synced' | 'diverged';
232
- type EditorChain = {
233
- id: string;
234
- nodes: EditorNode[];
235
- };
236
- type EditorNode = {
237
- id: string;
238
- kind: EditorNodeKind;
239
- expr: string;
240
- characterFont?: CharacterFontId;
241
- superscript: EditorChain | null;
242
- subscript: EditorChain | null;
243
- leftDelimExpr: string;
244
- rightDelimExpr: string;
245
- innerExpr: EditorChain | null;
246
- numerator: EditorChain | null;
247
- denominator: EditorChain | null;
248
- radicand: EditorChain | null;
249
- rootIndex: EditorChain | null;
250
- envName: GridEnvName | null;
251
- matrixStyle: MatrixEnvStyle | null;
252
- matrixRows: EditorChain[][] | null;
253
- columnAlignments: EnvColumnAlignment[] | null;
254
- };
255
- type EditorCaret = {
256
- chainId: string;
257
- index: number;
258
- };
259
- type EditorSelectionRange = {
260
- chainId: string;
261
- anchorIndex: number;
262
- focusIndex: number;
441
+ type FormatCiteLabelOptions = {
442
+ documentDirection?: Document2Direction;
443
+ digitForm?: DigitFormId;
263
444
  };
264
- type EditorSyncMap = Record<string, {
265
- expr: EditorSyncState;
445
+
446
+ type Document2LabelKind = 'fig' | 'tab' | 'eq';
447
+ type Document2LabelEntry = {
448
+ key: string;
449
+ kind: Document2LabelKind;
450
+ caption: string;
451
+ /** Block id for fig/tab, or math token id for eq. */
452
+ ownerId: string;
453
+ number: number;
454
+ };
455
+ type FloatMetaPatch = {
456
+ centered?: boolean;
457
+ captionEnabled?: boolean;
458
+ caption?: string;
459
+ labelEnabled?: boolean;
460
+ label?: string;
461
+ };
462
+ declare function defaultFloatMeta(): {
463
+ centered: true;
464
+ captionEnabled: false;
465
+ caption: '';
466
+ labelEnabled: false;
467
+ label: '';
468
+ };
469
+ declare function parseFloatMetaFromJson(json: {
470
+ caption?: string;
471
+ label?: string;
472
+ caption_enabled?: boolean;
473
+ label_enabled?: boolean;
474
+ centered?: boolean;
475
+ }): {
476
+ centered: boolean;
477
+ captionEnabled: boolean;
478
+ caption: string;
479
+ labelEnabled: boolean;
480
+ label: string;
481
+ };
482
+ declare function parseRefKeys(source: string): {
483
+ keys: string[];
484
+ refCommand: 'ref' | 'eqref';
485
+ };
486
+ declare function refTokenLatex(keys: string[], refCommand: 'ref' | 'eqref'): string;
487
+ /** Collect enabled labels in document order, numbering per kind. */
488
+ declare function collectDocument2Labels(document: Document2Node): Document2LabelEntry[];
489
+ declare function labelNumberMap(document: Document2Node): Map<string, {
490
+ kind: Document2LabelKind;
491
+ number: number;
266
492
  }>;
267
- type EditorDebugEntry = {
268
- timestamp: string;
269
- command: string;
270
- activeSide: EditorSide;
271
- englishLatex: string;
272
- arabicLatex: string;
273
- renderOk: boolean;
274
- renderError: string | null;
275
- note: string;
276
- };
277
- type EditorSession = {
278
- englishTree: EditorChain;
279
- arabicTree: EditorChain;
280
- activeSide: EditorSide;
281
- /** When true, each typed letter/digit inserts a new char/number leaf; when false, merge into adjacent same-kind leaves. */
282
- splitLeafTyping: boolean;
283
- /** One-shot: when true, the next insertChar/insertNumber bypasses leaf-merge (used by Space in merge mode). */
284
- forceSplitNextLeaf: boolean;
285
- /** When true and active side is Arabic, merged number typing prepends digits (RTL-style); still mirrored on EN/AR. */
286
- arabicReverseNumberTyping: boolean;
287
- /** Font applied to newly typed char nodes. */
288
- currentCharacterFont: CharacterFontId;
289
- /** Digit glyph form used for editor display and LaTeX preview output. */
290
- currentDigitForm: DigitFormId;
291
- caret: EditorCaret;
292
- /** Whole-node range selection inside a single chain (anchor==focus means no selection). */
293
- selection: EditorSelectionRange | null;
294
- selectedNodeId: string | null;
295
- selectedStructureNodeId: string | null;
296
- sync: EditorSyncMap;
297
- debugLog: EditorDebugEntry[];
298
- };
493
+ declare function formatRefLabel(keys: string[], document: Document2Node, refCommand: 'ref' | 'eqref', options?: FormatCiteLabelOptions): string;
494
+ declare function stripDisplayMathDelimiters(source: string): string;
495
+ declare function applyFloatMetaPatch<T extends ImageBlock2 | TableBlock2>(block: T, patch: FloatMetaPatch): void;
496
+
497
+ type ButexUiLocale = 'ar' | 'en';
498
+
499
+ declare const 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";
500
+ declare const 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";
501
+ /** Display-math body using BuTeX Diwani (for MathJax preview islands). */
502
+ declare function butexDiwaniDisplayTex(text: string): string;
503
+ /** Full display equation for XeLaTeX export. */
504
+ declare function butexDiwaniDisplayLatex(text: string): string;
505
+ declare const HIJRI_YEAR_MIN = 1400;
506
+ declare const HIJRI_YEAR_MAX = 1500;
507
+ declare const HIJRI_MONTH_IDS: HijriMonthId[];
508
+ declare const DEFAULT_HIJRI_DATE: Document2HijriDate;
509
+ declare function hijriMonthLabel(month: HijriMonthId, locale?: ButexUiLocale): string;
510
+ declare function emptyDocument2Meta(): Document2Meta;
511
+ declare function normalizeDocument2HijriDate(value: unknown): Document2HijriDate;
512
+ declare function normalizeDocument2Meta(value: unknown): Document2Meta;
513
+ type Document2MetaProp = Partial<Omit<Document2Meta, 'date'>> & {
514
+ date?: Partial<Document2HijriDate>;
515
+ };
516
+ /** Prop fills gaps; JSON wins only when the key is present on the JSON object. */
517
+ declare function mergeDocument2Meta(fromJson: unknown, fromProp?: Document2MetaProp): Document2Meta;
518
+ /** For live Document2Node meta: prop fills empty title/authors/abstract; date patch merges. */
519
+ declare function fillDocument2MetaGaps(current: Document2Meta, fromProp?: Document2MetaProp): Document2Meta;
520
+ declare function document2HasMaketitle(meta: Document2Meta): boolean;
521
+ declare function document2HasAbstract(meta: Document2Meta): boolean;
522
+ declare function document2HasTitleStack(meta: Document2Meta): boolean;
523
+ type FormatHijriDateOptions = {
524
+ locale?: ButexUiLocale;
525
+ digitForm?: DigitFormId;
526
+ };
527
+ declare function formatHijriDate(date: Document2HijriDate, options?: FormatHijriDateOptions | ButexUiLocale): string;
528
+ declare function cloneDocument2Meta(meta: Document2Meta): Document2Meta;
299
529
 
300
530
  declare function findFirstInlineField(document: Document2Node): InlineField2 | null;
301
531
  declare function updateTextToken(document: Document2Node, fieldId: string, tokenId: string, text: string): Document2Node;
302
532
  declare function cloneDocument2Node(document: Document2Node): Document2Node;
303
533
  declare function insertDocument2BlockAfter(document: Document2Node, afterBlockId: string | null | undefined, block: Document2Block): Document2Node;
304
534
  declare function moveDocument2BlockById(document: Document2Node, blockId: string, direction: -1 | 1): Document2Node;
535
+ declare function moveDocument2BlockRange(document: Document2Node, from: number, to: number, direction: -1 | 1): Document2Node;
536
+ declare function removeDocument2BlockRange(document: Document2Node, from: number, to: number): Document2Node;
305
537
  declare function removeMathTokenById(document: Document2Node, tokenId: string): Document2Node;
306
538
  declare function insertMathTokenAtCaret(document: Document2Node, fieldId: string, textTokenId: string | null, caretOffset: number, session: EditorSession, opening?: string, closing?: string, side?: EditorSide): Document2Node;
307
539
  declare function insertMathToken(document: Document2Node, fieldId: string, insertIndex: number, session: EditorSession, opening?: string, closing?: string, side?: EditorSide): Document2Node;
308
540
  declare function replaceMathTokenFromSession(document: Document2Node, tokenId: string, session: EditorSession, opening?: string, closing?: string, side?: EditorSide): Document2Node;
309
541
  declare function addDocument2TextBlock(document: Document2Node, command?: '\\section' | '\\subsection' | '\\subsubsection' | '\\paragraph', afterBlockId?: string | null): Document2Node;
542
+ declare function updateDocument2TextBlockCentered(document: Document2Node, blockId: string, centered: boolean): Document2Node;
310
543
  declare function removeDocument2BlockById(document: Document2Node, blockId: string): Document2Node;
311
544
  declare function addDocument2ListBlock(document: Document2Node, ordered: boolean, afterBlockId?: string | null): Document2Node;
312
545
  declare function addDocument2TableBlock(document: Document2Node, columns?: string, rowCount?: number, colCount?: number, afterBlockId?: string | null): Document2Node;
313
546
  declare function addDocument2ImageBlock(document: Document2Node, src?: string, afterBlockId?: string | null): Document2Node;
314
547
  declare function updateDocument2ImageValue(document: Document2Node, blockId: string, value: string): Document2Node;
548
+ declare function updateDocument2ImageMeta(document: Document2Node, blockId: string, patch: FloatMetaPatch): Document2Node;
549
+ declare function updateDocument2TableMeta(document: Document2Node, blockId: string, patch: FloatMetaPatch): Document2Node;
550
+ declare function updateMathTokenLabel(document: Document2Node, tokenId: string, patch: {
551
+ labelEnabled?: boolean;
552
+ label?: string;
553
+ }): Document2Node;
554
+ declare function insertRefTokenAtCaret(document: Document2Node, fieldId: string, textTokenId: string | null, caretOffset: number, keys: string[], refCommand?: 'ref' | 'eqref'): Document2Node;
555
+ declare function updateRefTokenKeys(document: Document2Node, tokenId: string, keys: string[], refCommand?: 'ref' | 'eqref'): Document2Node;
556
+ declare function removeRefTokenById(document: Document2Node, tokenId: string): Document2Node;
315
557
  declare function addDocument2ListItem(document: Document2Node, listBlockId: string): Document2Node;
316
558
  declare function removeDocument2ListItem(document: Document2Node, listBlockId: string, itemId: string): Document2Node;
559
+ declare function insertCiteTokenAtCaret(document: Document2Node, fieldId: string, textTokenId: string | null, caretOffset: number, keys: string[]): Document2Node;
560
+ declare function updateCiteTokenKeys(document: Document2Node, tokenId: string, keys: string[]): Document2Node;
561
+ declare function removeCiteTokenById(document: Document2Node, tokenId: string): Document2Node;
562
+ declare function ensureDocument2BibliographyBlock(document: Document2Node, afterBlockId?: string | null): Document2Node;
563
+ declare function addDocument2Reference(document: Document2Node, partial?: Partial<Reference2Json>): Document2Node;
564
+ declare function updateDocument2Reference(document: Document2Node, referenceId: string, patch: Partial<Reference2Json>): Document2Node;
565
+ declare function removeDocument2Reference(document: Document2Node, referenceId: string): Document2Node;
566
+ declare function moveDocument2Reference(document: Document2Node, referenceId: string, direction: -1 | 1): Document2Node;
567
+ declare function setDocument2References(document: Document2Node, references: Reference2[]): Document2Node;
568
+ declare function updateDocument2Meta(document: Document2Node, patch: Document2MetaProp): Document2Node;
569
+
570
+ declare function referenceNumberMap(references: Array<Pick<Reference2, 'key'>>): Map<string, number>;
571
+ declare function resolveCiteNumbers(keys: string[], references: Array<Pick<Reference2, 'key'>>): Array<number | null>;
572
+ declare function formatCiteLabel(keys: string[], references: Array<Pick<Reference2, 'key'>>, options?: FormatCiteLabelOptions): string;
573
+ declare function formatBibliographyNumber(index: number, options?: FormatCiteLabelOptions): string;
574
+ declare function parseCiteKeys(source: string): string[];
575
+ declare function citeTokenLatex(keys: string[]): string;
576
+ declare function referenceFromJson(json: Reference2Json): Reference2;
577
+ declare function createEmptyReference2(partial?: Partial<Reference2Json>): Reference2;
578
+ declare function bibliographyEntryLatex(reference: Reference2): string;
317
579
 
580
+ /** Arabic XeLaTeX preamble fragments for full-document export. */
581
+ type ArabicDigitsMapping = 'arabicdigits' | 'digits';
582
+ type ArabicXeLatexPreambleOptions = {
583
+ digitsMapping?: ArabicDigitsMapping;
584
+ /** When true, add the article class twocolumn option. Default false. */
585
+ twocolumn?: boolean;
586
+ };
587
+ declare function arabicXeLatexPreamblePkg(twocolumn?: boolean): string;
588
+ declare function arabicXeLatexPreambleFont(digitsMapping?: ArabicDigitsMapping): string;
589
+ declare function arabicXeLatexPreambleCmd(): string;
590
+ declare function getArabicXeLatexPreamble(digitsMappingOrOptions?: ArabicDigitsMapping | ArabicXeLatexPreambleOptions): string;
591
+
592
+ type Document2LatexOptions = {
593
+ /**
594
+ * When true (default), emit a complete Arabic XeLaTeX file:
595
+ * preamble + \\begin{document} + body + \\end{document}.
596
+ * Pass false for body-only blocks.
597
+ */
598
+ wrapDocument?: boolean;
599
+ /** Font digit Mapping when wrapping. Default 'arabicdigits'. */
600
+ digitsMapping?: ArabicDigitsMapping;
601
+ /** When true, use article class option twocolumn. Default false. */
602
+ twocolumn?: boolean;
603
+ };
318
604
  declare function inlineFieldLatex(field: InlineField2): string;
319
- declare function document2Latex(document: Document2Node): string;
605
+ declare function document2Latex(document: Document2Node, options?: Document2LatexOptions): string;
320
606
 
321
607
  declare const DEFAULT_DOCUMENT2_HISTORY_MAX_DEPTH = 100;
322
608
  type Document2HistoryStacks = {
@@ -331,11 +617,30 @@ declare function restoreDocument2Redo(stacks: Document2HistoryStacks, documentNo
331
617
  declare function document2HistoryCanUndo(stacks: Document2HistoryStacks): boolean;
332
618
  declare function document2HistoryCanRedo(stacks: Document2HistoryStacks): boolean;
333
619
 
620
+ type BlockSelectionRange = {
621
+ from: number;
622
+ to: number;
623
+ };
624
+ type BlockSelectionState = {
625
+ selection: BlockSelectionRange | null;
626
+ anchor: number | null;
627
+ };
628
+ declare function emptyBlockSelection(): BlockSelectionState;
629
+ declare function normalizeBlockSelection(selection: BlockSelectionRange | null, blockCount: number): BlockSelectionRange | null;
630
+ /** Checkbox click without Shift */
631
+ declare function toggleBlockInSelection(state: BlockSelectionState, index: number, blockCount: number): BlockSelectionState;
632
+ /** Shift+click on checkbox or header label */
633
+ declare function extendBlockSelectionFromAnchor(state: BlockSelectionState, index: number, blockCount: number): BlockSelectionState;
634
+ declare function remapSelectionAfterRangeMove(selection: BlockSelectionRange, direction: -1 | 1): BlockSelectionRange;
635
+
334
636
  declare function createInlineField2(value?: string, mathObjects?: MathObject2Json[], options?: ImportDocument2Options, path?: string, diagnostics?: Document2Diagnostic[]): InlineField2;
335
637
  declare function fromDocumentJson2(json: unknown, options?: ImportDocument2Options): Document2Node;
336
638
  declare function createEmptyDocument2(): Document2Node;
337
639
 
640
+ /** Math-only spans (used for math_objects alignment counts). */
338
641
  declare function detectMathSpans2(value: string): DetectedMathSpan2[];
642
+ /** Left-to-right math + \\cite + \\ref/\\eqref spans without overlap. */
643
+ declare function detectInlineSpans2(value: string): DetectedInlineSpan2[];
339
644
 
340
645
  type Document2MathBridgeResult = {
341
646
  editable: true;
@@ -354,6 +659,11 @@ declare function mathNodeFromSession(session: EditorSession, opening?: string, c
354
659
  declare function mathNodeFromArabicSession(session: EditorSession, opening?: string, closing?: string): MathNode;
355
660
  declare function mathTokenSourceForSide(token: MathToken2, side: EditorSide): string;
356
661
 
357
- declare function document2Preview(document: Document2Node, output?: Document2MathOutput, equationSide?: EditorSide): Document2Preview;
662
+ type Document2PreviewOptions = {
663
+ documentDirection?: Document2Direction;
664
+ digitForm?: DigitFormId;
665
+ uiLocale?: ButexUiLocale;
666
+ };
667
+ declare function document2Preview(document: Document2Node, output?: Document2MathOutput, equationSide?: EditorSide, previewOptions?: Document2PreviewOptions): Document2Preview;
358
668
 
359
- export { DEFAULT_DOCUMENT2_HISTORY_MAX_DEPTH, type Document2Block, type Document2BlockJson, type Document2Diagnostic, type Document2HistoryStacks, type Document2Json, type Document2MathOutput, type Document2Node, type ImageBlock2, type ImportDocument2Options, type InlineField2, type InlineToken2, type ListBlock2, type MathIsland2, type MathObject2Json, type MathToken2, type PreviewBlock2, type PreviewInline2, type RawBlock2, type TableBlock2, type TextBlock2, type TextToken2, addDocument2ImageBlock, addDocument2ListBlock, addDocument2ListItem, addDocument2TableBlock, addDocument2TextBlock, cloneDocument2Node, createDocument2History, createEmptyArabicEquationSession, createEmptyDocument2, createEmptyEquationSession, createInlineField2, detectMathSpans2, document2HistoryCanRedo, document2HistoryCanUndo, document2Latex, document2Preview, findFirstInlineField, fromDocumentJson2, inlineFieldLatex, insertDocument2BlockAfter, insertMathToken, insertMathTokenAtCaret, mathNodeFromArabicSession, mathNodeFromSession, mathSourceFromArabicSession, mathSourceFromSession, mathTokenSourceForSide, mathTokenToArabicEditorSession, mathTokenToEditorSession, moveDocument2BlockById, pushDocument2Snapshot, removeDocument2BlockById, removeDocument2ListItem, removeMathTokenById, replaceMathTokenFromSession, restoreDocument2Redo, restoreDocument2Undo, updateDocument2ImageValue, updateTextToken };
669
+ export { type ArabicDigitsMapping, type ArabicXeLatexPreambleOptions, BUTEX_BASMALA, BUTEX_CLOSING_HAMDALA, type BibliographyBlock2, type BlockSelectionRange, type BlockSelectionState, type CiteToken2, DEFAULT_DOCUMENT2_HISTORY_MAX_DEPTH, DEFAULT_HIJRI_DATE, type Document2Block, type Document2BlockJson, type Document2Diagnostic, type Document2Direction, type Document2HijriDate, type Document2HistoryStacks, type Document2Json, type Document2LabelEntry, type Document2LabelKind, type Document2LatexOptions, type Document2MathOutput, type Document2Meta, type Document2MetaProp, type Document2Node, type Document2Preview, type Document2PreviewOptions, type FloatMeta2, type FloatMetaPatch, type FormatCiteLabelOptions, HIJRI_MONTH_IDS, HIJRI_YEAR_MAX, HIJRI_YEAR_MIN, type HijriMonthId, type ImageBlock2, type ImportDocument2Options, type InlineField2, type InlineToken2, type ListBlock2, type MathIsland2, type MathObject2Json, type MathToken2, type PreviewBlock2, type PreviewInline2, type RawBlock2, type RefToken2, type Reference2, type Reference2Json, type TableBlock2, type TextBlock2, type TextToken2, addDocument2ImageBlock, addDocument2ListBlock, addDocument2ListItem, addDocument2Reference, addDocument2TableBlock, addDocument2TextBlock, applyFloatMetaPatch, arabicXeLatexPreambleCmd, arabicXeLatexPreambleFont, arabicXeLatexPreamblePkg, bibliographyEntryLatex, butexDiwaniDisplayLatex, butexDiwaniDisplayTex, citeTokenLatex, cloneDocument2Meta, cloneDocument2Node, collectDocument2Labels, createDocument2History, createEmptyArabicEquationSession, createEmptyDocument2, createEmptyEquationSession, createEmptyReference2, createInlineField2, defaultFloatMeta, detectInlineSpans2, detectMathSpans2, document2HasAbstract, document2HasMaketitle, document2HasTitleStack, document2HistoryCanRedo, document2HistoryCanUndo, document2Latex, document2Preview, emptyBlockSelection, emptyDocument2Meta, ensureDocument2BibliographyBlock, extendBlockSelectionFromAnchor, fillDocument2MetaGaps, findFirstInlineField, formatBibliographyNumber, formatCiteLabel, formatHijriDate, formatRefLabel, fromDocumentJson2, getArabicXeLatexPreamble, hijriMonthLabel, inlineFieldLatex, insertCiteTokenAtCaret, insertDocument2BlockAfter, insertMathToken, insertMathTokenAtCaret, insertRefTokenAtCaret, labelNumberMap, mathNodeFromArabicSession, mathNodeFromSession, mathSourceFromArabicSession, mathSourceFromSession, mathTokenSourceForSide, mathTokenToArabicEditorSession, mathTokenToEditorSession, mergeDocument2Meta, moveDocument2BlockById, moveDocument2BlockRange, moveDocument2Reference, normalizeBlockSelection, normalizeDocument2HijriDate, normalizeDocument2Meta, parseCiteKeys, parseFloatMetaFromJson, parseRefKeys, pushDocument2Snapshot, refTokenLatex, referenceFromJson, referenceNumberMap, remapSelectionAfterRangeMove, removeCiteTokenById, removeDocument2BlockById, removeDocument2BlockRange, removeDocument2ListItem, removeDocument2Reference, removeMathTokenById, removeRefTokenById, replaceMathTokenFromSession, resolveCiteNumbers, restoreDocument2Redo, restoreDocument2Undo, setDocument2References, stripDisplayMathDelimiters, toggleBlockInSelection, updateCiteTokenKeys, updateDocument2ImageMeta, updateDocument2ImageValue, updateDocument2Meta, updateDocument2Reference, updateDocument2TableMeta, updateDocument2TextBlockCentered, updateMathTokenLabel, updateRefTokenKeys, updateTextToken };