@pixerate/editor 0.23.0 → 0.25.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 +167 -2
- package/THIRD_PARTY_NOTICES.md +80 -0
- package/dist/agent/index.cjs +10 -8
- package/dist/agent/index.cjs.map +1 -1
- package/dist/agent/index.d.cts +4 -3
- package/dist/agent/index.d.ts +4 -3
- package/dist/agent/index.js +4 -2
- package/dist/agent/index.js.map +1 -1
- package/dist/canvas/index.cjs +4437 -411
- package/dist/canvas/index.cjs.map +1 -1
- package/dist/canvas/index.d.cts +934 -4
- package/dist/canvas/index.d.ts +934 -4
- package/dist/canvas/index.js +4309 -283
- package/dist/canvas/index.js.map +1 -1
- package/dist/chunk-4BVP64JJ.cjs +1406 -0
- package/dist/chunk-4BVP64JJ.cjs.map +1 -0
- package/dist/chunk-664LQYFJ.js +115 -0
- package/dist/chunk-664LQYFJ.js.map +1 -0
- package/dist/chunk-7WISYZFO.cjs +740 -0
- package/dist/chunk-7WISYZFO.cjs.map +1 -0
- package/dist/chunk-AL6JMGWW.cjs +783 -0
- package/dist/chunk-AL6JMGWW.cjs.map +1 -0
- package/dist/chunk-BVP3TSLH.cjs +69 -0
- package/dist/chunk-BVP3TSLH.cjs.map +1 -0
- package/dist/{chunk-RIKBJLS2.cjs → chunk-C7P22VIP.cjs} +2001 -279
- package/dist/chunk-C7P22VIP.cjs.map +1 -0
- package/dist/chunk-D6FH2YAX.cjs +131 -0
- package/dist/chunk-D6FH2YAX.cjs.map +1 -0
- package/dist/chunk-ECCCYKGL.js +740 -0
- package/dist/chunk-ECCCYKGL.js.map +1 -0
- package/dist/{chunk-4SUNJZWB.cjs → chunk-FD4LPFAE.cjs} +48 -200
- package/dist/chunk-FD4LPFAE.cjs.map +1 -0
- package/dist/{chunk-QBJ22YZR.js → chunk-FH6F67DN.js} +1999 -277
- package/dist/chunk-FH6F67DN.js.map +1 -0
- package/dist/{chunk-OYXSKQFR.js → chunk-FPENXAEJ.js} +35 -187
- package/dist/chunk-FPENXAEJ.js.map +1 -0
- package/dist/chunk-HLIMTC6N.js +1 -0
- package/dist/chunk-HLIMTC6N.js.map +1 -0
- package/dist/chunk-HRMEDT2L.js +1406 -0
- package/dist/chunk-HRMEDT2L.js.map +1 -0
- package/dist/{chunk-QGHGJ56O.js → chunk-IXHSYOVY.js} +2518 -122
- package/dist/chunk-IXHSYOVY.js.map +1 -0
- package/dist/chunk-MKA3AHLV.cjs +256 -0
- package/dist/chunk-MKA3AHLV.cjs.map +1 -0
- package/dist/chunk-NAXVVS5I.js +783 -0
- package/dist/chunk-NAXVVS5I.js.map +1 -0
- package/dist/chunk-O5RYH2PG.js +69 -0
- package/dist/chunk-O5RYH2PG.js.map +1 -0
- package/dist/chunk-OXSV64JX.js +689 -0
- package/dist/chunk-OXSV64JX.js.map +1 -0
- package/dist/chunk-P3RQ5UJE.cjs +689 -0
- package/dist/chunk-P3RQ5UJE.cjs.map +1 -0
- package/dist/chunk-P6ODPZ4W.js +256 -0
- package/dist/chunk-P6ODPZ4W.js.map +1 -0
- package/dist/chunk-PL4JB3BF.cjs +1 -0
- package/dist/chunk-PL4JB3BF.cjs.map +1 -0
- package/dist/{chunk-GVSMRPHB.cjs → chunk-PO6A6QQH.cjs} +2607 -211
- package/dist/chunk-PO6A6QQH.cjs.map +1 -0
- package/dist/{chunk-JJDLPEWL.js → chunk-QGYDWPRM.js} +2 -2
- package/dist/chunk-RR7F5GUM.js +5726 -0
- package/dist/chunk-RR7F5GUM.js.map +1 -0
- package/dist/chunk-TNNJXF46.cjs +115 -0
- package/dist/chunk-TNNJXF46.cjs.map +1 -0
- package/dist/chunk-U5HZSFCS.js +131 -0
- package/dist/chunk-U5HZSFCS.js.map +1 -0
- package/dist/{chunk-MJZBNWG7.js → chunk-UAKDG62Z.js} +309 -581
- package/dist/chunk-UAKDG62Z.js.map +1 -0
- package/dist/{chunk-CL37JGFW.cjs → chunk-V3CHHWOE.cjs} +4 -4
- package/dist/{chunk-CL37JGFW.cjs.map → chunk-V3CHHWOE.cjs.map} +1 -1
- package/dist/chunk-WSQKHXW7.cjs +5726 -0
- package/dist/chunk-WSQKHXW7.cjs.map +1 -0
- package/dist/{chunk-AA4ZEVCC.cjs → chunk-X4N4WUKB.cjs} +336 -608
- package/dist/chunk-X4N4WUKB.cjs.map +1 -0
- package/dist/commands/index.cjs +74 -1
- package/dist/commands/index.cjs.map +1 -1
- package/dist/commands/index.d.cts +38 -353
- package/dist/commands/index.d.ts +38 -353
- package/dist/commands/index.js +73 -0
- package/dist/commands/index.js.map +1 -1
- package/dist/comment-store-BEMgVlAA.d.cts +135 -0
- package/dist/comment-store-BEMgVlAA.d.ts +135 -0
- package/dist/compare/index.cjs +15 -0
- package/dist/compare/index.cjs.map +1 -0
- package/dist/compare/index.d.cts +58 -0
- package/dist/compare/index.d.ts +58 -0
- package/dist/compare/index.js +15 -0
- package/dist/compare/index.js.map +1 -0
- package/dist/controller-BIJiNoVs.d.cts +1842 -0
- package/dist/controller-BIJiNoVs.d.ts +1842 -0
- package/dist/documents-CHT1U5Vs.d.ts +86 -0
- package/dist/documents-Gp7wb0Me.d.cts +86 -0
- package/dist/extensions/index.cjs +100 -5
- package/dist/extensions/index.cjs.map +1 -1
- package/dist/extensions/index.d.cts +16 -4
- package/dist/extensions/index.d.ts +16 -4
- package/dist/extensions/index.js +100 -5
- package/dist/generation/index.cjs +2317 -0
- package/dist/generation/index.cjs.map +1 -0
- package/dist/generation/index.d.cts +1015 -0
- package/dist/generation/index.d.ts +1015 -0
- package/dist/generation/index.js +2317 -0
- package/dist/generation/index.js.map +1 -0
- package/dist/grammar/index.cjs +48 -3
- package/dist/grammar/index.cjs.map +1 -1
- package/dist/grammar/index.d.cts +322 -100
- package/dist/grammar/index.d.ts +322 -100
- package/dist/grammar/index.js +54 -9
- package/dist/image-editor/index.cjs +4392 -481
- package/dist/image-editor/index.cjs.map +1 -1
- package/dist/image-editor/index.d.cts +1249 -97
- package/dist/image-editor/index.d.ts +1249 -97
- package/dist/image-editor/index.js +4281 -370
- package/dist/image-editor/index.js.map +1 -1
- package/dist/index-7a6bHTLy.d.ts +1754 -0
- package/dist/index-Csd5qI8u.d.cts +1754 -0
- package/dist/index.cjs +1682 -99
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +118 -21
- package/dist/index.d.ts +118 -21
- package/dist/index.js +1630 -47
- 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/migrate-iTlm3a8h.d.cts +112 -0
- package/dist/migrate-iTlm3a8h.d.ts +112 -0
- package/dist/model-C1zApQM8.d.cts +219 -0
- package/dist/model-C1zApQM8.d.ts +219 -0
- package/dist/persistence/index.cjs +676 -0
- package/dist/persistence/index.cjs.map +1 -0
- package/dist/persistence/index.d.cts +230 -0
- package/dist/persistence/index.d.ts +230 -0
- package/dist/persistence/index.js +676 -0
- package/dist/persistence/index.js.map +1 -0
- package/dist/{registry-D5-R90p0.d.cts → registry-BDe-vlv3.d.ts} +2 -40
- package/dist/{registry-GJI4gToU.d.ts → registry-CEgwXED1.d.cts} +12 -1
- package/dist/{registry-GJI4gToU.d.cts → registry-CEgwXED1.d.ts} +12 -1
- package/dist/registry-D32LpIc4.d.cts +354 -0
- package/dist/{registry-D5-R90p0.d.ts → registry-DnzpgY27.d.cts} +2 -40
- package/dist/registry-evKTP4ls.d.ts +354 -0
- package/dist/schema-DYDGdYL9.d.cts +41 -0
- package/dist/schema-DYDGdYL9.d.ts +41 -0
- package/dist/serializers/index.cjs +12 -3
- package/dist/serializers/index.cjs.map +1 -1
- package/dist/serializers/index.d.cts +40 -12
- package/dist/serializers/index.d.ts +40 -12
- package/dist/serializers/index.js +12 -3
- package/dist/sourcemap-C8AH3MBQ.d.cts +91 -0
- package/dist/sourcemap-DA1Evhrv.d.ts +91 -0
- package/dist/spreadsheet/index.cjs +161 -3
- package/dist/spreadsheet/index.cjs.map +1 -1
- package/dist/spreadsheet/index.d.cts +611 -20
- package/dist/spreadsheet/index.d.ts +611 -20
- package/dist/spreadsheet/index.js +168 -10
- 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/{tokens-CyLZmIei.d.cts → tokens-D7yXvUU6.d.cts} +15 -2
- package/dist/{tokens-CyLZmIei.d.ts → tokens-D7yXvUU6.d.ts} +15 -2
- package/dist/{track-changes-BSF58RK0.d.cts → track-changes-D_VAPELi.d.cts} +110 -144
- package/dist/{track-changes-BSF58RK0.d.ts → track-changes-Db_fGEvx.d.ts} +110 -144
- package/package.json +21 -1
- package/dist/chunk-4SUNJZWB.cjs.map +0 -1
- package/dist/chunk-AA4ZEVCC.cjs.map +0 -1
- package/dist/chunk-AXV2ZW34.cjs +0 -118
- package/dist/chunk-AXV2ZW34.cjs.map +0 -1
- package/dist/chunk-B2ZRLN4S.js +0 -118
- package/dist/chunk-B2ZRLN4S.js.map +0 -1
- package/dist/chunk-GVSMRPHB.cjs.map +0 -1
- package/dist/chunk-K3R5NG2D.js +0 -3100
- package/dist/chunk-K3R5NG2D.js.map +0 -1
- package/dist/chunk-MJZBNWG7.js.map +0 -1
- package/dist/chunk-OYXSKQFR.js.map +0 -1
- package/dist/chunk-QBJ22YZR.js.map +0 -1
- package/dist/chunk-QGHGJ56O.js.map +0 -1
- package/dist/chunk-RIKBJLS2.cjs.map +0 -1
- package/dist/chunk-XDMXXC4Z.cjs +0 -3100
- package/dist/chunk-XDMXXC4Z.cjs.map +0 -1
- package/dist/controller-pePCJLT1.d.cts +0 -959
- package/dist/controller-pePCJLT1.d.ts +0 -959
- package/dist/index-CCr_z9kF.d.cts +0 -899
- package/dist/index-Ck6ajwXh.d.ts +0 -899
- /package/dist/{chunk-JJDLPEWL.js.map → chunk-QGYDWPRM.js.map} +0 -0
package/README.md
CHANGED
|
@@ -119,6 +119,26 @@ editor.commands.acceptChange(id); // rejectChange, acceptAllChanges, rejectAllCh
|
|
|
119
119
|
- **Serializers** render tracked content as if all changes were accepted: `getEditorText`, `htmlToPlainText` and `getEditorMarkdown` drop deleted text and use the new formatting and block types by default. Pass `{ trackedChanges: "original" }` (as if rejected) or `"all"` (both). `resolveTrackedChanges(doc, view)` returns a resolved document. TipTap's own `editor.getText()` still includes deleted text.
|
|
120
120
|
- Both extensions are also available as `createRichTextPreset({ comments: true, trackChanges: true })`. Mark styles use Tailwind classes; override them via `commentClass` / `activeClass` and `insertionHTMLAttributes` / `deletionHTMLAttributes` / `formatChangeHTMLAttributes` / `blockChangeClass`.
|
|
121
121
|
|
|
122
|
+
## Find and replace
|
|
123
|
+
|
|
124
|
+
```ts
|
|
125
|
+
import { createRichTextPreset, getFindReplaceState } from "@pixerate/editor";
|
|
126
|
+
|
|
127
|
+
const editor = new Editor({ extensions: [...createRichTextPreset({ findReplace: true })] }); // or FindReplace.configure({...})
|
|
128
|
+
|
|
129
|
+
editor.commands.setSearch({ query: "([A-Z][a-z]@), ([A-Z][a-z]@)", replacement: "\\2 \\1", options: { wildcards: true } });
|
|
130
|
+
editor.commands.findNext(); // findPrevious, replaceCurrent, replaceAll (one undo step), clearSearch
|
|
131
|
+
editor.commands.openFindReplace({ mode: "replace" }); // what Mod+F / Mod+H do; closeFindReplace hides it
|
|
132
|
+
getFindReplaceState(editor); // { open, mode, query, replacement, options, matches, currentIndex, error, truncated, lastReplaceCount }
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
- **Options** (`FindOptions`): `matchCase`, `wholeWord`, `matchPrefix`, `matchSuffix`, `ignorePunctuation`, `ignoreWhitespace`, `wildcards`, `regex`, `literal` (no `^` codes).
|
|
136
|
+
- **Special characters** (Word): `^p` paragraph mark (a pattern with `^p` reads sibling paragraphs together; `^p` in the replacement splits the paragraph), `^t` tab, `^l` line break, `^?` any character, `^#` digit, `^$` letter, `^w` white space, `^^` caret, `^+` / `^=` em / en dash, `^s` non-breaking space, `^13` / `^u8212` character codes. In Replace with: `^&` found text, `^c` clipboard (`getClipboardText` option).
|
|
137
|
+
- **Wildcards** (Word): `?`, `*` (shortest run within a paragraph), `[a-z]`, `[!x]`, `{n}`, `{n,}`, `{n,m}`, `@` (one or more), `<` / `>` word start / end, `( )` groups used as `\1`–`\9` in Replace with. Wildcard search always matches case.
|
|
138
|
+
- **Bounded**: special and wildcard patterns run on a linear-time matcher (no catastrophic backtracking); every search also has a time budget (`timeLimitMs`, 250 ms) and match cap (`maxMatches`, 10 000), reported as `truncated`. Raw `regex` mode uses JavaScript's `RegExp`, rejects nested repetition such as `(a+)+` and is only bounded between matches. Invalid patterns are reported as `error: { code, message }`, never thrown.
|
|
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
|
+
- 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
|
+
|
|
122
142
|
## File import and rich clipboard
|
|
123
143
|
|
|
124
144
|
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.
|
|
@@ -207,6 +227,46 @@ formatShortcut("Mod+Shift+Z", "mac"); // "⇧⌘Z" ("Ctrl+Shift+Z" on Windows /
|
|
|
207
227
|
- **Palette**: `rankCommands(query, entries, { recents })` does subsequence fuzzy ranking over label, group, keywords and id. Exact matches come first, and an empty query lists recent commands first. `createCommandPaletteController(registry)` holds the palette state that the React and Svelte `<CommandPalette>` components render.
|
|
208
228
|
- **Agents**: `createCommandToolProvider(registry)` adds `list_commands`, `run_command` and `run_commands` to a `ToolRegistry`, with complete MCP annotations. `run_commands` runs steps in order but is **not atomic**: steps that succeeded before a failure stay applied. To get all-or-nothing behaviour, register one `undoable` command that runs the steps.
|
|
209
229
|
|
|
230
|
+
## Generation models, planning and provenance
|
|
231
|
+
|
|
232
|
+
`@pixerate/editor/generation` describes generative models as data, checks a request against a model before it is sent, and records what produced each output. It reaches only the grammar and the agent tool-registry modules, so it has no third-party imports.
|
|
233
|
+
|
|
234
|
+
```ts
|
|
235
|
+
import {
|
|
236
|
+
planGenerationRequest,
|
|
237
|
+
capabilitiesToJsonSchema,
|
|
238
|
+
createGenerationToolProvider,
|
|
239
|
+
createProvenance,
|
|
240
|
+
recreateRequest,
|
|
241
|
+
estimateJobProgress,
|
|
242
|
+
expectedDurationFor,
|
|
243
|
+
type ModelCapabilities,
|
|
244
|
+
} from "@pixerate/editor/generation";
|
|
245
|
+
|
|
246
|
+
const model: ModelCapabilities = {
|
|
247
|
+
id: "img-xl",
|
|
248
|
+
media: "image",
|
|
249
|
+
prompt: { maxLength: 1000 }, // counted after templates/variables resolve (countResolved defaults to true)
|
|
250
|
+
negativePrompt: true,
|
|
251
|
+
aspectRatios: { options: ["9:16", "1:1", "16:9"], default: "1:1" },
|
|
252
|
+
quality: { options: ["draft", "standard", "high"] },
|
|
253
|
+
count: { max: 4, options: [1, 2, 4] },
|
|
254
|
+
references: { roles: { style_ref: { max: 2 }, character_ref: { max: 1 } }, maxTotal: 2 },
|
|
255
|
+
params: { guidance: { type: "number", min: 1, max: 10, step: 0.5 } },
|
|
256
|
+
timing: { baseMs: 12_000, perItemMs: 4_000 },
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
const plan = planGenerationRequest({ aspectRatio: "21:9", count: 3 }, model, { strategy: "downgrade", resolvedPrompt });
|
|
260
|
+
// plan.request: { model: "img-xl", aspectRatio: "16:9", count: 2 }
|
|
261
|
+
// plan.adjustments: [{ field: "aspectRatio", from: "21:9", to: "16:9", kind: "snapped", reason }, { field: "count", … }]
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
- **Strategies**: `nearest` (default) snaps enum values to the closest option, clamps and rounds ranges, truncates text and reference lists, and drops unsupported fields and references, reporting each change. Ties go to the higher value, or to the lower one with `downgrade`. `upgrade` also breaks ties upwards but does not drop references the model cannot use: that is an error, as is a text-only model given image references. `error` changes nothing and reports every mismatch. Missing required references, unsupported modes (`edit` / `inpaint`) and a model mismatch are always errors. Planning is pure and deterministic.
|
|
265
|
+
- **Schema and agents**: `capabilitiesToJsonSchema(model)` builds a strict request schema (enums, `minimum` / `maximum`, `maxItems`, and descriptions for per-role limits) that `validateJsonSchema` checks. `createGenerationToolProvider({ models, generate, estimate? })` adds `list_models`, `plan_generation` and `estimate_generation` (read-only) and `generate` (destructive) to a `ToolRegistry`. `generate` requires an `idempotencyKey`: a retry with the same key and arguments returns the first result (or joins it while it is still running) instead of generating again. `mergeCapabilities(remote, overlay)` keeps server capability values and takes only presentation fields (`name`, `description`, `badges`, `icon`, `group`) and overlay-only models from the overlay.
|
|
266
|
+
- **Provenance**: `createProvenance({ rawPrompt, templates, bindings, values, model, plan })` pins the exact bodies and versions of every template the prompt reached, and records the variable values, the resolved prompt with its source map, the planned settings, adjustments and references. `serializeProvenance` / `parseProvenance` are versioned. `recreateRequest(provenance)` returns the request that was sent, and `replayProvenancePrompt` re-resolves the prompt from the pinned bodies.
|
|
267
|
+
- **Lookups and progress**: `createBatchResolver({ fetch, maxBatch, delayMs })` batches and dedupes lookups; a failed batch resolves its keys as empty instead of retrying. `estimateJobProgress({ startedAt, now, expectedMs })` returns a time-based percent that stays at or below 95 until `done`, and a label such as `~2m left` or `Almost done…`. `expectedDurationFor(model, request)` uses the model's `timing`.
|
|
268
|
+
- **Forms**: `getGenerationControls(model)` and `getReferenceSlots(model, request)` describe the controls that the React and Svelte `<GenerationSettings>` render.
|
|
269
|
+
|
|
210
270
|
### Document structure and autocorrect
|
|
211
271
|
|
|
212
272
|
Opt-in preset options add Word/Pages-style document structure:
|
|
@@ -224,6 +284,76 @@ const extensions = createRichTextPreset({
|
|
|
224
284
|
|
|
225
285
|
`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.
|
|
226
286
|
|
|
287
|
+
## Image selections and masks
|
|
288
|
+
|
|
289
|
+
`@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.
|
|
290
|
+
|
|
291
|
+
```ts
|
|
292
|
+
const editor = new ImageEditorController({ image });
|
|
293
|
+
await editor.magicWand({ x: 120, y: 80 }, { tolerance: 32, contiguous: true, mode: "add" });
|
|
294
|
+
editor.selectEllipse({ x: 40, y: 40, width: 200, height: 120 }, { mode: "subtract", feather: 2 });
|
|
295
|
+
editor.modifySelection("grow", 4); // grow | shrink | feather | border | smooth
|
|
296
|
+
|
|
297
|
+
// A coarse AI segmentation (any size, white = selected), snapped to the image's edges.
|
|
298
|
+
await editor.loadSelectionFromImage(maskDataUrl);
|
|
299
|
+
await editor.refineSelection({ radius: 12, smartRadius: true, contrast: 20 });
|
|
300
|
+
|
|
301
|
+
// Use it as an adjustment layer mask; density and feather stay editable.
|
|
302
|
+
const id = editor.addAdjustmentLayer("hueSaturation");
|
|
303
|
+
editor.applySelectionAsLayerMask(id, { feather: 3 });
|
|
304
|
+
editor.setAdjustmentLayerMaskProps(id, { density: 0.6 });
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
- **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).
|
|
308
|
+
- **Modes**: `replace`, `add` (max), `subtract`, `intersect` (min), on 8-bit values; Shift / Alt map to add / subtract (`selectionModeFromModifiers`).
|
|
309
|
+
- **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.
|
|
310
|
+
- **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.
|
|
311
|
+
- **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.
|
|
312
|
+
- **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.
|
|
313
|
+
- **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.
|
|
314
|
+
- **Agent tools**: `createImageEditorToolProvider` adds `get_selection`, `select_region`, `select_by_color`, `modify_selection`, `refine_selection` and `apply_selection_as_mask`.
|
|
315
|
+
- **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.
|
|
316
|
+
|
|
317
|
+
## Spreadsheet sheets, defined names and LAMBDA
|
|
318
|
+
|
|
319
|
+
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.
|
|
320
|
+
|
|
321
|
+
```ts
|
|
322
|
+
import { SpreadsheetController } from "@pixerate/editor/spreadsheet";
|
|
323
|
+
|
|
324
|
+
const sheet = new SpreadsheetController({ document: { name: "Sales" } });
|
|
325
|
+
sheet.addSheet({ name: "Summary" }); // activates it; returns its id
|
|
326
|
+
sheet.defineName("Revenue", "=Sales!$F$2:$F$11");
|
|
327
|
+
sheet.defineName("WithTax", "=LAMBDA(x, x*1.2)");
|
|
328
|
+
sheet.setCellValue(sheet.document.rows[0].id, sheet.document.columns[0].id, "=WithTax(SUM(Revenue))");
|
|
329
|
+
sheet.renameSheet("Sales", "Sales 2026"); // formulas become ='Sales 2026'!…
|
|
330
|
+
sheet.getWorkbook(); // { sheets, activeSheetId, names } to save
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
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.
|
|
334
|
+
|
|
335
|
+
## Spreadsheet conditional formatting and data validation
|
|
336
|
+
|
|
337
|
+
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.
|
|
338
|
+
|
|
339
|
+
```ts
|
|
340
|
+
import { SpreadsheetController, createConditionalFormatRule } from "@pixerate/editor/spreadsheet";
|
|
341
|
+
|
|
342
|
+
const sheet = new SpreadsheetController({ document });
|
|
343
|
+
const all = { startRow: 1, startCol: 0, endRow: 20, endCol: 3 };
|
|
344
|
+
sheet.addConditionalFormat({ type: "expression", formula: "=$D2>100", style: { fill: "#c6efce" }, ranges: [all] });
|
|
345
|
+
sheet.addConditionalFormat(createConditionalFormatRule({ kind: "dataBar", ranges: [{ ...all, startCol: 3, endCol: 3 }] }));
|
|
346
|
+
sheet.getConditionalFormat(4, 3); // { fill, dataBar: { fraction, color, gradient }, ... } or undefined
|
|
347
|
+
|
|
348
|
+
sheet.addDataValidation({ ranges: [{ ...all, endCol: 0 }], type: "list", formula1: "North,South,East,West", errorStyle: "warning" });
|
|
349
|
+
sheet.startEditing(1, 0, "Mars");
|
|
350
|
+
const result = sheet.commitEditing(); // { status: "confirm", prompt } — the editor stays open
|
|
351
|
+
sheet.resolveValidationPrompt("cancel"); // or "accept" (warning / information) / "retry"
|
|
352
|
+
sheet.findInvalidCells(); // "circle invalid data"
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
`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.
|
|
356
|
+
|
|
227
357
|
## Other modules
|
|
228
358
|
|
|
229
359
|
Each module is also available as a subpath import:
|
|
@@ -235,12 +365,47 @@ Each module is also available as a subpath import:
|
|
|
235
365
|
| `@pixerate/editor/extensions` | Headless TipTap extensions |
|
|
236
366
|
| `@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` |
|
|
237
367
|
| `@pixerate/editor/spreadsheet` | `SpreadsheetController`, formula parser/evaluator and dependency graph, formula function registry (`registerFormulaFunction`, `listFormulaFunctions`, `searchFormulaFunctions`, `getFormulaSignature`) and autocomplete helpers (`getFormulaHints`, `applyFormulaSuggestion`) |
|
|
238
|
-
| `@pixerate/editor/image-editor` | `ImageEditorController` and Canvas 2D renderer: crop, adjustments, annotations, depth masks and AI hooks. Needs a DOM/canvas environment. |
|
|
368
|
+
| `@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). |
|
|
239
369
|
| `@pixerate/editor/canvas` | Node-graph layout (needs the optional `@dagrejs/dagre` peer), placement, trajectories, displacement, clipboard and streaming layout helpers |
|
|
240
370
|
| `@pixerate/editor/agent` | MCP tool schemas, a planner for agent-driven canvas edits, and the dynamic `ToolRegistry` with editor/spreadsheet tool providers |
|
|
241
371
|
| `@pixerate/editor/commands` | `CommandRegistry`, `ShortcutMap`, palette ranking, `createCommandToolProvider` and built-in history / arrange / spreadsheet commands (no third-party imports) |
|
|
372
|
+
| `@pixerate/editor/persistence` | Autosave and crash recovery (`createAutosaveController`, `createRecoveryPolicy`, `createRevisionTracker`), storage adapters (memory, localStorage, IndexedDB, OPFS) and document migration (`migrateDocument`, `loadSections`, `LoadReport`) (no third-party imports) |
|
|
373
|
+
| `@pixerate/editor/generation` | Model capability descriptors, `planGenerationRequest`, `capabilitiesToJsonSchema`, `createGenerationToolProvider`, provenance, `createBatchResolver` and job-progress estimates (no third-party imports) |
|
|
374
|
+
|
|
375
|
+
The root entry (`@pixerate/editor`) re-exports everything except canvas, agent, commands, persistence, generation and image editor, including TipTap. Import from the narrowest subpath you need: `grammar`, `history`, `spreadsheet`, `commands`, `persistence` and `image-editor` have no third-party imports, so a spreadsheet-only app bundles no TipTap, markdown or dagre code. The package is marked `sideEffects: false`, so unused root exports are also tree-shaken.
|
|
376
|
+
|
|
377
|
+
## Autosave, crash recovery and document migration
|
|
378
|
+
|
|
379
|
+
```ts
|
|
380
|
+
import {
|
|
381
|
+
createAutosaveController,
|
|
382
|
+
createIndexedDbAdapter,
|
|
383
|
+
createRevisionTracker,
|
|
384
|
+
formatRecoveryMessage,
|
|
385
|
+
} from "@pixerate/editor/persistence";
|
|
386
|
+
|
|
387
|
+
const revisions = createRevisionTracker({ history, dirty: bindDirtyToHistory({ history, getValue }) });
|
|
388
|
+
const autosave = createAutosaveController({
|
|
389
|
+
storage: createIndexedDbAdapter("my-app"), // or createLocalStorageAdapter / createOpfsAdapter / createMemoryStorage
|
|
390
|
+
key: "doc-42",
|
|
391
|
+
getSnapshot: () => doc,
|
|
392
|
+
revision: revisions.revision,
|
|
393
|
+
savedRevision: revisions.savedRevision,
|
|
394
|
+
name: "Quarterly plan",
|
|
395
|
+
});
|
|
396
|
+
revisions.subscribe(() => autosave.notify());
|
|
397
|
+
|
|
398
|
+
const recovered = await autosave.checkRecovery(); // { snapshot, meta: { time, name, revision } } | null
|
|
399
|
+
if (recovered && confirm(formatRecoveryMessage(recovered.meta))) load(recovered.snapshot);
|
|
400
|
+
else if (recovered) await autosave.discardRecovery();
|
|
401
|
+
autosave.ready(); // hydration gate: nothing is written before this
|
|
402
|
+
addEventListener("pagehide", () => autosave.flushNow());
|
|
403
|
+
// after an explicit save: autosave.markClean() retires the snapshot, but only one this session wrote
|
|
404
|
+
```
|
|
405
|
+
|
|
406
|
+
Snapshots are written `debounceMs` after the last edit and at least every `interval` while edits keep coming; failed writes retry with exponential backoff (`retryBaseMs` doubling to `retryMaxMs`). `createRecoveryPolicy` is the pure, clock-injected state machine behind it.
|
|
242
407
|
|
|
243
|
-
|
|
408
|
+
`migrateDocument(json, { currentVersion, steps: { 1: (doc) => … } })` upgrades stored JSON one version at a time and returns a `LoadReport`: `migratedFrom`, `readOnly` (a newer version, or a failed step: show it but never save over it) and `unknownKeys`. `loadSections(doc, { key: { parse, defaults } })` falls back to defaults one section at a time and keeps unknown keys; `loadDocument` does both. `loadImageEditorDocument(json)` (`@pixerate/editor/image-editor`) applies them to `SerializedImageEditorState`.
|
|
244
409
|
|
|
245
410
|
## History gestures, marks and dirty tracking
|
|
246
411
|
|
package/THIRD_PARTY_NOTICES.md
CHANGED
|
@@ -8,12 +8,29 @@ All sources below are from the ArtCraft "craft" apps, each licensed **MIT OR Apa
|
|
|
8
8
|
© 2026 ArtCraft Team and the respective app's contributors. They are used here under the MIT licence
|
|
9
9
|
reproduced at the end of this file. Paths are relative to each source repository.
|
|
10
10
|
|
|
11
|
+
## craft-libs
|
|
12
|
+
|
|
13
|
+
© 2026 ArtCraft Team and the craft-libs contributors.
|
|
14
|
+
|
|
15
|
+
- `crates/settings/src/migrate.rs` (`upgrade`: step-wise migration to a target version, missing version taken as current, newer version left alone) → `migrateDocument` in `packages/core/src/persistence/migrate.ts`
|
|
16
|
+
- `crates/settings/src/document.rs` (`deserialize_sections`: per-section fallback to defaults) → `loadSections` in `packages/core/src/persistence/migrate.ts`
|
|
17
|
+
- `crates/settings/src/error.rs` (`LoadReport`, `has_problems`, `SectionError`, read-only reasons) → `packages/core/src/persistence/migrate.ts`
|
|
18
|
+
- `crates/settings/src/store.rs` (debounced save, `retry_delay` exponential backoff with cap) → `packages/core/src/persistence/autosave.ts`, `exponentialBackoff` in `packages/core/src/persistence/recoveryPolicy.ts`
|
|
19
|
+
- Test cases from `crates/settings/tests/migrate.rs`, `tests/recovery.rs` and `tests/store.rs`, re-expressed → `packages/core/tests/persistence-migrate.test.ts`, `packages/core/tests/persistence-policy.test.ts`
|
|
20
|
+
|
|
11
21
|
## figcraft
|
|
12
22
|
|
|
13
23
|
© 2026 ArtCraft Team and the FigCraft contributors.
|
|
14
24
|
|
|
15
25
|
- `crates/engine/src/distance.rs` (`between`, `inside`, `to_guide`, `label`) → `packages/core/src/canvas/measure.ts`
|
|
16
26
|
- `crates/engine/src/measure_tests.rs` (test cases re-expressed) → `packages/core/tests/canvas-measure.test.ts`
|
|
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
|
+
- `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`
|
|
17
34
|
|
|
18
35
|
## filmcraft
|
|
19
36
|
|
|
@@ -24,6 +41,9 @@ reproduced at the end of this file. Paths are relative to each source repository
|
|
|
24
41
|
- `crates/engine/src/lib.rs` (`Session::execute`, `enabled_for`: re-checking enablement against targets named in params, catching command failures) → `packages/core/src/commands/registry.ts`
|
|
25
42
|
- `crates/engine/src/shortcuts.rs` and `crates/engine/src/shortcuts_tests.rs` (platform-neutral chords, `Mod` resolution per platform, scoped bindings, conflict detection, per-platform conflict test) → `packages/core/src/commands/chords.ts`, `packages/core/src/commands/shortcuts.ts`, `packages/core/tests/commands.test.ts`
|
|
26
43
|
- `crates/automation/src/server.rs` (`command_list` / `command_run` / `command_batch` tools, complete MCP annotations, unknown-argument errors naming the accepted keys) → `packages/core/src/commands/agentTools.ts`, `packages/core/src/commands/registry.ts`
|
|
44
|
+
- `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`
|
|
45
|
+
- `apps/filmcraft-web/src/recovery.rs` (snapshot-then-dirty-meta writes, recovery check of a dirty meta) → `packages/core/src/persistence/autosave.ts`
|
|
46
|
+
- `apps/filmcraft-web/tests/recovery_policy.rs` (test cases re-expressed) → `packages/core/tests/persistence-policy.test.ts`
|
|
27
47
|
|
|
28
48
|
## gridcraft
|
|
29
49
|
|
|
@@ -33,6 +53,19 @@ reproduced at the end of this file. Paths are relative to each source repository
|
|
|
33
53
|
- `crates/core/src/date.rs` (1900 date-system serials, days in month, civil-day arithmetic) → `packages/core/src/spreadsheet/formula/parse-input.ts`
|
|
34
54
|
- `crates/core/src/value.rs` (Excel comparison, 15-digit number comparison, sort order, number-to-text coercion) → `packages/core/src/spreadsheet/formula/values.ts`
|
|
35
55
|
- Test cases from the files above, re-expressed → `packages/core/tests/spreadsheet-typed-input.test.ts`
|
|
56
|
+
- `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/`
|
|
57
|
+
- `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`
|
|
58
|
+
- `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`
|
|
59
|
+
- `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`
|
|
60
|
+
- `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`
|
|
61
|
+
- `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`
|
|
62
|
+
- `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/`
|
|
63
|
+
- `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`
|
|
64
|
+
- 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`
|
|
65
|
+
**Apache POI.** The rows of `packages/core/tests/fixtures/poi-number-format-approx.ts` (format, value and Excel's
|
|
66
|
+
`TEXT()` result) come from Apache POI's `test-data/spreadsheet/NumberFormatApproxTests.xlsx`, as collected in
|
|
67
|
+
GridCraft. Apache POI is © The Apache Software Foundation, licensed under the Apache License, Version 2.0
|
|
68
|
+
(https://www.apache.org/licenses/LICENSE-2.0).
|
|
36
69
|
|
|
37
70
|
## lightcraft
|
|
38
71
|
|
|
@@ -43,6 +76,17 @@ reproduced at the end of this file. Paths are relative to each source repository
|
|
|
43
76
|
- `crates/pipeline/src/tone.rs` (`ToneMap::display` S-curve anchored at 0, grey and 1) → `packages/core/src/image-editor/color.ts`
|
|
44
77
|
- `crates/pipeline/src/local.rs` (relative white balance with neutral-luminance normalisation) → `packages/core/src/image-editor/color.ts`
|
|
45
78
|
- `crates/develop/src/settings.rs` and `docs/process-versions.md` (process version semantics) → `packages/core/src/image-editor/color.ts`
|
|
79
|
+
- `crates/develop/src/controls.rs` (`ControlSpec` table: id, label, section, range, default, step, decimals, track style; clamping) → `packages/core/src/image-editor/controls.ts`
|
|
80
|
+
|
|
81
|
+
## pdfcraft
|
|
82
|
+
|
|
83
|
+
© 2026 ArtCraft Team and the PDFCraft contributors.
|
|
84
|
+
|
|
85
|
+
- `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`
|
|
86
|
+
- 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`
|
|
87
|
+
- `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`
|
|
88
|
+
- `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`
|
|
89
|
+
- 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`
|
|
46
90
|
|
|
47
91
|
## photocraft
|
|
48
92
|
|
|
@@ -50,6 +94,17 @@ reproduced at the end of this file. Paths are relative to each source repository
|
|
|
50
94
|
|
|
51
95
|
- `crates/ops/src/lib.rs` (`History { max_states, max_bytes }`, `trim`, `unique_bytes`) → `packages/core/src/history/HistoryManager.ts`
|
|
52
96
|
- `crates/ui-egui/src/palette.rs` (`fuzzy_score`, `exact_bonus`, `push_recent`) → `packages/core/src/commands/palette.ts`
|
|
97
|
+
- `crates/doc/src/adjust.rs` (`Adjustment` model, `HueRange::neutral` / `weight`, `LevelsChannel` defaults) → `packages/core/src/image-editor/types.ts`, `packages/core/src/image-editor/adjustment-layers.ts`
|
|
98
|
+
- `crates/compose/src/adjust.rs` (`curve_lut`, `levels_q`, `tone_luts_q`, `hue_saturation`, `range_lightness`, `hue_range_tables`, `rgb_to_hsl` / `hsl_to_rgb`, `vibrance_px` / `vibrance_lin`, `color_balance_channel`, `black_white_gray`, `photo_filter_matrix`, `posterize`, Threshold) → `packages/core/src/image-editor/adjustment-layers.ts`
|
|
99
|
+
- `crates/color/src/blend.rs` (separable and non-separable blend modes, hardened `clip_color`, `set_lum`, `composite`) → `packages/core/src/image-editor/blend.ts`
|
|
100
|
+
- `crates/color/src/convert.rs` (`D50`, `SRGB_TO_XYZ_D50`, `XYZ_D50_TO_SRGB`) → `packages/core/src/image-editor/adjustment-layers.ts`
|
|
101
|
+
- `crates/algo/src/histogram.rs` (bounded RGB histogram, transparent exclusion, exact-endpoint clipping) → `packages/core/src/image-editor/histogram.ts`
|
|
102
|
+
- 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`
|
|
103
|
+
- `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`
|
|
104
|
+
- `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`
|
|
105
|
+
- `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`
|
|
106
|
+
- `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`
|
|
107
|
+
- 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`
|
|
53
108
|
|
|
54
109
|
## soundcraft
|
|
55
110
|
|
|
@@ -67,8 +122,33 @@ reproduced at the end of this file. Paths are relative to each source repository
|
|
|
67
122
|
- `crates/pathops/src/fit.rs` (`fit_cubics`, `fit_single`, `fit_single_from`, `generate`, `newton`, `chord_params`, `is_straight`) → `packages/core/src/canvas/curveFit.ts`
|
|
68
123
|
- `crates/pathops/src/edit.rs` (`simplify_with` corner splitting) → `packages/core/src/canvas/curveFit.ts`
|
|
69
124
|
- 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`
|
|
125
|
+
- `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`
|
|
126
|
+
- `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`
|
|
127
|
+
- `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`
|
|
128
|
+
- `crates/pathops/src/boolean.rs` (`try_merge` refit of tangent-continuous runs) → `mergeSmoothRuns` in `packages/core/src/canvas/pathops/stroke.ts`
|
|
129
|
+
- 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`
|
|
130
|
+
---
|
|
131
|
+
|
|
132
|
+
## wordcraft
|
|
133
|
+
|
|
134
|
+
© 2026 ArtCraft Team and the WordCraft contributors.
|
|
135
|
+
|
|
136
|
+
- `crates/engine/src/cmd/mailings.rs` (`parse_csv` delimiter auto-detection and blank-row skipping, `table_from_rows`, `header_split` padding / truncation and record caps, `address_map` field aliases) → `packages/core/src/grammar/data-source.ts`
|
|
137
|
+
- `crates/formats/src/sheet.rs` (`field_names`: blank headers as `Column N`, repeated headers numbered) → `packages/core/src/grammar/data-source.ts`
|
|
138
|
+
- `crates/engine/src/cmd/mailings.rs` (`condition` numeric-or-text comparison with `= <> < > <= >=`, `finish` one copy per record with SKIPIF and a merge cap) → `packages/core/src/grammar/variables.ts`, `packages/core/src/grammar/merge.ts`
|
|
139
|
+
- Test cases from `crates/engine/src/cmd/mailings.rs` (`csv_parsing`, `merge_end_to_end`, `recipients_from_fields_and_array_rows`, `skipif_drops_matching_records`, `if_rule_previews_its_result`), re-expressed → `packages/core/tests/mail-merge.test.ts`
|
|
140
|
+
- `crates/engine/src/cmd/tools.rs` (`compare`: block-level diff of case-folded / white-space-collapsed paragraph keys, pairing the i-th deleted with the i-th inserted paragraph for word or character diffs, whole insertions/deletions otherwise, `MAX_CHAR_DIFF` fallback, options not honoured; `tokens`, `word_diff`) → `packages/core/src/compare/documents.ts`
|
|
141
|
+
- `crates/engine/src/cmd/fmt_revisions.rs` (`compare_formatting`: equal text compared for formatting changes) → `packages/core/src/compare/documents.ts`
|
|
142
|
+
- Test cases from `crates/engine/src/cmd/tools.rs` (`compare_marks_changes`, `compare_options_ignore_case_and_white_space`), re-expressed → `packages/core/tests/compare-documents.test.ts`
|
|
70
143
|
|
|
71
144
|
---
|
|
145
|
+
- `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`
|
|
146
|
+
- `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`
|
|
147
|
+
- `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`
|
|
148
|
+
- `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`
|
|
149
|
+
- 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`
|
|
150
|
+
- `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`
|
|
151
|
+
- 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`
|
|
72
152
|
|
|
73
153
|
## MIT License
|
|
74
154
|
|
package/dist/agent/index.cjs
CHANGED
|
@@ -5,12 +5,14 @@ var _chunkCPOKFNBGcjs = require('../chunk-CPOKFNBG.cjs');
|
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
|
|
8
|
-
var
|
|
8
|
+
var _chunk7WISYZFOcjs = require('../chunk-7WISYZFO.cjs');
|
|
9
|
+
require('../chunk-BVP3TSLH.cjs');
|
|
10
|
+
require('../chunk-X4N4WUKB.cjs');
|
|
9
11
|
|
|
10
12
|
|
|
11
13
|
|
|
12
14
|
|
|
13
|
-
var
|
|
15
|
+
var _chunkC7P22VIPcjs = require('../chunk-C7P22VIP.cjs');
|
|
14
16
|
|
|
15
17
|
|
|
16
18
|
|
|
@@ -700,7 +702,7 @@ function createToolRegistry(options) {
|
|
|
700
702
|
var toEditor = (target) => "commands" in target ? target : target.editor;
|
|
701
703
|
function replaceWithText(editor, from, to, text) {
|
|
702
704
|
const { state, view } = editor;
|
|
703
|
-
const tr = /[\r\n]/.test(text) ? state.tr.replace(from, to,
|
|
705
|
+
const tr = /[\r\n]/.test(text) ? state.tr.replace(from, to, _chunk7WISYZFOcjs.plainTextToSlice.call(void 0, text, state.schema)) : text ? state.tr.insertText(text, from, to) : state.tr.delete(from, to);
|
|
704
706
|
view.dispatch(tr.scrollIntoView());
|
|
705
707
|
}
|
|
706
708
|
function selectionInfo(editor) {
|
|
@@ -733,7 +735,7 @@ function createRichTextToolProvider(target, options) {
|
|
|
733
735
|
},
|
|
734
736
|
handler: ({ format = "text" }) => {
|
|
735
737
|
const ed = editor();
|
|
736
|
-
const content = format === "html" ? ed.getHTML() : format === "markdown" ?
|
|
738
|
+
const content = format === "html" ? ed.getHTML() : format === "markdown" ? _chunk7WISYZFOcjs.getEditorMarkdown.call(void 0, ed) : _chunk7WISYZFOcjs.getEditorText.call(void 0, ed);
|
|
737
739
|
return { format, content, selection: selectionInfo(ed), size: ed.state.doc.content.size };
|
|
738
740
|
}
|
|
739
741
|
};
|
|
@@ -815,7 +817,7 @@ var CELL_REF_REGEX = /^\$?([A-Za-z]{1,3})\$?(\d{1,7})$/;
|
|
|
815
817
|
function parseCell(ref) {
|
|
816
818
|
const match = CELL_REF_REGEX.exec(ref.trim());
|
|
817
819
|
if (!match) throw new Error(`Invalid cell reference "${ref}". Use A1 notation, e.g. "B3".`);
|
|
818
|
-
return { col:
|
|
820
|
+
return { col: _chunkC7P22VIPcjs.colNameToIndex.call(void 0, match[1].toUpperCase()), row: parseInt(match[2], 10) - 1 };
|
|
819
821
|
}
|
|
820
822
|
function createSpreadsheetToolProvider(controller, options) {
|
|
821
823
|
const lookup = (row, col) => {
|
|
@@ -863,14 +865,14 @@ function createSpreadsheetToolProvider(controller, options) {
|
|
|
863
865
|
const cell = controller.getCell(found.rowDef.id, found.colDef.id);
|
|
864
866
|
valueRow.push(_nullishCoalesce(cell.error, () => ( cell.value)));
|
|
865
867
|
rawRow.push(cell.raw);
|
|
866
|
-
const error = _nullishCoalesce(cell.error, () => ( (
|
|
867
|
-
if (error) errors.push({ cell: `${
|
|
868
|
+
const error = _nullishCoalesce(cell.error, () => ( (_chunkC7P22VIPcjs.isFormulaError.call(void 0, cell.value) ? cell.value : void 0)));
|
|
869
|
+
if (error) errors.push({ cell: `${_chunkC7P22VIPcjs.indexToColName.call(void 0, c)}${r + 1}`, error });
|
|
868
870
|
}
|
|
869
871
|
values.push(valueRow);
|
|
870
872
|
raw.push(rawRow);
|
|
871
873
|
}
|
|
872
874
|
return {
|
|
873
|
-
range: values.length && values[0].length ? `${
|
|
875
|
+
range: values.length && values[0].length ? `${_chunkC7P22VIPcjs.indexToColName.call(void 0, c0)}${r0 + 1}:${_chunkC7P22VIPcjs.indexToColName.call(void 0, c1)}${r1 + 1}` : null,
|
|
874
876
|
values,
|
|
875
877
|
raw,
|
|
876
878
|
errors,
|