@drghaliasri/butex 5.6.1 → 6.0.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
@@ -264,6 +264,47 @@ Figures and tables always export as centered `figure` / `table` floats. Optional
264
264
 
265
265
  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.
266
266
 
267
+ ### Headless document commands
268
+
269
+ Canonical `Document2Json` exports include a stable `id` on every block. Legacy input without IDs remains accepted and receives IDs on its next import/export normalization. Use the pure command and outline helpers for server-side session transforms:
270
+
271
+ ```ts
272
+ import {
273
+ applyDocument2Command,
274
+ document2Outline,
275
+ fromDocumentJson2,
276
+ toDocumentJson2,
277
+ } from '@drghaliasri/butex/document2';
278
+
279
+ const canonical = toDocumentJson2(fromDocumentJson2(legacyDocument));
280
+ const outline = document2Outline(canonical);
281
+ const updated = applyDocument2Command(canonical, {
282
+ op: 'insert_text_block',
283
+ kind: 'paragraph',
284
+ text: 'New session text',
285
+ anchor: { end: true },
286
+ });
287
+ ```
288
+
289
+ Commands address top-level blocks only. Insertions require either `{ end: true }` or `{ after_block_id }`; missing anchors fail explicitly. Whole-block text replacement rejects formatted text, citations, references, and math so an agent cannot silently discard structured inline content.
290
+
291
+ ### Document worker CLI
292
+
293
+ Node 20+ can run the same transforms through the isolated `butex-document2` executable. One-shot mode reads one request from stdin and writes one response to stdout:
294
+
295
+ ```bash
296
+ printf '%s' '{"action":"normalize","document":{"node_type":"DocumentObject","blocks":[]}}' | butex-document2
297
+ ```
298
+
299
+ Railway HTTP mode is stateless and requires a service token:
300
+
301
+ ```bash
302
+ BUTEX_WORKER_TOKEN=replace-me PORT=3000 \
303
+ butex-document2 --serve --host 0.0.0.0
304
+ ```
305
+
306
+ It exposes `GET /health` plus authenticated `POST /v1/document2/normalize`, `/v1/document2/outline`, and `/v1/document2/commands`. The worker accepts document JSON and returns transformed JSON only. FastAPI remains responsible for user authentication, article/session lookup, revisions, idempotency, S3 persistence, and MCP policy; browsers and agents must never call the worker directly.
307
+
267
308
  ### Document JSON contract (`value` + `math_objects`)
268
309
 
269
310
  Both document layers import the same shape. A text field holds `value` (the full string, **with math delimiters kept in place**) and an optional parallel `math_objects` array that supplies the structured equation AST for each math span found in that string.
@@ -403,6 +444,7 @@ After an external upload (for example S3), insert a figure on the **live** edito
403
444
  import { useRef } from 'react';
404
445
  import {
405
446
  ButexDocumentEditor2,
447
+ type ImageAssetRef,
406
448
  type ButexDocumentEditor2Ref,
407
449
  } from '@drghaliasri/butex/react-document2';
408
450
 
@@ -410,9 +452,9 @@ export function ArticleEditorWithUpload() {
410
452
  const editorRef = useRef<ButexDocumentEditor2Ref>(null);
411
453
 
412
454
  async function onFileUploaded(uuid: string) {
413
- // Sets \\includegraphics{assets/<uuid>.jpg} after the focused block.
455
+ // Sets asset_id and \\includegraphics{assets/<uuid>.jpg} after the focused block.
414
456
  // Do not call addDocument2ImageBlock outside the component or remount with key++.
415
- editorRef.current?.insertImageBlock(`assets/${uuid}.jpg`);
457
+ editorRef.current?.insertImageBlock({ assetId: `assets/${uuid}.jpg` } satisfies ImageAssetRef);
416
458
  }
417
459
 
418
460
  return (
@@ -421,6 +463,10 @@ export function ArticleEditorWithUpload() {
421
463
  resolveImageUrl={({ assetId, value }) =>
422
464
  assetId ? `https://cdn.example/${assetId}` : value
423
465
  }
466
+ onRequestImagePick={async ({ current }) => {
467
+ /* open host asset picker; return { assetId, value?, label?, thumbUrl? } or null */
468
+ return current;
469
+ }}
424
470
  onDocumentJsonChange={(json) => {
425
471
  /* persist json */
426
472
  }}
@@ -429,7 +475,7 @@ export function ArticleEditorWithUpload() {
429
475
  }
430
476
  ```
431
477
 
432
- `insertImageBlock` matches the toolbar figure button (focus-aware insert + undo snapshot). Use `updateImageBlockValue(blockId, value)` to change a path on an existing image block, and `getDocumentJson()` to read the canonical wire JSON without host-side AST mutation.
478
+ `insertImageBlock` matches the toolbar figure button (focus-aware insert + undo snapshot). Pass `{ assetId, value?, label?, thumbUrl? }` to set both live `assetId` and wire `asset_id`; passing a non-empty string remains supported and treats that string as both `value` and `asset_id`. Use `updateImageBlockAsset(blockId, ref)` to set a host asset on an existing image block, `updateImageBlockValue(blockId, value)` to change only the path value while preserving any existing asset identity, and `getDocumentJson()` to read the canonical wire JSON without host-side AST mutation. BuTeX never uploads image bytes; hosts provide `resolveImageUrl`, optionally `onRequestImagePick`, `listImageAssets`, or `renderImageBlockEditor` for picker/inventory UI.
433
479
 
434
480
  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"`.
435
481