@pixerate/editor 0.24.0 → 0.26.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 +101 -1
- package/THIRD_PARTY_NOTICES.md +40 -0
- package/dist/agent/index.cjs +175 -11
- package/dist/agent/index.cjs.map +1 -1
- package/dist/agent/index.d.cts +18 -4
- package/dist/agent/index.d.ts +18 -4
- package/dist/agent/index.js +168 -4
- package/dist/agent/index.js.map +1 -1
- package/dist/{arrangeShortcuts-B8O3f8-P.d.cts → arrange-BICutYPk.d.cts} +243 -29
- package/dist/{arrangeShortcuts-sLlD2rSX.d.ts → arrange-BICutYPk.d.ts} +243 -29
- package/dist/builtins-CdPcLwwF.d.cts +176 -0
- package/dist/builtins-Chj6zv_O.d.ts +176 -0
- package/dist/canvas/index.cjs +8588 -1857
- package/dist/canvas/index.cjs.map +1 -1
- package/dist/canvas/index.d.cts +1847 -9
- package/dist/canvas/index.d.ts +1847 -9
- package/dist/canvas/index.js +8577 -1846
- package/dist/canvas/index.js.map +1 -1
- package/dist/chunk-3CC7OPUQ.cjs +831 -0
- package/dist/chunk-3CC7OPUQ.cjs.map +1 -0
- package/dist/chunk-4BVP64JJ.cjs +1406 -0
- package/dist/chunk-4BVP64JJ.cjs.map +1 -0
- package/dist/chunk-BVP3TSLH.cjs +69 -0
- package/dist/chunk-BVP3TSLH.cjs.map +1 -0
- package/dist/{chunk-OLZJXL4R.cjs → chunk-CS6NZWWA.cjs} +59 -25
- package/dist/chunk-CS6NZWWA.cjs.map +1 -0
- package/dist/{chunk-MCSJFWAO.cjs → chunk-EWCOFSCQ.cjs} +685 -100
- package/dist/chunk-EWCOFSCQ.cjs.map +1 -0
- package/dist/chunk-HDYMLS3X.cjs +409 -0
- package/dist/chunk-HDYMLS3X.cjs.map +1 -0
- package/dist/chunk-HRMEDT2L.js +1406 -0
- package/dist/chunk-HRMEDT2L.js.map +1 -0
- package/dist/{chunk-D27YFC46.cjs → chunk-IFHKKVXH.cjs} +382 -63
- package/dist/chunk-IFHKKVXH.cjs.map +1 -0
- package/dist/{chunk-PGNZKFQS.js → chunk-JOIREKL7.js} +137 -58
- package/dist/chunk-JOIREKL7.js.map +1 -0
- package/dist/chunk-KRGXUQ2Y.js +1696 -0
- package/dist/chunk-KRGXUQ2Y.js.map +1 -0
- package/dist/{chunk-3LV6RRPH.js → chunk-MHEPLBFB.js} +2 -2
- package/dist/chunk-MR2764X2.js +831 -0
- package/dist/chunk-MR2764X2.js.map +1 -0
- package/dist/{chunk-XNORY7M6.cjs → chunk-NFVBT3UZ.cjs} +166 -87
- package/dist/chunk-NFVBT3UZ.cjs.map +1 -0
- package/dist/chunk-NQF32I6Z.cjs +6478 -0
- package/dist/chunk-NQF32I6Z.cjs.map +1 -0
- package/dist/chunk-O5RYH2PG.js +69 -0
- package/dist/chunk-O5RYH2PG.js.map +1 -0
- package/dist/{chunk-ITBSKI26.js → chunk-Q2OHWD2A.js} +1064 -360
- package/dist/chunk-Q2OHWD2A.js.map +1 -0
- package/dist/{chunk-FODNLX5N.js → chunk-QJSOZFCZ.js} +38 -4
- package/dist/chunk-QJSOZFCZ.js.map +1 -0
- package/dist/{chunk-SML64VJG.cjs → chunk-RRREAOVM.cjs} +4 -4
- package/dist/{chunk-SML64VJG.cjs.map → chunk-RRREAOVM.cjs.map} +1 -1
- package/dist/{chunk-5S3Z4PPF.js → chunk-SWE3NPOU.js} +347 -28
- package/dist/chunk-SWE3NPOU.js.map +1 -0
- package/dist/chunk-TXXNBDQX.cjs +1696 -0
- package/dist/chunk-TXXNBDQX.cjs.map +1 -0
- package/dist/chunk-UBCFIABU.js +409 -0
- package/dist/chunk-UBCFIABU.js.map +1 -0
- package/dist/chunk-X7HXL2SB.js +6478 -0
- package/dist/chunk-X7HXL2SB.js.map +1 -0
- package/dist/{chunk-SBCZ6RN5.cjs → chunk-YOMYSFJU.cjs} +1163 -459
- package/dist/chunk-YOMYSFJU.cjs.map +1 -0
- package/dist/{chunk-3CN2WD3X.js → chunk-ZRNHGAQT.js} +661 -76
- package/dist/chunk-ZRNHGAQT.js.map +1 -0
- package/dist/commands/index.cjs +425 -4
- package/dist/commands/index.cjs.map +1 -1
- package/dist/commands/index.d.cts +39 -77
- package/dist/commands/index.d.ts +39 -77
- package/dist/commands/index.js +424 -3
- package/dist/commands/index.js.map +1 -1
- package/dist/compare/index.cjs +3 -3
- package/dist/compare/index.d.cts +1 -1
- package/dist/compare/index.d.ts +1 -1
- package/dist/compare/index.js +2 -2
- package/dist/controller-CxXGOEmy.d.cts +2038 -0
- package/dist/controller-CxXGOEmy.d.ts +2038 -0
- package/dist/{documents-BQjzJNe2.d.ts → documents-CHT1U5Vs.d.ts} +10 -0
- package/dist/{documents-KRTPhvst.d.cts → documents-Gp7wb0Me.d.cts} +10 -0
- package/dist/export/index.cjs +36 -0
- package/dist/export/index.cjs.map +1 -0
- package/dist/export/index.d.cts +243 -0
- package/dist/export/index.d.ts +243 -0
- package/dist/export/index.js +36 -0
- package/dist/export/index.js.map +1 -0
- package/dist/extensions/index.cjs +101 -6
- package/dist/extensions/index.cjs.map +1 -1
- package/dist/extensions/index.d.cts +18 -5
- package/dist/extensions/index.d.ts +18 -5
- package/dist/extensions/index.js +101 -6
- package/dist/generation/index.cjs +1013 -69
- package/dist/generation/index.cjs.map +1 -1
- package/dist/generation/index.d.cts +474 -13
- package/dist/generation/index.d.ts +474 -13
- package/dist/generation/index.js +976 -32
- package/dist/generation/index.js.map +1 -1
- package/dist/image-editor/index.cjs +3385 -1275
- package/dist/image-editor/index.cjs.map +1 -1
- package/dist/image-editor/index.d.cts +693 -128
- package/dist/image-editor/index.d.ts +693 -128
- package/dist/image-editor/index.js +3325 -1215
- package/dist/image-editor/index.js.map +1 -1
- package/dist/{index-fJrlkJCd.d.ts → index-B273Zllk.d.ts} +500 -7
- package/dist/{index-BhJBSm2d.d.cts → index-jcJwmBYt.d.cts} +500 -7
- package/dist/index.cjs +1603 -45
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +154 -27
- package/dist/index.d.ts +154 -27
- package/dist/index.js +1584 -26
- package/dist/index.js.map +1 -1
- package/dist/mention-text-CDnCFd3c.d.cts +53 -0
- package/dist/mention-text-CDnCFd3c.d.ts +53 -0
- package/dist/model-C1zApQM8.d.cts +219 -0
- package/dist/model-C1zApQM8.d.ts +219 -0
- package/dist/persistence/index.d.cts +3 -120
- package/dist/persistence/index.d.ts +3 -120
- package/dist/registry-CED5C0lA.d.cts +204 -0
- package/dist/registry-CED5C0lA.d.ts +204 -0
- package/dist/{registry-GJI4gToU.d.cts → registry-CEgwXED1.d.cts} +12 -1
- package/dist/{registry-GJI4gToU.d.ts → registry-CEgwXED1.d.ts} +12 -1
- package/dist/{registry-D32LpIc4.d.cts → registry-D0GADKc7.d.cts} +1 -1
- package/dist/{registry-evKTP4ls.d.ts → registry-DhVUZioj.d.ts} +1 -1
- package/dist/serializers/index.cjs +12 -4
- package/dist/serializers/index.cjs.map +1 -1
- package/dist/serializers/index.d.cts +41 -13
- package/dist/serializers/index.d.ts +41 -13
- package/dist/serializers/index.js +12 -4
- package/dist/spreadsheet/index.cjs +157 -3
- package/dist/spreadsheet/index.cjs.map +1 -1
- package/dist/spreadsheet/index.d.cts +534 -11
- package/dist/spreadsheet/index.d.ts +534 -11
- package/dist/spreadsheet/index.js +157 -3
- package/dist/storage-BpFfspHm.d.cts +120 -0
- package/dist/storage-BpFfspHm.d.ts +120 -0
- package/dist/tokens/index.cjs +115 -0
- package/dist/tokens/index.cjs.map +1 -0
- package/dist/tokens/index.d.cts +212 -0
- package/dist/tokens/index.d.ts +212 -0
- package/dist/tokens/index.js +115 -0
- package/dist/tokens/index.js.map +1 -0
- package/dist/{track-changes-BK5sVf4_.d.cts → track-changes-D_VAPELi.d.cts} +95 -11
- package/dist/{track-changes-CDjCrwKe.d.ts → track-changes-Db_fGEvx.d.ts} +95 -11
- package/package.json +15 -1
- package/dist/chunk-3CN2WD3X.js.map +0 -1
- package/dist/chunk-5S3Z4PPF.js.map +0 -1
- package/dist/chunk-D27YFC46.cjs.map +0 -1
- package/dist/chunk-FODNLX5N.js.map +0 -1
- package/dist/chunk-ITBSKI26.js.map +0 -1
- package/dist/chunk-MA2F5KHF.cjs +0 -3223
- package/dist/chunk-MA2F5KHF.cjs.map +0 -1
- package/dist/chunk-MCSJFWAO.cjs.map +0 -1
- package/dist/chunk-OLZJXL4R.cjs.map +0 -1
- package/dist/chunk-PGNZKFQS.js.map +0 -1
- package/dist/chunk-SBCZ6RN5.cjs.map +0 -1
- package/dist/chunk-U5KWZU4X.js +0 -3223
- package/dist/chunk-U5KWZU4X.js.map +0 -1
- package/dist/chunk-XNORY7M6.cjs.map +0 -1
- package/dist/controller-BhLLAoi8.d.cts +0 -1000
- package/dist/controller-BhLLAoi8.d.ts +0 -1000
- package/dist/types-DX4Mh4SL.d.cts +0 -217
- package/dist/types-DX4Mh4SL.d.ts +0 -217
- /package/dist/{chunk-3LV6RRPH.js.map → chunk-MHEPLBFB.js.map} +0 -0
package/README.md
CHANGED
|
@@ -139,6 +139,24 @@ getFindReplaceState(editor); // { open, mode, query, replacement, options, match
|
|
|
139
139
|
- **Track changes**: tracked-deleted text is skipped (a match never spans a deletion, but word boundaries see the live text), and replacements made while tracking become tracked deletions and insertions.
|
|
140
140
|
- Matches are decorated with `pixerate-find-match` and the current one also with `pixerate-find-match-current` (`matchClass` / `currentMatchClass`); style them in your CSS. `createFindReplaceCommands(editor)` returns command-registry specs (`findReplace.open`, `.next`, `.find`, `.replaceAll`, …).
|
|
141
141
|
|
|
142
|
+
## Tables
|
|
143
|
+
|
|
144
|
+
`createRichTextPreset()` includes tables by default (TipTap's `table`, `tableRow`, `tableHeader`, `tableCell`): resizable columns (`colwidth`), header rows, columns and cells, `colspan` / `rowspan` (kept from pasted HTML and from DOCX / PDF import), cell `align` and `backgroundColor`, and table node selection. Configure them with `table: { resizable, cellMinWidth, allowTableNodeSelection, HTMLAttributes, ... }`, or pass `table: false` to leave tables out of the schema (pasted and imported tables are then flattened into paragraphs, as before).
|
|
145
|
+
|
|
146
|
+
```ts
|
|
147
|
+
import { createTableCommands, getTableState, runTableAction, getTableActionState } from "@pixerate/editor";
|
|
148
|
+
|
|
149
|
+
runTableAction(editor, "insertTable", { rows: 3, cols: 4, withHeaderRow: true });
|
|
150
|
+
getTableActionState(editor, "mergeCells"); // true, or why not: "Select two or more adjacent cells to merge."
|
|
151
|
+
getTableState(editor); // { inTable, rows, columns, row, column, headerRow, headerColumn, headerCell, cellAlignment, actions: { … } }
|
|
152
|
+
registry.register(createTableCommands(editor)); // table.insertTable, table.addRowAfter, table.mergeCells, … (palette & agents)
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
- **Actions** (`TABLE_ACTIONS`): `insertTable` (no nesting), `addRowBefore` / `addRowAfter` / `deleteRow`, `addColumnBefore` / `addColumnAfter` / `deleteColumn`, `deleteTable`, `mergeCells` / `splitCell`, `toggleHeaderRow` / `toggleHeaderColumn` / `toggleHeaderCell`, `goToNextCell` / `goToPreviousCell` (Tab / Shift+Tab), `setCellAlignment` (`{ align }`) and `setCellBackground` (`{ color }`, sanitized). `createTableStore(editor)` is the external store the React and Svelte toolbars use; `DEFAULT_TABLE_TOOLBAR` lists their buttons. `createRichTextToolProvider` adds an `edit_table` agent tool when the schema has tables.
|
|
156
|
+
- **Export**: `getEditorText` writes each row as a line of tab-separated cells. Markdown (`markdown: true`) writes a GFM pipe table (column alignment from the header cells, `|` escaped) when the first row is all header cells, nothing spans and every cell holds one paragraph; otherwise the table is written as HTML, which parses back. GFM tables in markdown input become tables.
|
|
157
|
+
- **Track changes**: edits inside cells are tracked like any text. Inserting or deleting rows, columns (their cells) or a whole table is tracked as a whole-block change (`blockChange` with `operation`): rejecting an insertion or accepting a deletion removes the rows/cells/table; deleting rows you inserted yourself removes them. Header toggles are tracked as block changes (reject restores the cell type). Cell attributes (alignment, background, widths) are not tracked, and merging or splitting cells is refused while tracking. `compareDocuments` compares tables row by row and cell by cell.
|
|
158
|
+
- Style the tables in your CSS (`.tableWrapper`, `table`, `th`, `td`, `.selectedCell`, `.column-resize-handle`; the table has the `pixerate-table` class).
|
|
159
|
+
|
|
142
160
|
## File import and rich clipboard
|
|
143
161
|
|
|
144
162
|
An import adapter registry maps file types (MIME types and extensions) to converters. Built-in `document` adapters cover markdown (`.md`, `.markdown`, `.mdown`, `.mkdn`, `.mkd`, `.Rmd`, `.md.txt`), HTML (sanitized with the same allowlist as markdown), plain text (`.txt`), RTF (best-effort text extraction) and DOCX (`.docx`, `.docm`, `.dotx`). The longest matching extension wins, then exact MIME types, then `priority`, then the most recent registration.
|
|
@@ -284,6 +302,88 @@ const extensions = createRichTextPreset({
|
|
|
284
302
|
|
|
285
303
|
`Autocorrect` takes a `rules` list (`defaultAutocorrectRules`, or compose `smartQuoteRules`, `symbolRules`, `fractionRules`, `capitalizeSentenceRules`, `twoInitialCapitalsRules`, `smilieAutocorrectRules` and your own `{ id, find, replace }` rules). Undo right after a correction restores what was typed; nothing is corrected in code or inside prompt tokens. It replaces the Typography extension in the preset. `getDocumentStructureCommandItems(editor, query)` returns ready-made slash-menu/toolbar items for the installed extensions.
|
|
286
304
|
|
|
305
|
+
## Image selections and masks
|
|
306
|
+
|
|
307
|
+
`@pixerate/editor/image-editor` has Photoshop-style selections. A selection is a `SelectionMask`: one 8-bit coverage value per source pixel (natural image pixels, the same space as the crop box), with `bounds`, treated as immutable.
|
|
308
|
+
|
|
309
|
+
```ts
|
|
310
|
+
const editor = new ImageEditorController({ image });
|
|
311
|
+
await editor.magicWand({ x: 120, y: 80 }, { tolerance: 32, contiguous: true, mode: "add" });
|
|
312
|
+
editor.selectEllipse({ x: 40, y: 40, width: 200, height: 120 }, { mode: "subtract", feather: 2 });
|
|
313
|
+
editor.modifySelection("grow", 4); // grow | shrink | feather | border | smooth
|
|
314
|
+
|
|
315
|
+
// A coarse AI segmentation (any size, white = selected), snapped to the image's edges.
|
|
316
|
+
await editor.loadSelectionFromImage(maskDataUrl);
|
|
317
|
+
await editor.refineSelection({ radius: 12, smartRadius: true, contrast: 20 });
|
|
318
|
+
|
|
319
|
+
// Use it as an adjustment layer mask; density and feather stay editable.
|
|
320
|
+
const id = editor.addAdjustmentLayer("hueSaturation");
|
|
321
|
+
editor.applySelectionAsLayerMask(id, { feather: 3 });
|
|
322
|
+
editor.setAdjustmentLayerMaskProps(id, { density: 0.6 });
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
- **Tools**: `magicWand` (scanline span fill with an explicit stack; RGB or perceptual ΔE tolerance; contiguous or global; optional `sampleMerged`), `colorRange` (samples, fuzziness, localized clusters), `toneRange`, `hueRange`, `rectSelection`, `ellipseSelection`, `polygonSelection` (anti-aliased).
|
|
326
|
+
- **Modes**: `replace`, `add` (max), `subtract`, `intersect` (min), on 8-bit values; Shift / Alt map to add / subtract (`selectionModeFromModifiers`).
|
|
327
|
+
- **Modify**: grow / shrink through an exact Euclidean distance transform, border, smooth (box + threshold), feather (Gaussian, σ = radius, three box passes above σ 8). All linear in the pixel count.
|
|
328
|
+
- **Refine Edge**: `refineSelection(pixels, mask, { radius, smartRadius, smooth, feather, contrast, shiftEdge })`, a colour guided filter in a band around the edge, confined to the mask's bounds plus a halo.
|
|
329
|
+
- **Layer masks**: `AdjustmentLayer.mask` (`{ width, height, rle, density?, feather?, enabled? }`) scales the layer's opacity per pixel; it is resampled through the crop / rotation / flips like the image. `selectionFromLayerMask` loads it back.
|
|
330
|
+
- **Document**: `toJSON()` adds `selection` (run-length encoded, `r1:` + base64) only when something is selected, and layers only carry `mask` when they have one, so selection- and mask-free documents serialize exactly as before. Selections are part of undo history. `renderMask()` / `toMaskDataURL()` include the selection for inpainting.
|
|
331
|
+
- **Viewport**: marching ants by default, `quickMask` tints the unselected area; `RenderOptions.selectionDisplay` overrides, `draftSelectionPath` shows a shape being drawn. `SelectionGesture` turns pointer events into selections for UIs.
|
|
332
|
+
- **Agent tools**: `createImageEditorToolProvider` adds `get_selection`, `select_region`, `select_by_color`, `modify_selection`, `refine_selection` and `apply_selection_as_mask`.
|
|
333
|
+
- **Pixels**: the tools read the source image through a canvas; pass `setSamplePixels(imageData)` (or `{ pixels }` per call) where there is no canvas or the image is cross-origin.
|
|
334
|
+
|
|
335
|
+
## Spreadsheet sheets, defined names and LAMBDA
|
|
336
|
+
|
|
337
|
+
A `SpreadsheetController` is a workbook. Created from one `document` it has a single sheet and behaves as before (`controller.document` is the active sheet); pass `workbook` for several sheets.
|
|
338
|
+
|
|
339
|
+
```ts
|
|
340
|
+
import { SpreadsheetController } from "@pixerate/editor/spreadsheet";
|
|
341
|
+
|
|
342
|
+
const sheet = new SpreadsheetController({ document: { name: "Sales" } });
|
|
343
|
+
sheet.addSheet({ name: "Summary" }); // activates it; returns its id
|
|
344
|
+
sheet.defineName("Revenue", "=Sales!$F$2:$F$11");
|
|
345
|
+
sheet.defineName("WithTax", "=LAMBDA(x, x*1.2)");
|
|
346
|
+
sheet.setCellValue(sheet.document.rows[0].id, sheet.document.columns[0].id, "=WithTax(SUM(Revenue))");
|
|
347
|
+
sheet.renameSheet("Sales", "Sales 2026"); // formulas become ='Sales 2026'!…
|
|
348
|
+
sheet.getWorkbook(); // { sheets, activeSheetId, names } to save
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
Formulas reference other sheets (`Sheet2!A1`, `'My Sheet'!A1:B2`, `Data!A1#`) and 3-D ranges (`SUM(Sheet1:Sheet3!A1)`). Deleting a sheet turns references to it into `#REF!`; inserting or deleting rows and columns rewrites references from every sheet. Names can be workbook- or sheet-scoped (`{ sheet }`), are validated like Excel (`validateDefinedName`), and `renameName` rewrites the formulas using them. `LAMBDA` values can be called inline (`=LAMBDA(x, x+1)(2)`), stored with `LET` or a name (recursion is capped at 1000 calls with `#NUM!`), or passed to `MAP`, `REDUCE`, `SCAN`, `BYROW`, `BYCOL` and `MAKEARRAY`. Undo restores the sheet a step changed (switching to it) or, for sheet and name operations, the whole workbook.
|
|
352
|
+
|
|
353
|
+
## Spreadsheet conditional formatting and data validation
|
|
354
|
+
|
|
355
|
+
Rules live in the sheet's `document.table` (`conditionalFormats`, highest priority first, and `dataValidations`), so they are saved, undone and redone with the sheet, and their ranges and formulas follow inserted and deleted rows and columns.
|
|
356
|
+
|
|
357
|
+
```ts
|
|
358
|
+
import { SpreadsheetController, createConditionalFormatRule } from "@pixerate/editor/spreadsheet";
|
|
359
|
+
|
|
360
|
+
const sheet = new SpreadsheetController({ document });
|
|
361
|
+
const all = { startRow: 1, startCol: 0, endRow: 20, endCol: 3 };
|
|
362
|
+
sheet.addConditionalFormat({ type: "expression", formula: "=$D2>100", style: { fill: "#c6efce" }, ranges: [all] });
|
|
363
|
+
sheet.addConditionalFormat(createConditionalFormatRule({ kind: "dataBar", ranges: [{ ...all, startCol: 3, endCol: 3 }] }));
|
|
364
|
+
sheet.getConditionalFormat(4, 3); // { fill, dataBar: { fraction, color, gradient }, ... } or undefined
|
|
365
|
+
|
|
366
|
+
sheet.addDataValidation({ ranges: [{ ...all, endCol: 0 }], type: "list", formula1: "North,South,East,West", errorStyle: "warning" });
|
|
367
|
+
sheet.startEditing(1, 0, "Mars");
|
|
368
|
+
const result = sheet.commitEditing(); // { status: "confirm", prompt } — the editor stays open
|
|
369
|
+
sheet.resolveValidationPrompt("cancel"); // or "accept" (warning / information) / "retry"
|
|
370
|
+
sheet.findInvalidCells(); // "circle invalid data"
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
`evaluateConditionalFormats(sheet, rules)` and `validateCellInput(sheet, rules, row, col, input)` also work on any grid through the small `ConditionalFormatSheet` / `DataValidationSheet` interfaces. Rule formulas are written for the top-left cell of the first range and shift for other cells; custom validation formulas see the value being entered. `setCellValue`, paste and import never validate (as in Excel); `commitEditing` and `commitCellInput` do.
|
|
374
|
+
|
|
375
|
+
## Large spreadsheets: incremental recalculation and virtualised rendering
|
|
376
|
+
|
|
377
|
+
The dependency graph stores each range a formula reads once, in an interval index per column (wide ranges in a per-sheet index), instead of one key per cell, so `=SUM(A1:A50000)` and `=SUM(A:A)` are one entry each. An edit finds the formulas reading the cell in O(log n + k), recalculates only that dirty subgraph in topological order (a range read by many formulas is resolved once per recalculation), and still detects cycles, re-spills dynamic arrays, follows sheet-qualified, 3-D and defined-name references and recalculates volatile functions (`NOW`, `TODAY`, `RAND`, `RANDBETWEEN`, `RANDARRAY`) on every edit. CSV import, `loadWorkbook` and large pastes register the cells and evaluate each formula once; a single-cell edit is undone without a whole-sheet snapshot. `SUM`, `PRODUCT`, `AVERAGE`, `COUNT`, `COUNTA`, `MIN` and `MAX` read their ranges without materialising blank cells.
|
|
378
|
+
|
|
379
|
+
```ts
|
|
380
|
+
sheet.setCellValue(rowId, colId, "42");
|
|
381
|
+
sheet.getRecalculationStats(); // { evaluated: 1, planned: 2, passes: 1 }: deterministic work counts
|
|
382
|
+
sheet.depGraph.getIndexStats(); // { formulas, cellEdges, rangeEntries, volatile }
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
`measureSpreadsheetView`, `getSpreadsheetViewWindow` and `getSpreadsheetScrollToCell` work out which displayed rows and columns to render for a scroll position (frozen rows and columns always, merges crossing the edge whole) and how far to scroll to show the active cell. The React and Svelte `SpreadsheetEditor` use them for sheets with more than 200 displayed rows or 60 columns (`virtualize` prop: `'auto'`, `true` or `false`). `packages/core/tests/spreadsheet-recalc.bench.test.ts` measures 50k-cell sheets (`SPREADSHEET_BENCH=1`).
|
|
386
|
+
|
|
287
387
|
## Other modules
|
|
288
388
|
|
|
289
389
|
Each module is also available as a subpath import:
|
|
@@ -295,7 +395,7 @@ Each module is also available as a subpath import:
|
|
|
295
395
|
| `@pixerate/editor/extensions` | Headless TipTap extensions |
|
|
296
396
|
| `@pixerate/editor/history` | `HistoryManager`: a command-pattern undo/redo stack with gesture merge keys, undo marks, a byte budget and a History-panel listing; `DirtyTracker` / `bindDirtyToHistory` |
|
|
297
397
|
| `@pixerate/editor/spreadsheet` | `SpreadsheetController`, formula parser/evaluator and dependency graph, formula function registry (`registerFormulaFunction`, `listFormulaFunctions`, `searchFormulaFunctions`, `getFormulaSignature`) and autocomplete helpers (`getFormulaHints`, `applyFormulaSuggestion`) |
|
|
298
|
-
| `@pixerate/editor/image-editor` | `ImageEditorController` and Canvas 2D renderer: crop, adjustments, annotations, depth masks and AI hooks. Needs a DOM/canvas environment. |
|
|
398
|
+
| `@pixerate/editor/image-editor` | `ImageEditorController` and Canvas 2D renderer: crop, adjustments, annotations, depth masks, selections and layer masks, and AI hooks. Needs a DOM/canvas environment (the selection algorithms themselves are pure). |
|
|
299
399
|
| `@pixerate/editor/canvas` | Node-graph layout (needs the optional `@dagrejs/dagre` peer), placement, trajectories, displacement, clipboard and streaming layout helpers |
|
|
300
400
|
| `@pixerate/editor/agent` | MCP tool schemas, a planner for agent-driven canvas edits, and the dynamic `ToolRegistry` with editor/spreadsheet tool providers |
|
|
301
401
|
| `@pixerate/editor/commands` | `CommandRegistry`, `ShortcutMap`, palette ranking, `createCommandToolProvider` and built-in history / arrange / spreadsheet commands (no third-party imports) |
|
package/THIRD_PARTY_NOTICES.md
CHANGED
|
@@ -26,6 +26,14 @@ reproduced at the end of this file. Paths are relative to each source repository
|
|
|
26
26
|
- `crates/engine/src/measure_tests.rs` (test cases re-expressed) → `packages/core/tests/canvas-measure.test.ts`
|
|
27
27
|
- `crates/layout/src/lib.rs` (`layout_frame`, `distribute`, `main_offsets`, `fit_hug_text`, `enforce_limits`, `item_baseline`, `resize_child`, `grid_place`, `resolve_tracks`, `track_content`, `grid_geometry`, `layout_grid`, `apply_constraints`, `layout_page`, `layout_up`, `coherent_sizing`, `clamp_size`) → `packages/core/src/canvas/autoLayout.ts`
|
|
28
28
|
- `crates/layout/src/{tests.rs,behaviour_tests.rs,nested_tests.rs,content_align_tests.rs}` (test cases re-expressed) → `packages/core/tests/canvas-auto-layout.test.ts`
|
|
29
|
+
- `crates/doc/src/styles.rs` (`Collection`, `Mode`, `Variable`, `VariableValue`, `Variables::resolve`, `default_mode`, `current_mode`, `find_mode`, `scopes_for`, `normalize_scopes`, `in_scope`) → `packages/core/src/tokens/types.ts`, `packages/core/src/tokens/model.ts`
|
|
30
|
+
- `crates/engine/src/cmd/variables.rs` (`alias_target` cycle check, `forget_variables` alias detaching, `remove_mode`, `move_mode`, `rename_collection`, `delete_collection`, `field_scope`) → `packages/core/src/tokens/model.ts`, `packages/core/src/canvas/tokenBindings.ts`
|
|
31
|
+
- `crates/doc/src/lib.rs` (`modes_for`: modes inherited down the tree) → `getEffectiveTokenModes` in `packages/core/src/canvas/tokenBindings.ts`
|
|
32
|
+
- `crates/engine/src/variables_io_tests.rs` (behaviour of the variables JSON import / export: merging by name, junk and loop handling, CSS export per mode) → `packages/core/src/tokens/w3c.ts`, `packages/core/src/tokens/css.ts`
|
|
33
|
+
- `crates/engine/src/{variable_tests.rs,variables_behaviour_tests.rs,variables_io_tests.rs}` (test cases re-expressed) → `packages/core/tests/tokens.test.ts`, `packages/core/tests/canvas-tokens.test.ts`
|
|
34
|
+
- `crates/engine/src/components.rs` (instances rebuilt from their main keeping ids and overrides — `sync_instance`, `carry_overrides`, `path_map` matching, `variant_kept`, `diff_groups`, `sync_after_edit`, `rebuild_instances` ordering, `rebase_instance`, `unlink_layers`, `relink_copy`, component property chains, `default_variant`, `variant_props`, `variant_name`, `variant_options`, `find_variant`, `variant_conflicts`) → `packages/core/src/canvas/components.ts`
|
|
35
|
+
- `crates/engine/src/cmd/component.rs` (create component, combine as variants, create / detach / swap instance, reset instance and overrides, push changes, restore main, set instance properties, add / edit / delete / bind component properties) → `packages/core/src/canvas/components.ts`, `packages/core/src/canvas/componentsController.ts`
|
|
36
|
+
- `crates/engine/src/{components_tests.rs,components_behaviour_tests.rs,components_scale_tests.rs}` (test cases re-expressed) → `packages/core/tests/canvas-components.test.ts`, `packages/core/tests/canvas-components-controller.test.ts`
|
|
29
37
|
|
|
30
38
|
## filmcraft
|
|
31
39
|
|
|
@@ -39,6 +47,9 @@ reproduced at the end of this file. Paths are relative to each source repository
|
|
|
39
47
|
- `apps/filmcraft-web/src/recovery_policy.rs` (`Policy`, `Action`, `tick` / `written` / `cleaned`: snapshot at most once per interval and revision, retry failed writes, retire only this session's snapshot) → `packages/core/src/persistence/recoveryPolicy.ts`
|
|
40
48
|
- `apps/filmcraft-web/src/recovery.rs` (snapshot-then-dirty-meta writes, recovery check of a dirty meta) → `packages/core/src/persistence/autosave.ts`
|
|
41
49
|
- `apps/filmcraft-web/tests/recovery_policy.rs` (test cases re-expressed) → `packages/core/tests/persistence-policy.test.ts`
|
|
50
|
+
- `crates/project/src/keyframe.rs` (`Keyframe`, `Param::value_at` segment evaluation and hold, `set_at` inheriting the previous interpolation, `remove_keyframe_at` keeping the last value, `toggle_animation` keeping the value at a time, temporal influence easing, `cubic_bezier_y_for_x` Newton + bisection solver) → `packages/core/src/canvas/animation/{easing,model,evaluate,edit}.ts`
|
|
51
|
+
- `crates/engine/src/keyframe_move.rs` (`move_many`: one common delta across lanes clamped to the movement range, remove-all-originals-then-insert, validation without partial edits, at most 4096 keys) → `moveKeyframes` / `scaleKeyframes` in `packages/core/src/canvas/animation/edit.ts`, keyframe drags in `packages/core/src/canvas/animation/controller.ts`
|
|
52
|
+
- `crates/project/src/keyframe.rs` tests, `crates/engine/src/keyframe_move.rs` tests and `crates/ui-egui/tests/keyframes_ui.rs` (group move and undo as one step, snapping within a screen-space threshold, Escape cancelling a drag preview; test cases re-expressed) → `packages/core/tests/canvas-animation.test.ts`, `packages/react/tests/canvas-animation.test.tsx`, `packages/svelte/tests/canvas/canvas-animation.test.ts`
|
|
42
53
|
|
|
43
54
|
## gridcraft
|
|
44
55
|
|
|
@@ -50,6 +61,15 @@ reproduced at the end of this file. Paths are relative to each source repository
|
|
|
50
61
|
- Test cases from the files above, re-expressed → `packages/core/tests/spreadsheet-typed-input.test.ts`
|
|
51
62
|
- `crates/numfmt/src/lib.rs`, `parse.rs`, `render.rs`, `decimal.rs`, `builtin.rs` (Excel number format parsing and rendering, 15-digit decimal rounding, General format, `TEXT()`, built-in formats 0–49, parse cache) and `crates/core/src/date.rs` (serial to date) → `packages/core/src/spreadsheet/numfmt/`
|
|
52
63
|
- `crates/numfmt/src/tests.rs` and `crates/numfmt/tests/poi_number_format_approx.rs` (test cases re-expressed) → `packages/core/tests/number-format.test.ts`, `packages/core/tests/fixtures/poi-number-format-approx.ts`
|
|
64
|
+
- `crates/calc/src/eval.rs` (LET/LAMBDA environments and closures, lambda calls and argument counts, ISOMITTED, MAP/REDUCE/SCAN/BYROW/BYCOL/MAKEARRAY, functions passed by name, defined-name evaluation, recursion depth limit) → `packages/core/src/spreadsheet/formula/evaluator.ts`, `packages/core/src/spreadsheet/formula/lambda.ts`, `packages/core/src/spreadsheet/formula/functions/lambda.ts`
|
|
65
|
+
- `crates/model/src/workbook.rs` (sheet name rules, defined-name lookup by scope) and `crates/engine/src/cmd/formulas.rs` (defined-name validation, R1C1 look-alikes, renaming names in formulas) → `packages/core/src/spreadsheet/workbook.ts`, `packages/core/src/spreadsheet/controller.ts`
|
|
66
|
+
- `crates/formula/src/adjust.rs` and `crates/formula/src/printer.rs` (rename/delete sheet rewriting, 3-D span shrinking, sheet-name quoting) and `crates/engine/src/cmd/sheet.rs` (add, rename, move, copy and delete sheets with their names) → `packages/core/src/spreadsheet/formula/references.ts`, `packages/core/src/spreadsheet/controller.ts`
|
|
67
|
+
- `crates/calc/src/tests.rs`, `crates/formula/src/tests.rs`, `crates/engine/src/tests.rs` and `crates/model/src/workbook.rs` tests (test cases re-expressed) → `packages/core/tests/spreadsheet-lambda.test.ts`, `packages/core/tests/spreadsheet-workbook.test.ts`
|
|
68
|
+
- `crates/model/src/features.rs` (conditional formatting and data validation rule model, icon-set thresholds) and `crates/engine/src/cf.rs` (rule evaluation: priority, stop if true, colour scales, data bars, icon sets, top/bottom, averages, duplicates, text and date rules, relative rule formulas) → `packages/core/src/spreadsheet/conditional-format/`
|
|
69
|
+
- `crates/engine/src/cmd/data.rs` (validation checks, list items, circle invalid data, removing rule cells) and `crates/engine/src/cmd/sheet.rs` (rule ranges following inserted / deleted rows and columns) → `packages/core/src/spreadsheet/validation/`, `packages/core/src/spreadsheet/rule-ranges.ts`
|
|
70
|
+
- Test cases from `crates/engine/src/cf.rs` and `crates/engine/src/tests.rs` (icon-set thresholds, relative validation formulas), re-expressed → `packages/core/tests/spreadsheet-conditional-format.test.ts`, `packages/core/tests/spreadsheet-data-validation.test.ts`
|
|
71
|
+
- `crates/calc/src/recalc.rs` (`Intervals`: row intervals sorted by start with an implicit max-end segment tree, pending insertions and dead entries merged lazily; ranges indexed per column with a per-sheet index for wide ranges; dependents of a cell in O(log n + k); dirty closure of an edit) → `packages/core/src/spreadsheet/formula/dependency-graph.ts`
|
|
72
|
+
- `crates/calc/src/tests.rs` and `crates/calc/src/recalc.rs` (interval-index and incremental-recalculation cases, re-expressed) → `packages/core/tests/spreadsheet-recalc.test.ts`
|
|
53
73
|
**Apache POI.** The rows of `packages/core/tests/fixtures/poi-number-format-approx.ts` (format, value and Excel's
|
|
54
74
|
`TEXT()` result) come from Apache POI's `test-data/spreadsheet/NumberFormatApproxTests.xlsx`, as collected in
|
|
55
75
|
GridCraft. Apache POI is © The Apache Software Foundation, licensed under the Apache License, Version 2.0
|
|
@@ -72,6 +92,9 @@ GridCraft. Apache POI is © The Apache Software Foundation, licensed under the A
|
|
|
72
92
|
|
|
73
93
|
- `crates/compare/src/lib.rs` (`diff` with common prefix/suffix stripping, `myers` keeping only the −d..d frontier, patience `anchors` with longest increasing subsequence, recursion depth limit, grouping into inserted/deleted/replaced) → `packages/core/src/compare/diff.ts`
|
|
74
94
|
- Test cases from the file above (`long_documents_with_scattered_edits`, `very_different_documents_finish`, `insertions_deletions_and_replacements`), re-expressed → `packages/core/tests/compare-diff.test.ts`
|
|
95
|
+
- `crates/export/src/lib.rs` (`body_size`: body text size by character count; `level`: heading levels relative to it, bold body-size headings; `tables`: grid columns from aligned cell edges, rows from overlapping spans, spanning header and totals rows next to a run, the 63-column cap) → `packages/core/src/import/pdf-layout.ts`
|
|
96
|
+
- `crates/edit/src/text_regions.rs` (`group_layout`: runs joined on a baseline across small gaps, rows grouped into blocks by pitch and alignment, line-end hyphens removed when joining rows) → `packages/core/src/import/pdf-layout.ts`
|
|
97
|
+
- Test cases from `crates/export/src/lib.rs` (`grid_text_becomes_a_real_table_in_every_format`, `a_staircase_of_blocks_is_not_a_huge_table`, `two_column_body_text_is_not_mistaken_for_a_table`, `word_does_not_invent_borders_for_aligned_invoice_fields`), re-expressed → `packages/core/tests/pdf-layout.test.ts`
|
|
75
98
|
|
|
76
99
|
## photocraft
|
|
77
100
|
|
|
@@ -85,6 +108,11 @@ GridCraft. Apache POI is © The Apache Software Foundation, licensed under the A
|
|
|
85
108
|
- `crates/color/src/convert.rs` (`D50`, `SRGB_TO_XYZ_D50`, `XYZ_D50_TO_SRGB`) → `packages/core/src/image-editor/adjustment-layers.ts`
|
|
86
109
|
- `crates/algo/src/histogram.rs` (bounded RGB histogram, transparent exclusion, exact-endpoint clipping) → `packages/core/src/image-editor/histogram.ts`
|
|
87
110
|
- Test cases from `crates/compose/src/adjust.rs`, `crates/compose/src/tests.rs`, `crates/doc/src/adjust.rs`, `crates/color/src/blend.rs` and `crates/algo/src/histogram.rs`, re-expressed → `packages/core/tests/image-editor-adjustment-layers.test.ts`, `packages/core/tests/image-editor-blend-histogram.test.ts`
|
|
111
|
+
- `crates/algo/src/selection.rs` (`SelectionMode` / `combine`, `magic_wand` / `wand_region` span fill and anti-aliasing, `color_range_samples`, `tone_range`, `hue_range`, `expand` / `contract` / `border`, anti-aliased `polygon`) → `packages/core/src/image-editor/selection/mask.ts`, `shapes.ts`, `wand.ts`, `modify.ts`
|
|
112
|
+
- `crates/algo/src/selection/distance.rs` (Felzenszwalb–Huttenlocher `edt`) and `crates/algo/src/selection_blur.rs` (Smooth: box + threshold; Feather: Gaussian with σ = radius) → `packages/core/src/image-editor/selection/modify.ts`
|
|
113
|
+
- `crates/algo/src/matting.rs` (`RefineParams`, `box_mean`, `guided_filter_color`, `edge_width`, `refine_buffer` band / smooth / feather / contrast / `shift_mask`) → `packages/core/src/image-editor/selection/refine.ts`, `modify.ts`
|
|
114
|
+
- `crates/engine/src/layer_mask_props_cmds.rs` and `crates/compose/src/masks.rs` (non-destructive mask density and feather) → `packages/core/src/image-editor/selection/layer-mask.ts`
|
|
115
|
+
- Test cases from `crates/algo/src/selection.rs`, `crates/algo/src/selection_blur.rs`, `crates/algo/src/matting.rs` and `crates/algo/src/tests_selection_mix.rs`, re-expressed → `packages/core/tests/image-editor-selection.test.ts`, `packages/core/tests/image-editor-selection-controller.test.ts`
|
|
88
116
|
|
|
89
117
|
## soundcraft
|
|
90
118
|
|
|
@@ -102,6 +130,11 @@ GridCraft. Apache POI is © The Apache Software Foundation, licensed under the A
|
|
|
102
130
|
- `crates/pathops/src/fit.rs` (`fit_cubics`, `fit_single`, `fit_single_from`, `generate`, `newton`, `chord_params`, `is_straight`) → `packages/core/src/canvas/curveFit.ts`
|
|
103
131
|
- `crates/pathops/src/edit.rs` (`simplify_with` corner splitting) → `packages/core/src/canvas/curveFit.ts`
|
|
104
132
|
- Test cases from the files above and `crates/pathops/tests/ops.rs`, re-expressed → `packages/core/tests/canvas-resize-snap.test.ts`, `packages/core/tests/canvas-curve-fit.test.ts`
|
|
133
|
+
- `crates/pathops/src/boolean.rs` (`BoolOp::apply`, fill-rule `inside`, `Tidy::keeping` / `exact_merge`: rebuilding split pieces as exact sub-curves of the input curves, `is_sliver` mean-width sliver test) → `packages/core/src/canvas/pathops/boolean.ts`
|
|
134
|
+
- `crates/pathops/src/offset.rs` (`offset_path`: fill ∪ / − a `2·|d|` stroke ring, open subpaths outlined with butt caps, spurious-contour removal by `deep_point` distance; `stroke_region`) → `packages/core/src/canvas/pathops/stroke.ts`
|
|
135
|
+
- `crates/effects/src/stroke/dash.rs` (exact dash plans: odd patterns doubled, offset into the pattern, restart per subpath, joining across the start of a closed subpath, zero-length dots, invalid / too-fine patterns drawn solid) → `packages/core/src/canvas/pathops/stroke.ts`
|
|
136
|
+
- `crates/pathops/src/boolean.rs` (`try_merge` refit of tangent-continuous runs) → `mergeSmoothRuns` in `packages/core/src/canvas/pathops/stroke.ts`
|
|
137
|
+
- Test cases from `crates/pathops/tests/ops.rs`, `tests/props.rs`, `tests/prop_pathops.rs`, `crates/pathops/src/offset.rs` and `crates/engine/src/tests_outlinestroke.rs`, re-expressed → `packages/core/tests/canvas-pathops.test.ts`
|
|
105
138
|
---
|
|
106
139
|
|
|
107
140
|
## wordcraft
|
|
@@ -120,6 +153,13 @@ GridCraft. Apache POI is © The Apache Software Foundation, licensed under the A
|
|
|
120
153
|
- `crates/engine/src/cmd/find_pattern.rs` (`compile`, `special`, `wildcards`, `count`, `class`, `find_code` / `replace_code` / `numeric_code`, `parse_replace`, `expand`) → `packages/core/src/extensions/find-replace/pattern.ts`
|
|
121
154
|
- `crates/engine/src/cmd/edit.rs` (`search_full`: live-text haystack with a position map and cuts at tracked deletions, joining paragraphs only for `^p`, stepping past empty matches, match cap; `replace_all`: last-to-first, tracked path, paragraph splits) → `packages/core/src/extensions/find-replace/search.ts`, `packages/core/src/extensions/find-replace/extension.ts`
|
|
122
155
|
- `crates/engine/src/tests.rs` (`find_replace`, `find_replace_special_codes`, `find_replace_wildcards`, `hostile_find_patterns_are_bounded`, `replace_under_track_changes_skips_deleted_text`, `find_reads_the_text_around_tracked_deletions`, `replace_all_is_tracked`, `replace_all_splits_paragraphs_at_a_paragraph_mark_in_the_replacement`; re-expressed) → `packages/core/tests/find-replace.test.ts`
|
|
156
|
+
- `crates/engine/src/cmd/moves.rs` (`move_sized`; `cut_tracked` / `take_paste`: a tracked cut gets its own revision and the paste right after it, by the same author and of the same text, links both ends with a move name; `resolve`: accepting a move removes its source and keeps its destination, rejecting restores the source and removes the destination, a moved paragraph's emptied block goes with it) and `crates/engine/src/cmd/review.rs` (moves listed as one change, accepting or rejecting either end resolves both) → `packages/core/src/extensions/track-changes.ts`
|
|
157
|
+
- Test cases from `crates/engine/src/cmd/moves.rs` (`a_tracked_cut_and_paste_of_a_paragraph_is_a_move`, `accepting_one_end_of_a_move_accepts_both`, `rejecting_a_move_puts_the_text_back`), re-expressed → `packages/core/tests/track-changes-moves.test.ts`
|
|
158
|
+
- `crates/doc/src/styles.rs` (`Style` with `based_on` / `next` / `linked`, the built-in style set's shape, `gallery`, `chain` (bounded, cycle-safe), `find`, `upsert`, `remove` (rebasing dependants, dropping `next` / `linked` references), `new_id`) and `crates/doc/src/resolve.rs` (cascade: document defaults → paragraph style chain → character style chain → direct formatting) → `packages/core/src/extensions/paragraph-styles/registry.ts`
|
|
159
|
+
- Test cases from `crates/doc/src/styles.rs` (`builtins_present`, `chain_is_cycle_safe`, `new_ids_unique`) and `crates/doc/src/resolve.rs` (`heading_resolves`, `char_style_then_direct`), re-expressed → `packages/core/tests/paragraph-styles.test.ts`
|
|
160
|
+
- `crates/docx/src/write/mod.rs` (package assembly: `[Content_Types].xml` defaults and overrides, part relationships with reused ids, `comments.xml` + `commentsExtended.xml` with `w15:paraIdParent` / `w15:done`, `annotation_author` and anonymous initials for removed personal information, `core_xml` without creator / last modified by, `styles_xml` / `style_xml` with `basedOn` / `next` / `link` / `uiPriority` / `qFormat`, lower-case built-in style names, `numbering_xml`), `crates/docx/src/write/story.rs` (`rev_open` / `rev_close`, `rev_tag` moves as `w:moveFrom` / `w:moveTo`, `move_ranges` with named move ranges, `w:delText` only for deletions that are not moves, `rpr_change` / `w:pPrChange` last in their properties, comment range start / end / reference runs, `w:p` with `w14:paraId` for comments, tables with `w:tblGrid`, `w:gridSpan`, `w:vMerge`, inline `wp:inline` pictures), `crates/docx/src/write/props.rs` (run property order), `crates/docx/src/package.rs` (relationship types), `crates/docx/src/xml.rs` (`esc`, XML 1.0 character filter, writer) and `crates/docx/src/units.rs` (twips / EMU conversion) → `packages/core/src/export/docx.ts`, `packages/core/src/export/xml.ts`, `packages/core/src/export/units.ts`
|
|
161
|
+
- `crates/docx/src/read/mod.rs` (`read_comments`: comments keyed by the `w14:paraId` of their last paragraph, `commentEx` parents and `done`) and `crates/docx/src/read/story.rs` (tracked insertions, deletions and named move ranges) → `packages/core/src/import/docx.ts`
|
|
162
|
+
- Test cases from `crates/docx/tests/privacy.rs` (`remove_personal_information_and_dates_on_save`: the two-commenter fixture, no names or initials in any part, "Author" / "A"), `crates/docx/tests/roundtrip.rs` and `crates/docx/tests/fixtures.rs` (comments, replies, tracked changes and moves surviving a save and reload), re-expressed → `packages/core/tests/docx-export.test.ts`
|
|
123
163
|
|
|
124
164
|
## MIT License
|
|
125
165
|
|
package/dist/agent/index.cjs
CHANGED
|
@@ -5,13 +5,21 @@ var _chunkCPOKFNBGcjs = require('../chunk-CPOKFNBG.cjs');
|
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
|
|
8
|
-
var _chunkXNORY7M6cjs = require('../chunk-XNORY7M6.cjs');
|
|
9
|
-
require('../chunk-D27YFC46.cjs');
|
|
10
8
|
|
|
11
9
|
|
|
10
|
+
var _chunkHDYMLS3Xcjs = require('../chunk-HDYMLS3X.cjs');
|
|
12
11
|
|
|
13
12
|
|
|
14
|
-
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
var _chunkNFVBT3UZcjs = require('../chunk-NFVBT3UZ.cjs');
|
|
16
|
+
require('../chunk-BVP3TSLH.cjs');
|
|
17
|
+
require('../chunk-IFHKKVXH.cjs');
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
var _chunkEWCOFSCQcjs = require('../chunk-EWCOFSCQ.cjs');
|
|
15
23
|
|
|
16
24
|
|
|
17
25
|
|
|
@@ -698,10 +706,11 @@ function createToolRegistry(options) {
|
|
|
698
706
|
}
|
|
699
707
|
|
|
700
708
|
// src/agent/providers.ts
|
|
709
|
+
var _state = require('@tiptap/pm/state');
|
|
701
710
|
var toEditor = (target) => "commands" in target ? target : target.editor;
|
|
702
711
|
function replaceWithText(editor, from, to, text) {
|
|
703
712
|
const { state, view } = editor;
|
|
704
|
-
const tr = /[\r\n]/.test(text) ? state.tr.replace(from, to,
|
|
713
|
+
const tr = /[\r\n]/.test(text) ? state.tr.replace(from, to, _chunkNFVBT3UZcjs.plainTextToSlice.call(void 0, text, state.schema)) : text ? state.tr.insertText(text, from, to) : state.tr.delete(from, to);
|
|
705
714
|
view.dispatch(tr.scrollIntoView());
|
|
706
715
|
}
|
|
707
716
|
function selectionInfo(editor) {
|
|
@@ -734,7 +743,7 @@ function createRichTextToolProvider(target, options) {
|
|
|
734
743
|
},
|
|
735
744
|
handler: ({ format = "text" }) => {
|
|
736
745
|
const ed = editor();
|
|
737
|
-
const content = format === "html" ? ed.getHTML() : format === "markdown" ?
|
|
746
|
+
const content = format === "html" ? ed.getHTML() : format === "markdown" ? _chunkNFVBT3UZcjs.getEditorMarkdown.call(void 0, ed) : _chunkNFVBT3UZcjs.getEditorText.call(void 0, ed);
|
|
738
747
|
return { format, content, selection: selectionInfo(ed), size: ed.state.doc.content.size };
|
|
739
748
|
}
|
|
740
749
|
};
|
|
@@ -793,13 +802,62 @@ function createRichTextToolProvider(target, options) {
|
|
|
793
802
|
return { replaced: { from, to }, selection: selectionInfo(ed) };
|
|
794
803
|
}
|
|
795
804
|
};
|
|
805
|
+
const editTable = {
|
|
806
|
+
name: "edit_table",
|
|
807
|
+
title: "Edit tables",
|
|
808
|
+
description: "Inserts a table or edits the table at the cursor (or at `at`): add/delete rows and columns, delete the table, merge/split cells, toggle header row/column/cell, move between cells, set cell alignment or background. Returns the table size and the cursor cell afterwards.",
|
|
809
|
+
inputSchema: {
|
|
810
|
+
type: "object",
|
|
811
|
+
properties: {
|
|
812
|
+
operation: { type: "string", enum: _chunkHDYMLS3Xcjs.TABLE_ACTIONS.map((a) => a.action), description: "Table operation." },
|
|
813
|
+
at: { type: "integer", minimum: 0, description: "Document position to place the cursor first (e.g. inside a cell)." },
|
|
814
|
+
rows: { type: "integer", minimum: 1, maximum: 100, description: "insertTable: rows (default 3)." },
|
|
815
|
+
cols: { type: "integer", minimum: 1, maximum: 50, description: "insertTable: columns (default 3)." },
|
|
816
|
+
withHeaderRow: { type: "boolean", description: "insertTable: first row is a header row (default true)." },
|
|
817
|
+
align: {
|
|
818
|
+
type: ["string", "null"],
|
|
819
|
+
enum: ["left", "center", "right", "justify", null],
|
|
820
|
+
description: "setCellAlignment: alignment, or null to clear."
|
|
821
|
+
},
|
|
822
|
+
color: { type: ["string", "null"], description: "setCellBackground: CSS color, or null to clear." }
|
|
823
|
+
},
|
|
824
|
+
required: ["operation"],
|
|
825
|
+
additionalProperties: false
|
|
826
|
+
},
|
|
827
|
+
annotations: {
|
|
828
|
+
title: "Edit tables",
|
|
829
|
+
readOnlyHint: false,
|
|
830
|
+
destructiveHint: true,
|
|
831
|
+
idempotentHint: false,
|
|
832
|
+
openWorldHint: false
|
|
833
|
+
},
|
|
834
|
+
handler: ({ operation, at, ...params }) => {
|
|
835
|
+
const ed = editor();
|
|
836
|
+
if (at !== void 0) {
|
|
837
|
+
const size = ed.state.doc.content.size;
|
|
838
|
+
if (at > size) throw new Error(`Position ${at} is outside the document (size ${size}).`);
|
|
839
|
+
ed.view.dispatch(ed.state.tr.setSelection(_state.TextSelection.near(ed.state.doc.resolve(at))));
|
|
840
|
+
}
|
|
841
|
+
const action = operation;
|
|
842
|
+
const state = _chunkHDYMLS3Xcjs.getTableActionState.call(void 0, ed, action, params);
|
|
843
|
+
if (state !== true) throw new Error(state);
|
|
844
|
+
_chunkHDYMLS3Xcjs.runTableAction.call(void 0, ed, action, params);
|
|
845
|
+
const table = _chunkHDYMLS3Xcjs.getTableState.call(void 0, ed);
|
|
846
|
+
return {
|
|
847
|
+
operation,
|
|
848
|
+
table: table.inTable ? { rows: table.rows, columns: table.columns, row: table.row, column: table.column } : null,
|
|
849
|
+
selection: selectionInfo(ed)
|
|
850
|
+
};
|
|
851
|
+
}
|
|
852
|
+
};
|
|
796
853
|
return {
|
|
797
854
|
id: options.id,
|
|
798
855
|
namespace: options.namespace,
|
|
799
856
|
tools: () => {
|
|
800
857
|
const ed = editor();
|
|
801
858
|
if (!ed || ed.isDestroyed) return [];
|
|
802
|
-
|
|
859
|
+
if (!ed.isEditable) return [getText];
|
|
860
|
+
return _chunkHDYMLS3Xcjs.isTableEnabled.call(void 0, ed) ? [getText, insertText, replaceSelection, editTable] : [getText, insertText, replaceSelection];
|
|
803
861
|
},
|
|
804
862
|
subscribe: (notify) => {
|
|
805
863
|
const ed = editor();
|
|
@@ -816,7 +874,7 @@ var CELL_REF_REGEX = /^\$?([A-Za-z]{1,3})\$?(\d{1,7})$/;
|
|
|
816
874
|
function parseCell(ref) {
|
|
817
875
|
const match = CELL_REF_REGEX.exec(ref.trim());
|
|
818
876
|
if (!match) throw new Error(`Invalid cell reference "${ref}". Use A1 notation, e.g. "B3".`);
|
|
819
|
-
return { col:
|
|
877
|
+
return { col: _chunkEWCOFSCQcjs.colNameToIndex.call(void 0, match[1].toUpperCase()), row: parseInt(match[2], 10) - 1 };
|
|
820
878
|
}
|
|
821
879
|
function createSpreadsheetToolProvider(controller, options) {
|
|
822
880
|
const lookup = (row, col) => {
|
|
@@ -864,14 +922,14 @@ function createSpreadsheetToolProvider(controller, options) {
|
|
|
864
922
|
const cell = controller.getCell(found.rowDef.id, found.colDef.id);
|
|
865
923
|
valueRow.push(_nullishCoalesce(cell.error, () => ( cell.value)));
|
|
866
924
|
rawRow.push(cell.raw);
|
|
867
|
-
const error = _nullishCoalesce(cell.error, () => ( (
|
|
868
|
-
if (error) errors.push({ cell: `${
|
|
925
|
+
const error = _nullishCoalesce(cell.error, () => ( (_chunkEWCOFSCQcjs.isFormulaError.call(void 0, cell.value) ? cell.value : void 0)));
|
|
926
|
+
if (error) errors.push({ cell: `${_chunkEWCOFSCQcjs.indexToColName.call(void 0, c)}${r + 1}`, error });
|
|
869
927
|
}
|
|
870
928
|
values.push(valueRow);
|
|
871
929
|
raw.push(rawRow);
|
|
872
930
|
}
|
|
873
931
|
return {
|
|
874
|
-
range: values.length && values[0].length ? `${
|
|
932
|
+
range: values.length && values[0].length ? `${_chunkEWCOFSCQcjs.indexToColName.call(void 0, c0)}${r0 + 1}:${_chunkEWCOFSCQcjs.indexToColName.call(void 0, c1)}${r1 + 1}` : null,
|
|
875
933
|
values,
|
|
876
934
|
raw,
|
|
877
935
|
errors,
|
|
@@ -950,6 +1008,112 @@ function createEditorMcpToolProvider(options) {
|
|
|
950
1008
|
return { id: options.id, namespace: options.namespace, tools };
|
|
951
1009
|
}
|
|
952
1010
|
|
|
1011
|
+
// src/agent/componentTools.ts
|
|
1012
|
+
var IDS = { type: "array", items: { type: "string" }, description: "Node ids (default: the selection)." };
|
|
1013
|
+
var ID = { type: "string", description: "An instance, or a layer inside one (default: the first selected node)." };
|
|
1014
|
+
var READ = { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false };
|
|
1015
|
+
var WRITE = { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false };
|
|
1016
|
+
var check = (result) => {
|
|
1017
|
+
if (!result.ok) throw new Error(result.reason);
|
|
1018
|
+
return { ids: result.ids };
|
|
1019
|
+
};
|
|
1020
|
+
function createCanvasComponentToolProvider(controller, options = { id: "canvas-components" }) {
|
|
1021
|
+
const tool = (name, description, properties, handler, extra = {}) => ({
|
|
1022
|
+
name,
|
|
1023
|
+
description,
|
|
1024
|
+
inputSchema: { type: "object", properties, ...extra.required ? { required: extra.required } : {}, additionalProperties: false },
|
|
1025
|
+
annotations: extra.readOnly ? READ : WRITE,
|
|
1026
|
+
handler
|
|
1027
|
+
});
|
|
1028
|
+
const tools = [
|
|
1029
|
+
tool("list_components", "Lists components (id, name, variant values, instance count) and component sets with their variant options.", {}, () => controller.list(), {
|
|
1030
|
+
readOnly: true
|
|
1031
|
+
}),
|
|
1032
|
+
tool(
|
|
1033
|
+
"inspect_instance",
|
|
1034
|
+
"Describes an instance: its main component, variant, component property values (with types) and the overrides of its layers.",
|
|
1035
|
+
{ id: ID },
|
|
1036
|
+
({ id }) => {
|
|
1037
|
+
const info = controller.inspect(id);
|
|
1038
|
+
if (!info) throw new Error("Not an instance (or a layer inside one).");
|
|
1039
|
+
return info;
|
|
1040
|
+
},
|
|
1041
|
+
{ readOnly: true }
|
|
1042
|
+
),
|
|
1043
|
+
tool(
|
|
1044
|
+
"create_component",
|
|
1045
|
+
"Turns the given frame into a main component, or wraps the given layers in a new one.",
|
|
1046
|
+
{ ids: IDS },
|
|
1047
|
+
({ ids }) => check(controller.createComponent(ids))
|
|
1048
|
+
),
|
|
1049
|
+
tool(
|
|
1050
|
+
"combine_as_variants",
|
|
1051
|
+
'Wraps sibling components in a component set (variants named "Property=Value, \u2026").',
|
|
1052
|
+
{ ids: IDS },
|
|
1053
|
+
({ ids }) => check(controller.combineAsVariants(ids))
|
|
1054
|
+
),
|
|
1055
|
+
tool(
|
|
1056
|
+
"create_instance",
|
|
1057
|
+
"Places an instance of a component or component set, optionally inside a frame, at a position, with property values.",
|
|
1058
|
+
{
|
|
1059
|
+
componentId: { type: "string" },
|
|
1060
|
+
parentId: { type: "string" },
|
|
1061
|
+
x: { type: "number" },
|
|
1062
|
+
y: { type: "number" },
|
|
1063
|
+
properties: { type: "object", additionalProperties: true, description: "Variant and component property values." }
|
|
1064
|
+
},
|
|
1065
|
+
({ componentId, parentId, x, y, properties }) => check(
|
|
1066
|
+
controller.createInstance(componentId, {
|
|
1067
|
+
...parentId !== void 0 ? { parentId } : {},
|
|
1068
|
+
...x !== void 0 || y !== void 0 ? { position: { x: _nullishCoalesce(x, () => ( 0)), y: _nullishCoalesce(y, () => ( 0)) } } : {},
|
|
1069
|
+
...properties ? { properties } : {}
|
|
1070
|
+
})
|
|
1071
|
+
),
|
|
1072
|
+
{ required: ["componentId"] }
|
|
1073
|
+
),
|
|
1074
|
+
tool(
|
|
1075
|
+
"set_instance_properties",
|
|
1076
|
+
"Sets property values of instances: variant properties switch the variant (keeping compatible overrides), booleans toggle layers, text sets text, instance swap takes a component id.",
|
|
1077
|
+
{ properties: { type: "object", additionalProperties: true }, ids: IDS },
|
|
1078
|
+
({ properties, ids }) => check(controller.setInstanceProperties(properties, ids)),
|
|
1079
|
+
{ required: ["properties"] }
|
|
1080
|
+
),
|
|
1081
|
+
tool(
|
|
1082
|
+
"swap_instance",
|
|
1083
|
+
"Makes instances show another component (or a set\u2019s default variant), keeping overrides of layers with matching names.",
|
|
1084
|
+
{ componentId: { type: "string" }, ids: IDS },
|
|
1085
|
+
({ componentId, ids }) => check(controller.swapInstance(componentId, ids)),
|
|
1086
|
+
{ required: ["componentId"] }
|
|
1087
|
+
),
|
|
1088
|
+
tool(
|
|
1089
|
+
"reset_overrides",
|
|
1090
|
+
'Resets overrides: of a whole instance (default), of one layer, or one override group such as "data.label" or "shape.fill".',
|
|
1091
|
+
{ id: ID, group: { type: "string" } },
|
|
1092
|
+
({ id, group }) => check(controller.resetOverrides(id, group))
|
|
1093
|
+
),
|
|
1094
|
+
tool(
|
|
1095
|
+
"push_overrides",
|
|
1096
|
+
"Copies an instance\u2019s overrides to its main component (and so to every other instance).",
|
|
1097
|
+
{ id: ID },
|
|
1098
|
+
({ id }) => check(controller.pushOverrides(id))
|
|
1099
|
+
),
|
|
1100
|
+
tool("detach_instance", "Turns instances into plain frames that keep their current look.", { ids: IDS }, ({ ids }) => check(controller.detachInstance(ids))),
|
|
1101
|
+
tool("go_to_main_component", "Selects the main component of an instance and returns its id.", { id: ID }, ({ id }) => {
|
|
1102
|
+
const main = controller.goToMainComponent(id);
|
|
1103
|
+
if (!main) throw new Error("The main component was not found.");
|
|
1104
|
+
return { id: main };
|
|
1105
|
+
}),
|
|
1106
|
+
tool(
|
|
1107
|
+
"restore_main_component",
|
|
1108
|
+
"Turns an instance whose main component was deleted back into the main component.",
|
|
1109
|
+
{ id: ID },
|
|
1110
|
+
({ id }) => check(controller.restoreMainComponent(id))
|
|
1111
|
+
)
|
|
1112
|
+
];
|
|
1113
|
+
return { id: options.id, namespace: options.namespace, tools };
|
|
1114
|
+
}
|
|
1115
|
+
|
|
1116
|
+
|
|
953
1117
|
|
|
954
1118
|
|
|
955
1119
|
|
|
@@ -969,5 +1133,5 @@ function createEditorMcpToolProvider(options) {
|
|
|
969
1133
|
|
|
970
1134
|
|
|
971
1135
|
|
|
972
|
-
exports.EDITOR_MCP_TOOLS = EDITOR_MCP_TOOLS; exports.EXPORT_EXCLUDED_SELECTORS = EXPORT_EXCLUDED_SELECTORS; exports.MCP_TOOL_EDIT_CANVAS = MCP_TOOL_EDIT_CANVAS; exports.MCP_TOOL_EDIT_PROMPT = MCP_TOOL_EDIT_PROMPT; exports.MCP_TOOL_GET_EDITOR_STATE = MCP_TOOL_GET_EDITOR_STATE; exports.MCP_TOOL_VIEW_EDITOR = MCP_TOOL_VIEW_EDITOR; exports.TOOL_NAMESPACE_SEPARATOR = TOOL_NAMESPACE_SEPARATOR; exports.ToolRegistry = ToolRegistry; exports.ToolRegistryError = ToolRegistryError; exports.createEditorMcpToolProvider = createEditorMcpToolProvider; exports.createRichTextToolProvider = createRichTextToolProvider; exports.createSpreadsheetToolProvider = createSpreadsheetToolProvider; exports.createToolRegistry = createToolRegistry; exports.normalizeSide = normalizeSide; exports.planAgentCommand = planAgentCommand; exports.planToolCall = planToolCall; exports.toToolResult = toToolResult; exports.toolCallToAgentCommand = toolCallToAgentCommand; exports.validateJsonSchema = _chunkCPOKFNBGcjs.validateJsonSchema;
|
|
1136
|
+
exports.EDITOR_MCP_TOOLS = EDITOR_MCP_TOOLS; exports.EXPORT_EXCLUDED_SELECTORS = EXPORT_EXCLUDED_SELECTORS; exports.MCP_TOOL_EDIT_CANVAS = MCP_TOOL_EDIT_CANVAS; exports.MCP_TOOL_EDIT_PROMPT = MCP_TOOL_EDIT_PROMPT; exports.MCP_TOOL_GET_EDITOR_STATE = MCP_TOOL_GET_EDITOR_STATE; exports.MCP_TOOL_VIEW_EDITOR = MCP_TOOL_VIEW_EDITOR; exports.TOOL_NAMESPACE_SEPARATOR = TOOL_NAMESPACE_SEPARATOR; exports.ToolRegistry = ToolRegistry; exports.ToolRegistryError = ToolRegistryError; exports.createCanvasComponentToolProvider = createCanvasComponentToolProvider; exports.createEditorMcpToolProvider = createEditorMcpToolProvider; exports.createRichTextToolProvider = createRichTextToolProvider; exports.createSpreadsheetToolProvider = createSpreadsheetToolProvider; exports.createToolRegistry = createToolRegistry; exports.normalizeSide = normalizeSide; exports.planAgentCommand = planAgentCommand; exports.planToolCall = planToolCall; exports.toToolResult = toToolResult; exports.toolCallToAgentCommand = toolCallToAgentCommand; exports.validateJsonSchema = _chunkCPOKFNBGcjs.validateJsonSchema;
|
|
973
1137
|
//# sourceMappingURL=index.cjs.map
|