@drghaliasri/butex 5.5.1 → 5.5.3
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 +69 -3
- package/dist/document.d.mts +5 -4
- package/dist/document.d.ts +5 -4
- package/dist/document.js +64 -2
- package/dist/document.js.map +1 -1
- package/dist/document.mjs +63 -2
- package/dist/document.mjs.map +1 -1
- package/dist/document2.d.mts +20 -4
- package/dist/document2.d.ts +20 -4
- package/dist/document2.js +227 -11
- package/dist/document2.js.map +1 -1
- package/dist/document2.mjs +226 -11
- package/dist/document2.mjs.map +1 -1
- package/dist/index.global.js +1 -1
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/index.mjs.map +1 -1
- package/dist/react-document.d.mts +2 -2
- package/dist/react-document.d.ts +2 -2
- package/dist/react-document.js +3 -3
- package/dist/react-document.js.map +1 -1
- package/dist/react-document.mjs +3 -3
- package/dist/react-document.mjs.map +1 -1
- package/dist/react-document2.d.mts +18 -3
- package/dist/react-document2.d.ts +18 -3
- package/dist/react-document2.js +252 -12
- package/dist/react-document2.js.map +1 -1
- package/dist/react-document2.mjs +252 -12
- package/dist/react-document2.mjs.map +1 -1
- package/dist/react.js +1 -1
- package/dist/react.js.map +1 -1
- package/dist/react.mjs +1 -1
- package/dist/react.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -19,8 +19,8 @@ The intended editor model is **AST-first**:
|
|
|
19
19
|
## Rendering outputs and MVP scope
|
|
20
20
|
|
|
21
21
|
- **CommonHTML (`chtml`)** and **SVG** are supported. Load the matching MathJax 4 bundle (`tex-chtml.js` vs `tex-svg.js`) and pass the same mode as `output` to `renderBuTeXMathIsland` / `mountBuTeXMathIsland`. `<ButexEditor />` and document math preview follow the loaded bundle when possible (`tex2chtml` vs `tex2svg`).
|
|
22
|
-
- `\
|
|
23
|
-
- Compatibility macros: `\
|
|
22
|
+
- `\arsqrt`: canonical serialized Arabic TeX command with an optional index and mandatory radicand (same argument shape as `\sqrt`).
|
|
23
|
+
- Compatibility macros: MathJax accepts `\arabsqrt` as an alias of `\arsqrt`; `\unit{...}` and `\idx{...}` are browser-side passthrough wrappers for imported/reference TeX.
|
|
24
24
|
- **Arabic surface parser** (`parseBuTeX`) — optional helper that maps a tiny subset of Arabic command names to MathJax-safe TeX before rendering (see below). Long-term editor state is structured AST/JSON, not this string transform alone.
|
|
25
25
|
|
|
26
26
|
## Arabic preprocessor (`parseBuTeX`)
|
|
@@ -254,7 +254,7 @@ const latex = document2Latex(documentNode);
|
|
|
254
254
|
const preview = document2Preview(documentNode, 'svg');
|
|
255
255
|
```
|
|
256
256
|
|
|
257
|
-
`document2Latex(doc)` emits a **complete Arabic XeLaTeX document** by default (preamble + `\begin{document}` + body + `\end{document}`). Pass `{ wrapDocument: false }` for body-only blocks. Paragraph blocks export as `\par` followed by the inline content, so prose, citations, and math are not wrapped inside `\paragraph{...}`. Optional `digitsMapping: 'arabicdigits' | 'digits'` controls eastern vs western digit font mapping in the preamble (default `'arabicdigits'`). Pass `{ twocolumn: true }` for `\documentclass[12pt,a4paper,notitlepage,twocolumn]{article}` (title block stays in-column; figures use `\columnwidth`). 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
|
+
`document2Latex(doc)` emits a **complete Arabic XeLaTeX document** by default (preamble + `\begin{document}` + body + `\end{document}`). Structured and editor-authored Arabic roots serialize with the canonical `\arsqrt`; the bundled preamble also accepts raw legacy `\arabsqrt` with the same optional-index signature. Pass `{ wrapDocument: false }` for body-only blocks. Paragraph blocks export as `\par` followed by the inline content, so prose, citations, and math are not wrapped inside `\paragraph{...}`. Optional `digitsMapping: 'arabicdigits' | 'digits'` controls eastern vs western digit font mapping in the preamble (default `'arabicdigits'`). Pass `{ twocolumn: true }` for `\documentclass[12pt,a4paper,notitlepage,twocolumn]{article}` (title block stays in-column; figures use `\columnwidth`). 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.
|
|
258
258
|
|
|
259
259
|
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`).
|
|
260
260
|
|
|
@@ -271,6 +271,7 @@ Rules:
|
|
|
271
271
|
- **Per field, not per document.** Each paragraph, heading, and list `item` carries its own `math_objects`.
|
|
272
272
|
- **Tables are the exception:** one flat `math_objects` array lives on the `\begin{tabular}` block and is consumed **row-major** across all cells (each cell takes as many entries as it has spans).
|
|
273
273
|
- **Missing `math_objects`** → the span renders as a non-editable raw math chip from its delimited source.
|
|
274
|
+
- **Mixed raw and structured math** uses `null` placeholders so every detected span still has one positional entry, for example `math_objects: [null, structuredMathObject]`. A labeled raw display span uses a `RawMathObject` positional metadata entry so its label survives without pretending the equation has an editable AST.
|
|
274
275
|
- The browser **does not** parse equation LaTeX bodies into `ChainClass`; structured chains come from `math_objects` (import) or the equation editor (GUI).
|
|
275
276
|
|
|
276
277
|
Paragraph with one inline span:
|
|
@@ -337,6 +338,57 @@ export default function Page() {
|
|
|
337
338
|
}
|
|
338
339
|
```
|
|
339
340
|
|
|
341
|
+
#### Persisting Document Editor v2 state
|
|
342
|
+
|
|
343
|
+
`Document2Node` is the live editor AST. It contains class-backed equation nodes and must not cross an API, database, local-storage, worker, or `JSON.stringify` boundary. Persist the canonical `Document2Json` wire format instead. The backend may store and return that JSON unchanged.
|
|
344
|
+
|
|
345
|
+
Use `onDocumentJsonChange` for a persistence-ready snapshot:
|
|
346
|
+
|
|
347
|
+
```tsx
|
|
348
|
+
'use client';
|
|
349
|
+
|
|
350
|
+
import { useRef } from 'react';
|
|
351
|
+
import { ButexDocumentEditor2 } from '@drghaliasri/butex/react-document2';
|
|
352
|
+
import type { Document2Json } from '@drghaliasri/butex/document2';
|
|
353
|
+
|
|
354
|
+
export function ArticleEditor({ loadedDocument }: { loadedDocument: Document2Json }) {
|
|
355
|
+
const latestDocument = useRef<Document2Json>(loadedDocument);
|
|
356
|
+
|
|
357
|
+
async function saveArticle() {
|
|
358
|
+
await fetch('/api/article', {
|
|
359
|
+
method: 'PUT',
|
|
360
|
+
headers: { 'content-type': 'application/json' },
|
|
361
|
+
body: JSON.stringify({ document: latestDocument.current }),
|
|
362
|
+
});
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
return (
|
|
366
|
+
<>
|
|
367
|
+
<ButexDocumentEditor2
|
|
368
|
+
initialDocument={loadedDocument}
|
|
369
|
+
editableEquations
|
|
370
|
+
onDocumentJsonChange={(json) => {
|
|
371
|
+
latestDocument.current = json;
|
|
372
|
+
}}
|
|
373
|
+
/>
|
|
374
|
+
<button type="button" onClick={saveArticle}>Save</button>
|
|
375
|
+
</>
|
|
376
|
+
);
|
|
377
|
+
}
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
`onDocumentChange` remains available for hosts that need the live AST. Convert it before persistence with `toDocumentJson2`:
|
|
381
|
+
|
|
382
|
+
```ts
|
|
383
|
+
import { toDocumentJson2 } from '@drghaliasri/butex/document2';
|
|
384
|
+
|
|
385
|
+
onDocumentChange={(node) => {
|
|
386
|
+
latestDocument.current = toDocumentJson2(node);
|
|
387
|
+
}}
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
Do not call `JSON.stringify(documentNode)` directly. JSON serialization removes `CharNode`, `CommandNode`, and other equation prototypes: the saved TeX source can still render in preview, but the structured equation cannot reopen in the editor. Also do not feed every change callback back into `initialDocument`; that prop is for initial or externally loaded data, and changing it resets the editor state and undo history. Keep the current save snapshot in a ref or separate state that is not passed back as `initialDocument`. Change callbacks run during editing, so debounce network autosaves or save explicitly.
|
|
391
|
+
|
|
340
392
|
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"`.
|
|
341
393
|
|
|
342
394
|
### Styling/theming contract
|
|
@@ -346,6 +398,20 @@ Host apps still register BuTeX with MathJax before preview rendering. `uiLocale`
|
|
|
346
398
|
- Document editor theming is scoped under `.butex-document-widget`. Override `--butex-document-*` variables on that root or an ancestor, especially `--butex-document-bg`, `--butex-document-fg`, `--butex-document-panel`, `--butex-document-border`, `--butex-document-accent`, `--butex-document-preview-bg`, `--butex-document-drawer-bg`, `--butex-document-input-bg`, `--butex-document-table-border`, `--butex-document-math-bg`, and `--butex-document-dev-bg`.
|
|
347
399
|
- Document editor v2 theming is scoped under `.butex-document2-widget`. Override `--butex-document2-*` variables on that root or an ancestor; defaults inherit from the existing `--butex-*` variables where practical, including accent, panel, border, preview, focus, danger/error, and debug colors. The embedded equation editor opens as a centered modal (backdrop + panel); its BuTeX chrome reads the same `--butex-document2-*` tokens via a scoped bridge, so hosts normally theme once on `.butex-document2-widget` without a second equation theme. The built-in editor uses a **sticky compact icon toolbar** (Arabic `نص` formatting icons, heading glyphs, inline `$…$` vs display `\[…\]` math buttons with tooltips, a labeled references menu, and a table size popover), **focus-aware block insertion** after the active block, **↑/↓ block reorder**, **math delete** (chip × and drawer button), **document undo/redo** (toolbar + Ctrl/⌘Z, Ctrl/⌘Shift+Z, Ctrl+Y) via AST snapshots, and theme-tinted block cards. Preview tables use fixed black cell borders; raw `\raw` blocks stay out of preview until dedicated rendering exists.
|
|
348
400
|
|
|
401
|
+
#### Keeping the Document Editor v2 toolbar sticky
|
|
402
|
+
|
|
403
|
+
`<ButexDocumentEditor2 />` injects `style#butex-document2-widget-styles`; its main toolbar ships with `position: sticky`, `top: 0`, and a stacking layer. If a strict CSP blocks injected inline styles, embed `DOCUMENT2_WIDGET_CSS` through the host's approved global stylesheet or nonced style path, after Tailwind Preflight or other resets.
|
|
404
|
+
|
|
405
|
+
Do not override `.butex-document2-widget__toolbar` with `position: static`, `position: fixed`, a lower stacking layer, or an unrelated `top`. Avoid `overflow: auto`, `scroll`, or `hidden` on editor ancestors unless that ancestor is intentionally the constrained vertical scrolling container; sticky positioning follows the nearest such ancestor. `overflow-x: clip` by itself does not require an override. For a fixed application header, keep the toolbar sticky and apply only a scoped offset:
|
|
406
|
+
|
|
407
|
+
```css
|
|
408
|
+
.article-editor .butex-document2-widget__toolbar {
|
|
409
|
+
top: var(--app-header-height, 0px);
|
|
410
|
+
}
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
When diagnosing host CSS, verify that `#butex-document2-widget-styles` exists and that `getComputedStyle(toolbar).position` is `sticky` before adding overrides.
|
|
414
|
+
|
|
349
415
|
## Demo
|
|
350
416
|
|
|
351
417
|
From repo root (after `npm run build`):
|
package/dist/document.d.mts
CHANGED
|
@@ -60,7 +60,7 @@ type DocumentJson = {
|
|
|
60
60
|
type DocumentBlockJson = {
|
|
61
61
|
command: string;
|
|
62
62
|
value?: string;
|
|
63
|
-
math_objects?: MathObjectJson
|
|
63
|
+
math_objects?: Array<MathObjectJson | null>;
|
|
64
64
|
options?: Record<string, string>;
|
|
65
65
|
closing?: string;
|
|
66
66
|
items?: DocumentListItemJson[];
|
|
@@ -69,7 +69,7 @@ type DocumentBlockJson = {
|
|
|
69
69
|
};
|
|
70
70
|
type DocumentListItemJson = {
|
|
71
71
|
value: string;
|
|
72
|
-
math_objects?: MathObjectJson
|
|
72
|
+
math_objects?: Array<MathObjectJson | null>;
|
|
73
73
|
blocks?: DocumentBlockJson[];
|
|
74
74
|
};
|
|
75
75
|
type DocumentCommand = '\\section' | '\\subsection' | '\\subsubsection' | '\\paragraph' | '\\begin{itemize}' | '\\begin{enumerate}' | '\\begin{tabular}' | '\\includegraphics' | '\\raw';
|
|
@@ -156,6 +156,7 @@ declare function createImageBlock(value?: string, options?: Record<string, strin
|
|
|
156
156
|
declare function isSupportedDocumentCommand(command: string): command is DocumentCommand;
|
|
157
157
|
|
|
158
158
|
declare function fromMathObjectJson(json: unknown, mode?: DocumentParseMode): MathNode;
|
|
159
|
+
declare function toMathObjectJson(math: MathNode): MathObjectJson;
|
|
159
160
|
declare function renderMathNodeLatex(math: MathNode): string;
|
|
160
161
|
declare function isDisplayMathNode(math: MathNode): boolean;
|
|
161
162
|
|
|
@@ -250,7 +251,7 @@ declare function mathNodeFromEditorSession(session: EditorSession, mathMode?: st
|
|
|
250
251
|
declare function mathSourceFromEditorSession(session: EditorSession, mathMode?: string, closing?: string): string;
|
|
251
252
|
|
|
252
253
|
declare function detectMathSpans(value: string): DetectedMathSpan[];
|
|
253
|
-
declare function buildInlines(value: string, mathObjects?: MathObjectJson
|
|
254
|
+
declare function buildInlines(value: string, mathObjects?: Array<MathObjectJson | null>, mode?: DocumentParseMode): DocumentInline[];
|
|
254
255
|
|
|
255
256
|
declare function renderDocumentLatex(document: DocumentNode): string;
|
|
256
257
|
declare function buildDocumentPreview(document: DocumentNode): DocumentPreview;
|
|
@@ -307,4 +308,4 @@ declare function replaceMathSpanFromEditor(document: DocumentNode, target: Docum
|
|
|
307
308
|
declare function insertMathSpanFromEditor(document: DocumentNode, target: DocumentInlineTarget, insertAt: number, math: MathNode, source?: string): DocumentNode;
|
|
308
309
|
declare function documentInlineText(inlines: DocumentInline[]): string;
|
|
309
310
|
|
|
310
|
-
export { type DetectedMathSpan, type DocumentBlockJson, type DocumentBlockNode, type DocumentCommand, type DocumentInline, type DocumentInlineTarget, type DocumentJson, type DocumentListItemJson, type DocumentMathTarget, type DocumentNode, type DocumentParseMode, type DocumentPreview, type DocumentPreviewMathIsland, type ImageBlockNode, type ListBlockNode, type ListItemNode, type MathInline, type MathNode, type MathObjectJson, type NewDocumentBlockKind, type RawBlockNode, type TableBlockNode, type TextBlockNode, type TextInline, addDocumentBlock, addListItem, addNestedListBlock, addTableColumn, addTableRow, buildDocumentPreview, buildInlines, createDocumentBlock, createEmptyDocument, createImageBlock, createListBlock, createRawBlock, createTableBlock, createTextBlock, detectMathSpans, documentInlineText, fromDocumentJson, fromMathObjectJson, insertMathSpanFromEditor, isDisplayMathNode, isSupportedDocumentCommand, mathNodeFromEditorSession, mathSourceFromEditorSession, moveDocumentBlock, removeDocumentBlock, removeListItem, removeTableColumn, removeTableRow, renderDocumentLatex, renderMathNodeLatex, replaceMathSpanFromEditor, replaceMathSpanSource, updateImageBlock, updateListItemValue, updateRawBlockValue, updateTableCell, updateTableColumns, updateTextBlockValue };
|
|
311
|
+
export { type DetectedMathSpan, type DocumentBlockJson, type DocumentBlockNode, type DocumentCommand, type DocumentInline, type DocumentInlineTarget, type DocumentJson, type DocumentListItemJson, type DocumentMathTarget, type DocumentNode, type DocumentParseMode, type DocumentPreview, type DocumentPreviewMathIsland, type ImageBlockNode, type ListBlockNode, type ListItemNode, type MathInline, type MathNode, type MathObjectJson, type NewDocumentBlockKind, type RawBlockNode, type TableBlockNode, type TextBlockNode, type TextInline, addDocumentBlock, addListItem, addNestedListBlock, addTableColumn, addTableRow, buildDocumentPreview, buildInlines, createDocumentBlock, createEmptyDocument, createImageBlock, createListBlock, createRawBlock, createTableBlock, createTextBlock, detectMathSpans, documentInlineText, fromDocumentJson, fromMathObjectJson, insertMathSpanFromEditor, isDisplayMathNode, isSupportedDocumentCommand, mathNodeFromEditorSession, mathSourceFromEditorSession, moveDocumentBlock, removeDocumentBlock, removeListItem, removeTableColumn, removeTableRow, renderDocumentLatex, renderMathNodeLatex, replaceMathSpanFromEditor, replaceMathSpanSource, toMathObjectJson, updateImageBlock, updateListItemValue, updateRawBlockValue, updateTableCell, updateTableColumns, updateTextBlockValue };
|
package/dist/document.d.ts
CHANGED
|
@@ -60,7 +60,7 @@ type DocumentJson = {
|
|
|
60
60
|
type DocumentBlockJson = {
|
|
61
61
|
command: string;
|
|
62
62
|
value?: string;
|
|
63
|
-
math_objects?: MathObjectJson
|
|
63
|
+
math_objects?: Array<MathObjectJson | null>;
|
|
64
64
|
options?: Record<string, string>;
|
|
65
65
|
closing?: string;
|
|
66
66
|
items?: DocumentListItemJson[];
|
|
@@ -69,7 +69,7 @@ type DocumentBlockJson = {
|
|
|
69
69
|
};
|
|
70
70
|
type DocumentListItemJson = {
|
|
71
71
|
value: string;
|
|
72
|
-
math_objects?: MathObjectJson
|
|
72
|
+
math_objects?: Array<MathObjectJson | null>;
|
|
73
73
|
blocks?: DocumentBlockJson[];
|
|
74
74
|
};
|
|
75
75
|
type DocumentCommand = '\\section' | '\\subsection' | '\\subsubsection' | '\\paragraph' | '\\begin{itemize}' | '\\begin{enumerate}' | '\\begin{tabular}' | '\\includegraphics' | '\\raw';
|
|
@@ -156,6 +156,7 @@ declare function createImageBlock(value?: string, options?: Record<string, strin
|
|
|
156
156
|
declare function isSupportedDocumentCommand(command: string): command is DocumentCommand;
|
|
157
157
|
|
|
158
158
|
declare function fromMathObjectJson(json: unknown, mode?: DocumentParseMode): MathNode;
|
|
159
|
+
declare function toMathObjectJson(math: MathNode): MathObjectJson;
|
|
159
160
|
declare function renderMathNodeLatex(math: MathNode): string;
|
|
160
161
|
declare function isDisplayMathNode(math: MathNode): boolean;
|
|
161
162
|
|
|
@@ -250,7 +251,7 @@ declare function mathNodeFromEditorSession(session: EditorSession, mathMode?: st
|
|
|
250
251
|
declare function mathSourceFromEditorSession(session: EditorSession, mathMode?: string, closing?: string): string;
|
|
251
252
|
|
|
252
253
|
declare function detectMathSpans(value: string): DetectedMathSpan[];
|
|
253
|
-
declare function buildInlines(value: string, mathObjects?: MathObjectJson
|
|
254
|
+
declare function buildInlines(value: string, mathObjects?: Array<MathObjectJson | null>, mode?: DocumentParseMode): DocumentInline[];
|
|
254
255
|
|
|
255
256
|
declare function renderDocumentLatex(document: DocumentNode): string;
|
|
256
257
|
declare function buildDocumentPreview(document: DocumentNode): DocumentPreview;
|
|
@@ -307,4 +308,4 @@ declare function replaceMathSpanFromEditor(document: DocumentNode, target: Docum
|
|
|
307
308
|
declare function insertMathSpanFromEditor(document: DocumentNode, target: DocumentInlineTarget, insertAt: number, math: MathNode, source?: string): DocumentNode;
|
|
308
309
|
declare function documentInlineText(inlines: DocumentInline[]): string;
|
|
309
310
|
|
|
310
|
-
export { type DetectedMathSpan, type DocumentBlockJson, type DocumentBlockNode, type DocumentCommand, type DocumentInline, type DocumentInlineTarget, type DocumentJson, type DocumentListItemJson, type DocumentMathTarget, type DocumentNode, type DocumentParseMode, type DocumentPreview, type DocumentPreviewMathIsland, type ImageBlockNode, type ListBlockNode, type ListItemNode, type MathInline, type MathNode, type MathObjectJson, type NewDocumentBlockKind, type RawBlockNode, type TableBlockNode, type TextBlockNode, type TextInline, addDocumentBlock, addListItem, addNestedListBlock, addTableColumn, addTableRow, buildDocumentPreview, buildInlines, createDocumentBlock, createEmptyDocument, createImageBlock, createListBlock, createRawBlock, createTableBlock, createTextBlock, detectMathSpans, documentInlineText, fromDocumentJson, fromMathObjectJson, insertMathSpanFromEditor, isDisplayMathNode, isSupportedDocumentCommand, mathNodeFromEditorSession, mathSourceFromEditorSession, moveDocumentBlock, removeDocumentBlock, removeListItem, removeTableColumn, removeTableRow, renderDocumentLatex, renderMathNodeLatex, replaceMathSpanFromEditor, replaceMathSpanSource, updateImageBlock, updateListItemValue, updateRawBlockValue, updateTableCell, updateTableColumns, updateTextBlockValue };
|
|
311
|
+
export { type DetectedMathSpan, type DocumentBlockJson, type DocumentBlockNode, type DocumentCommand, type DocumentInline, type DocumentInlineTarget, type DocumentJson, type DocumentListItemJson, type DocumentMathTarget, type DocumentNode, type DocumentParseMode, type DocumentPreview, type DocumentPreviewMathIsland, type ImageBlockNode, type ListBlockNode, type ListItemNode, type MathInline, type MathNode, type MathObjectJson, type NewDocumentBlockKind, type RawBlockNode, type TableBlockNode, type TextBlockNode, type TextInline, addDocumentBlock, addListItem, addNestedListBlock, addTableColumn, addTableRow, buildDocumentPreview, buildInlines, createDocumentBlock, createEmptyDocument, createImageBlock, createListBlock, createRawBlock, createTableBlock, createTextBlock, detectMathSpans, documentInlineText, fromDocumentJson, fromMathObjectJson, insertMathSpanFromEditor, isDisplayMathNode, isSupportedDocumentCommand, mathNodeFromEditorSession, mathSourceFromEditorSession, moveDocumentBlock, removeDocumentBlock, removeListItem, removeTableColumn, removeTableRow, renderDocumentLatex, renderMathNodeLatex, replaceMathSpanFromEditor, replaceMathSpanSource, toMathObjectJson, updateImageBlock, updateListItemValue, updateRawBlockValue, updateTableCell, updateTableColumns, updateTextBlockValue };
|
package/dist/document.js
CHANGED
|
@@ -52,6 +52,7 @@ __export(document_exports, {
|
|
|
52
52
|
renderMathNodeLatex: () => renderMathNodeLatex,
|
|
53
53
|
replaceMathSpanFromEditor: () => replaceMathSpanFromEditor,
|
|
54
54
|
replaceMathSpanSource: () => replaceMathSpanSource,
|
|
55
|
+
toMathObjectJson: () => toMathObjectJson,
|
|
55
56
|
updateImageBlock: () => updateImageBlock,
|
|
56
57
|
updateListItemValue: () => updateListItemValue,
|
|
57
58
|
updateRawBlockValue: () => updateRawBlockValue,
|
|
@@ -227,7 +228,7 @@ var CommandNode = class extends BaseAstNode {
|
|
|
227
228
|
arabicLatex() {
|
|
228
229
|
const sup = this.superscript !== null ? this.superscript.arabicLatex() : "";
|
|
229
230
|
const sub = this.subscript !== null ? this.subscript.arabicLatex() : "";
|
|
230
|
-
const arabicName = this.name === "\\sqrt"
|
|
231
|
+
const arabicName = this.name === "\\sqrt" || this.name === "\\arabsqrt" ? "\\arsqrt" : this.name;
|
|
231
232
|
const content = renderCommandCore({
|
|
232
233
|
name: arabicName,
|
|
233
234
|
optionalArgs: this.optionalArgs,
|
|
@@ -409,6 +410,66 @@ function fromMathObjectJson(json, mode = "english") {
|
|
|
409
410
|
closing: json.closing
|
|
410
411
|
};
|
|
411
412
|
}
|
|
413
|
+
function scriptsToJson(node, path) {
|
|
414
|
+
return {
|
|
415
|
+
...node.superscript ? { superscript: chainToJson(node.superscript, `${path}.superscript`) } : {},
|
|
416
|
+
...node.subscript ? { subscript: chainToJson(node.subscript, `${path}.subscript`) } : {}
|
|
417
|
+
};
|
|
418
|
+
}
|
|
419
|
+
function nodeToJson(node, path) {
|
|
420
|
+
const scripts = scriptsToJson(node, path);
|
|
421
|
+
if (node instanceof CharNode || node instanceof NumberNode || node instanceof OperatorNode) {
|
|
422
|
+
return { node_type: node.nodeType, expr: node.expr, ...scripts };
|
|
423
|
+
}
|
|
424
|
+
if (node instanceof DelimiterNode) {
|
|
425
|
+
return {
|
|
426
|
+
node_type: "DelimiterObject",
|
|
427
|
+
left_delim_expr: node.leftDelimExpr,
|
|
428
|
+
inner_expr: chainToJson(node.innerExpr, `${path}.inner_expr`),
|
|
429
|
+
right_delim_expr: node.rightDelimExpr,
|
|
430
|
+
...scripts
|
|
431
|
+
};
|
|
432
|
+
}
|
|
433
|
+
if (node instanceof CommandNode) {
|
|
434
|
+
return {
|
|
435
|
+
node_type: "CommandObject",
|
|
436
|
+
name: node.name,
|
|
437
|
+
optional_args: node.optionalArgs.map((arg, index) => chainToJson(arg, `${path}.optional_args[${String(index)}]`)),
|
|
438
|
+
mandatory_args: node.mandatoryArgs.map((arg, index) => chainToJson(arg, `${path}.mandatory_args[${String(index)}]`)),
|
|
439
|
+
...scripts
|
|
440
|
+
};
|
|
441
|
+
}
|
|
442
|
+
if (node instanceof EnvNode) {
|
|
443
|
+
return {
|
|
444
|
+
node_type: "EnvObject",
|
|
445
|
+
opening: node.opening,
|
|
446
|
+
lines: node.lines.map((line, index) => chainToJson(line, `${path}.lines[${String(index)}]`)),
|
|
447
|
+
closing: node.closing,
|
|
448
|
+
...scripts
|
|
449
|
+
};
|
|
450
|
+
}
|
|
451
|
+
throw new Error(`Unsupported runtime math node at ${path}: ${node.nodeType}`);
|
|
452
|
+
}
|
|
453
|
+
function chainToJson(chain, path) {
|
|
454
|
+
if (!(chain instanceof ChainNode)) {
|
|
455
|
+
throw new Error(`Expected live ChainNode at ${path}`);
|
|
456
|
+
}
|
|
457
|
+
return {
|
|
458
|
+
node_type: "ChainClass",
|
|
459
|
+
chain: chain.chain.map((node, index) => nodeToJson(node, `${path}.chain[${String(index)}]`))
|
|
460
|
+
};
|
|
461
|
+
}
|
|
462
|
+
function toMathObjectJson(math) {
|
|
463
|
+
if (math.nodeType !== "MathObject" || !Array.isArray(math.lines) || math.lines.length === 0) {
|
|
464
|
+
throw new Error("Cannot serialize invalid runtime MathObject");
|
|
465
|
+
}
|
|
466
|
+
return {
|
|
467
|
+
node_type: "MathObject",
|
|
468
|
+
math_mode: math.mathMode,
|
|
469
|
+
lines: math.lines.map((line, index) => chainToJson(line, `$.lines[${String(index)}]`)),
|
|
470
|
+
closing: math.closing
|
|
471
|
+
};
|
|
472
|
+
}
|
|
412
473
|
function renderMathNodeLatex(math) {
|
|
413
474
|
const lines = math.lines.map((line) => line.arabicLatex());
|
|
414
475
|
if (math.mathMode === "$" || math.mathMode === "\\(") {
|
|
@@ -1898,7 +1959,7 @@ function renderNodeArabic(node, options) {
|
|
|
1898
1959
|
return arabicScripts(node, content, options);
|
|
1899
1960
|
}
|
|
1900
1961
|
if (node.kind === "sqrt" && node.radicand) {
|
|
1901
|
-
const content = `\\
|
|
1962
|
+
const content = `\\arsqrt${optionalRootIndexArabic(node, options)}{${renderChainArabic(node.radicand, options)}}`;
|
|
1902
1963
|
return arabicScripts(node, content, options);
|
|
1903
1964
|
}
|
|
1904
1965
|
if (node.kind === "overset" && node.baseExpr) {
|
|
@@ -3319,6 +3380,7 @@ function documentInlineText(inlines) {
|
|
|
3319
3380
|
renderMathNodeLatex,
|
|
3320
3381
|
replaceMathSpanFromEditor,
|
|
3321
3382
|
replaceMathSpanSource,
|
|
3383
|
+
toMathObjectJson,
|
|
3322
3384
|
updateImageBlock,
|
|
3323
3385
|
updateListItemValue,
|
|
3324
3386
|
updateRawBlockValue,
|