@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.
Files changed (162) hide show
  1. package/README.md +101 -1
  2. package/THIRD_PARTY_NOTICES.md +40 -0
  3. package/dist/agent/index.cjs +175 -11
  4. package/dist/agent/index.cjs.map +1 -1
  5. package/dist/agent/index.d.cts +18 -4
  6. package/dist/agent/index.d.ts +18 -4
  7. package/dist/agent/index.js +168 -4
  8. package/dist/agent/index.js.map +1 -1
  9. package/dist/{arrangeShortcuts-B8O3f8-P.d.cts → arrange-BICutYPk.d.cts} +243 -29
  10. package/dist/{arrangeShortcuts-sLlD2rSX.d.ts → arrange-BICutYPk.d.ts} +243 -29
  11. package/dist/builtins-CdPcLwwF.d.cts +176 -0
  12. package/dist/builtins-Chj6zv_O.d.ts +176 -0
  13. package/dist/canvas/index.cjs +8588 -1857
  14. package/dist/canvas/index.cjs.map +1 -1
  15. package/dist/canvas/index.d.cts +1847 -9
  16. package/dist/canvas/index.d.ts +1847 -9
  17. package/dist/canvas/index.js +8577 -1846
  18. package/dist/canvas/index.js.map +1 -1
  19. package/dist/chunk-3CC7OPUQ.cjs +831 -0
  20. package/dist/chunk-3CC7OPUQ.cjs.map +1 -0
  21. package/dist/chunk-4BVP64JJ.cjs +1406 -0
  22. package/dist/chunk-4BVP64JJ.cjs.map +1 -0
  23. package/dist/chunk-BVP3TSLH.cjs +69 -0
  24. package/dist/chunk-BVP3TSLH.cjs.map +1 -0
  25. package/dist/{chunk-OLZJXL4R.cjs → chunk-CS6NZWWA.cjs} +59 -25
  26. package/dist/chunk-CS6NZWWA.cjs.map +1 -0
  27. package/dist/{chunk-MCSJFWAO.cjs → chunk-EWCOFSCQ.cjs} +685 -100
  28. package/dist/chunk-EWCOFSCQ.cjs.map +1 -0
  29. package/dist/chunk-HDYMLS3X.cjs +409 -0
  30. package/dist/chunk-HDYMLS3X.cjs.map +1 -0
  31. package/dist/chunk-HRMEDT2L.js +1406 -0
  32. package/dist/chunk-HRMEDT2L.js.map +1 -0
  33. package/dist/{chunk-D27YFC46.cjs → chunk-IFHKKVXH.cjs} +382 -63
  34. package/dist/chunk-IFHKKVXH.cjs.map +1 -0
  35. package/dist/{chunk-PGNZKFQS.js → chunk-JOIREKL7.js} +137 -58
  36. package/dist/chunk-JOIREKL7.js.map +1 -0
  37. package/dist/chunk-KRGXUQ2Y.js +1696 -0
  38. package/dist/chunk-KRGXUQ2Y.js.map +1 -0
  39. package/dist/{chunk-3LV6RRPH.js → chunk-MHEPLBFB.js} +2 -2
  40. package/dist/chunk-MR2764X2.js +831 -0
  41. package/dist/chunk-MR2764X2.js.map +1 -0
  42. package/dist/{chunk-XNORY7M6.cjs → chunk-NFVBT3UZ.cjs} +166 -87
  43. package/dist/chunk-NFVBT3UZ.cjs.map +1 -0
  44. package/dist/chunk-NQF32I6Z.cjs +6478 -0
  45. package/dist/chunk-NQF32I6Z.cjs.map +1 -0
  46. package/dist/chunk-O5RYH2PG.js +69 -0
  47. package/dist/chunk-O5RYH2PG.js.map +1 -0
  48. package/dist/{chunk-ITBSKI26.js → chunk-Q2OHWD2A.js} +1064 -360
  49. package/dist/chunk-Q2OHWD2A.js.map +1 -0
  50. package/dist/{chunk-FODNLX5N.js → chunk-QJSOZFCZ.js} +38 -4
  51. package/dist/chunk-QJSOZFCZ.js.map +1 -0
  52. package/dist/{chunk-SML64VJG.cjs → chunk-RRREAOVM.cjs} +4 -4
  53. package/dist/{chunk-SML64VJG.cjs.map → chunk-RRREAOVM.cjs.map} +1 -1
  54. package/dist/{chunk-5S3Z4PPF.js → chunk-SWE3NPOU.js} +347 -28
  55. package/dist/chunk-SWE3NPOU.js.map +1 -0
  56. package/dist/chunk-TXXNBDQX.cjs +1696 -0
  57. package/dist/chunk-TXXNBDQX.cjs.map +1 -0
  58. package/dist/chunk-UBCFIABU.js +409 -0
  59. package/dist/chunk-UBCFIABU.js.map +1 -0
  60. package/dist/chunk-X7HXL2SB.js +6478 -0
  61. package/dist/chunk-X7HXL2SB.js.map +1 -0
  62. package/dist/{chunk-SBCZ6RN5.cjs → chunk-YOMYSFJU.cjs} +1163 -459
  63. package/dist/chunk-YOMYSFJU.cjs.map +1 -0
  64. package/dist/{chunk-3CN2WD3X.js → chunk-ZRNHGAQT.js} +661 -76
  65. package/dist/chunk-ZRNHGAQT.js.map +1 -0
  66. package/dist/commands/index.cjs +425 -4
  67. package/dist/commands/index.cjs.map +1 -1
  68. package/dist/commands/index.d.cts +39 -77
  69. package/dist/commands/index.d.ts +39 -77
  70. package/dist/commands/index.js +424 -3
  71. package/dist/commands/index.js.map +1 -1
  72. package/dist/compare/index.cjs +3 -3
  73. package/dist/compare/index.d.cts +1 -1
  74. package/dist/compare/index.d.ts +1 -1
  75. package/dist/compare/index.js +2 -2
  76. package/dist/controller-CxXGOEmy.d.cts +2038 -0
  77. package/dist/controller-CxXGOEmy.d.ts +2038 -0
  78. package/dist/{documents-BQjzJNe2.d.ts → documents-CHT1U5Vs.d.ts} +10 -0
  79. package/dist/{documents-KRTPhvst.d.cts → documents-Gp7wb0Me.d.cts} +10 -0
  80. package/dist/export/index.cjs +36 -0
  81. package/dist/export/index.cjs.map +1 -0
  82. package/dist/export/index.d.cts +243 -0
  83. package/dist/export/index.d.ts +243 -0
  84. package/dist/export/index.js +36 -0
  85. package/dist/export/index.js.map +1 -0
  86. package/dist/extensions/index.cjs +101 -6
  87. package/dist/extensions/index.cjs.map +1 -1
  88. package/dist/extensions/index.d.cts +18 -5
  89. package/dist/extensions/index.d.ts +18 -5
  90. package/dist/extensions/index.js +101 -6
  91. package/dist/generation/index.cjs +1013 -69
  92. package/dist/generation/index.cjs.map +1 -1
  93. package/dist/generation/index.d.cts +474 -13
  94. package/dist/generation/index.d.ts +474 -13
  95. package/dist/generation/index.js +976 -32
  96. package/dist/generation/index.js.map +1 -1
  97. package/dist/image-editor/index.cjs +3385 -1275
  98. package/dist/image-editor/index.cjs.map +1 -1
  99. package/dist/image-editor/index.d.cts +693 -128
  100. package/dist/image-editor/index.d.ts +693 -128
  101. package/dist/image-editor/index.js +3325 -1215
  102. package/dist/image-editor/index.js.map +1 -1
  103. package/dist/{index-fJrlkJCd.d.ts → index-B273Zllk.d.ts} +500 -7
  104. package/dist/{index-BhJBSm2d.d.cts → index-jcJwmBYt.d.cts} +500 -7
  105. package/dist/index.cjs +1603 -45
  106. package/dist/index.cjs.map +1 -1
  107. package/dist/index.d.cts +154 -27
  108. package/dist/index.d.ts +154 -27
  109. package/dist/index.js +1584 -26
  110. package/dist/index.js.map +1 -1
  111. package/dist/mention-text-CDnCFd3c.d.cts +53 -0
  112. package/dist/mention-text-CDnCFd3c.d.ts +53 -0
  113. package/dist/model-C1zApQM8.d.cts +219 -0
  114. package/dist/model-C1zApQM8.d.ts +219 -0
  115. package/dist/persistence/index.d.cts +3 -120
  116. package/dist/persistence/index.d.ts +3 -120
  117. package/dist/registry-CED5C0lA.d.cts +204 -0
  118. package/dist/registry-CED5C0lA.d.ts +204 -0
  119. package/dist/{registry-GJI4gToU.d.cts → registry-CEgwXED1.d.cts} +12 -1
  120. package/dist/{registry-GJI4gToU.d.ts → registry-CEgwXED1.d.ts} +12 -1
  121. package/dist/{registry-D32LpIc4.d.cts → registry-D0GADKc7.d.cts} +1 -1
  122. package/dist/{registry-evKTP4ls.d.ts → registry-DhVUZioj.d.ts} +1 -1
  123. package/dist/serializers/index.cjs +12 -4
  124. package/dist/serializers/index.cjs.map +1 -1
  125. package/dist/serializers/index.d.cts +41 -13
  126. package/dist/serializers/index.d.ts +41 -13
  127. package/dist/serializers/index.js +12 -4
  128. package/dist/spreadsheet/index.cjs +157 -3
  129. package/dist/spreadsheet/index.cjs.map +1 -1
  130. package/dist/spreadsheet/index.d.cts +534 -11
  131. package/dist/spreadsheet/index.d.ts +534 -11
  132. package/dist/spreadsheet/index.js +157 -3
  133. package/dist/storage-BpFfspHm.d.cts +120 -0
  134. package/dist/storage-BpFfspHm.d.ts +120 -0
  135. package/dist/tokens/index.cjs +115 -0
  136. package/dist/tokens/index.cjs.map +1 -0
  137. package/dist/tokens/index.d.cts +212 -0
  138. package/dist/tokens/index.d.ts +212 -0
  139. package/dist/tokens/index.js +115 -0
  140. package/dist/tokens/index.js.map +1 -0
  141. package/dist/{track-changes-BK5sVf4_.d.cts → track-changes-D_VAPELi.d.cts} +95 -11
  142. package/dist/{track-changes-CDjCrwKe.d.ts → track-changes-Db_fGEvx.d.ts} +95 -11
  143. package/package.json +15 -1
  144. package/dist/chunk-3CN2WD3X.js.map +0 -1
  145. package/dist/chunk-5S3Z4PPF.js.map +0 -1
  146. package/dist/chunk-D27YFC46.cjs.map +0 -1
  147. package/dist/chunk-FODNLX5N.js.map +0 -1
  148. package/dist/chunk-ITBSKI26.js.map +0 -1
  149. package/dist/chunk-MA2F5KHF.cjs +0 -3223
  150. package/dist/chunk-MA2F5KHF.cjs.map +0 -1
  151. package/dist/chunk-MCSJFWAO.cjs.map +0 -1
  152. package/dist/chunk-OLZJXL4R.cjs.map +0 -1
  153. package/dist/chunk-PGNZKFQS.js.map +0 -1
  154. package/dist/chunk-SBCZ6RN5.cjs.map +0 -1
  155. package/dist/chunk-U5KWZU4X.js +0 -3223
  156. package/dist/chunk-U5KWZU4X.js.map +0 -1
  157. package/dist/chunk-XNORY7M6.cjs.map +0 -1
  158. package/dist/controller-BhLLAoi8.d.cts +0 -1000
  159. package/dist/controller-BhLLAoi8.d.ts +0 -1000
  160. package/dist/types-DX4Mh4SL.d.cts +0 -217
  161. package/dist/types-DX4Mh4SL.d.ts +0 -217
  162. /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) |
@@ -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
 
@@ -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
- var _chunkMCSJFWAOcjs = require('../chunk-MCSJFWAO.cjs');
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, _chunkXNORY7M6cjs.plainTextToSlice.call(void 0, text, state.schema)) : text ? state.tr.insertText(text, from, to) : state.tr.delete(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" ? _chunkXNORY7M6cjs.getEditorMarkdown.call(void 0, ed) : _chunkXNORY7M6cjs.getEditorText.call(void 0, ed);
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
- return ed.isEditable ? [getText, insertText, replaceSelection] : [getText];
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: _chunkMCSJFWAOcjs.colNameToIndex.call(void 0, match[1].toUpperCase()), row: parseInt(match[2], 10) - 1 };
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, () => ( (_chunkMCSJFWAOcjs.isFormulaError.call(void 0, cell.value) ? cell.value : void 0)));
868
- if (error) errors.push({ cell: `${_chunkMCSJFWAOcjs.indexToColName.call(void 0, c)}${r + 1}`, error });
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 ? `${_chunkMCSJFWAOcjs.indexToColName.call(void 0, c0)}${r0 + 1}:${_chunkMCSJFWAOcjs.indexToColName.call(void 0, c1)}${r1 + 1}` : null,
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