@pixerate/editor 0.154.0 → 0.156.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/dist/animation-editor/index.cjs +60 -61
- package/dist/animation-editor/index.js +5 -6
- package/dist/{brushstrokeRender-YAZVQUXY.cjs → brushstrokeRender-5WANQIG4.cjs} +10 -10
- package/dist/{brushstrokeRender-EUDQTDYS.js → brushstrokeRender-Q4LI22QV.js} +3 -3
- package/dist/canvas/index.cjs +23 -24
- package/dist/canvas/index.js +3 -4
- package/dist/chart/index.cjs +2 -3
- package/dist/chart/index.js +1 -2
- package/dist/{chunk-AGZ47XY2.js → chunk-2N2QPO3H.js} +6 -2
- package/dist/{chunk-NMXPOKF3.js → chunk-45XXVHVG.js} +1 -1
- package/dist/{chunk-FLOGD3QB.js → chunk-6UPGM3JB.js} +2 -2
- package/dist/{chunk-6FUAHCVI.cjs → chunk-7NW46N7I.cjs} +16 -16
- package/dist/{chunk-ORDXMBD4.js → chunk-A5UCR2TO.js} +10 -3
- package/dist/{chunk-PI45JMP7.cjs → chunk-CD7PLU77.cjs} +81 -81
- package/dist/{chunk-LRRCMRSL.cjs → chunk-EBDCRTZI.cjs} +20 -16
- package/dist/{chunk-OLCYQLFE.js → chunk-EL5PAHNP.js} +1 -1
- package/dist/{chunk-PUC43JPT.cjs → chunk-EYVVIZJ3.cjs} +1 -1
- package/dist/{chunk-BXMIBMVG.cjs → chunk-JGKCC5OF.cjs} +65 -58
- package/dist/chunk-M4MIZPQI.cjs +393 -0
- package/dist/{chunk-LIXLD433.cjs → chunk-MHEXLG3M.cjs} +35 -35
- package/dist/chunk-NHLYK3AX.js +393 -0
- package/dist/{chunk-RHXW4TA5.cjs → chunk-QMI5HZWA.cjs} +7 -7
- package/dist/{chunk-XFDQEAOS.cjs → chunk-RQCMSEM5.cjs} +2 -2
- package/dist/{chunk-BCY22GBH.js → chunk-SDHZG5TF.js} +1 -1
- package/dist/{chunk-HVX35M2I.cjs → chunk-WI7QJA7A.cjs} +2 -2
- package/dist/{chunk-UILRJQFD.js → chunk-WNAAWLZ5.js} +1 -1
- package/dist/{chunk-4J4MC3F5.js → chunk-YXOW456B.js} +1 -1
- package/dist/{chunk-Y64YPIFT.js → chunk-ZQWLWDZI.js} +1 -1
- package/dist/diagram/index.cjs +2 -3
- package/dist/diagram/index.js +1 -2
- package/dist/image-editor/controller.d.cts +8 -0
- package/dist/image-editor/controller.d.ts +8 -0
- package/dist/image-editor/editorDocument.d.cts +16 -0
- package/dist/image-editor/editorDocument.d.ts +16 -0
- package/dist/image-editor/index.cjs +787 -737
- package/dist/image-editor/index.d.cts +1 -0
- package/dist/image-editor/index.d.ts +1 -0
- package/dist/image-editor/index.js +56 -6
- package/dist/index.cjs +4 -5
- package/dist/index.js +2 -3
- package/dist/paint/grain.d.cts +7 -0
- package/dist/paint/grain.d.ts +7 -0
- package/dist/paint/index.cjs +3 -3
- package/dist/paint/index.js +2 -2
- package/dist/paint-editor/controller.d.cts +38 -0
- package/dist/paint-editor/controller.d.ts +38 -0
- package/dist/paint-editor/document.d.cts +8 -0
- package/dist/paint-editor/document.d.ts +8 -0
- package/dist/paint-editor/editorDocument.d.cts +17 -0
- package/dist/paint-editor/editorDocument.d.ts +17 -0
- package/dist/paint-editor/index.cjs +476 -273
- package/dist/paint-editor/index.d.cts +1 -0
- package/dist/paint-editor/index.d.ts +1 -0
- package/dist/paint-editor/index.js +216 -13
- package/dist/paint-editor/paper.d.cts +8 -0
- package/dist/paint-editor/paper.d.ts +8 -0
- package/dist/paint-editor/stencils.d.cts +2 -0
- package/dist/paint-editor/stencils.d.ts +2 -0
- package/dist/paint-editor/wet.d.cts +1 -0
- package/dist/paint-editor/wet.d.ts +1 -0
- package/dist/parameters/index.cjs +2 -3
- package/dist/parameters/index.js +1 -2
- package/dist/persistence/assets.d.cts +51 -0
- package/dist/persistence/assets.d.ts +51 -0
- package/dist/persistence/editorDocument.d.cts +12 -0
- package/dist/persistence/editorDocument.d.ts +12 -0
- package/dist/persistence/index.cjs +32 -8
- package/dist/persistence/index.d.cts +1 -0
- package/dist/persistence/index.d.ts +1 -0
- package/dist/persistence/index.js +34 -10
- package/dist/spreadsheet/index.cjs +3 -4
- package/dist/spreadsheet/index.js +2 -3
- package/dist/tokens/index.cjs +3 -4
- package/dist/tokens/index.js +2 -3
- package/dist/vector/index.cjs +952 -953
- package/dist/vector/index.js +5 -6
- package/dist/video-editor/index.cjs +2 -3
- package/dist/video-editor/index.js +1 -2
- package/package.json +1 -1
- package/skills/pixerate-image-editor/SKILL.md +16 -24
- package/skills/pixerate-image-editor/references/smart-objects.md +28 -0
- package/skills/pixerate-paint-editor/SKILL.md +26 -6
- package/dist/chunk-5H2C3QSF.cjs +0 -134
- package/dist/chunk-GNV54DLO.js +0 -134
- package/dist/chunk-L5OWG2NA.cjs +0 -135
- package/dist/chunk-WHKNYFM2.js +0 -135
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
resolveExternalAssets,
|
|
8
8
|
serializeLayers,
|
|
9
9
|
serializeLayersExternal
|
|
10
|
-
} from "../chunk-
|
|
10
|
+
} from "../chunk-ZQWLWDZI.js";
|
|
11
11
|
import "../chunk-3CI7FRMK.js";
|
|
12
12
|
import {
|
|
13
13
|
EffectRendererPool
|
|
@@ -54,7 +54,7 @@ import {
|
|
|
54
54
|
realPresenceClock,
|
|
55
55
|
tweenPresenceCursor
|
|
56
56
|
} from "../chunk-O2ANJFUF.js";
|
|
57
|
-
import "../chunk-
|
|
57
|
+
import "../chunk-6UPGM3JB.js";
|
|
58
58
|
import {
|
|
59
59
|
BRUSH_ID,
|
|
60
60
|
BRUSH_ROTATIONS,
|
|
@@ -119,7 +119,7 @@ import {
|
|
|
119
119
|
tiltToOrientation,
|
|
120
120
|
toStrokePoint,
|
|
121
121
|
usesPaintExchange
|
|
122
|
-
} from "../chunk-
|
|
122
|
+
} from "../chunk-A5UCR2TO.js";
|
|
123
123
|
import "../chunk-M6DNZNVY.js";
|
|
124
124
|
import "../chunk-WEIK3UYB.js";
|
|
125
125
|
import "../chunk-PWBZH3FK.js";
|
|
@@ -189,7 +189,7 @@ import {
|
|
|
189
189
|
seedFromString,
|
|
190
190
|
solidPaint,
|
|
191
191
|
updatePaintGradientStop
|
|
192
|
-
} from "../chunk-
|
|
192
|
+
} from "../chunk-2N2QPO3H.js";
|
|
193
193
|
import "../chunk-ODBT55Q7.js";
|
|
194
194
|
import {
|
|
195
195
|
BLEND_MODES,
|
|
@@ -224,6 +224,11 @@ import "../chunk-QGHEDC7B.js";
|
|
|
224
224
|
import "../chunk-SIFAIGB3.js";
|
|
225
225
|
import "../chunk-CIQIM2IP.js";
|
|
226
226
|
import "../chunk-VDGBHYC7.js";
|
|
227
|
+
import {
|
|
228
|
+
createEditorAssetRegistry,
|
|
229
|
+
defineEditorDocument
|
|
230
|
+
} from "../chunk-NHLYK3AX.js";
|
|
231
|
+
import "../chunk-HF5QZS7Z.js";
|
|
227
232
|
import "../chunk-KY3FYKP3.js";
|
|
228
233
|
import "../chunk-2WPNAGHI.js";
|
|
229
234
|
import {
|
|
@@ -1352,6 +1357,17 @@ function stencilMask(stencils, width, height) {
|
|
|
1352
1357
|
}
|
|
1353
1358
|
return { width, height, data };
|
|
1354
1359
|
}
|
|
1360
|
+
function translateStencil(s, dx, dy) {
|
|
1361
|
+
const p = (q) => q ? { x: q.x + dx, y: q.y + dy } : void 0;
|
|
1362
|
+
return {
|
|
1363
|
+
...s,
|
|
1364
|
+
...s.from ? { from: p(s.from) } : {},
|
|
1365
|
+
...s.to ? { to: p(s.to) } : {},
|
|
1366
|
+
...s.center ? { center: p(s.center) } : {},
|
|
1367
|
+
...s.rect ? { rect: { ...s.rect, x: s.rect.x + dx, y: s.rect.y + dy } } : {},
|
|
1368
|
+
...s.points ? { points: s.points.map((q) => p(q)) } : {}
|
|
1369
|
+
};
|
|
1370
|
+
}
|
|
1355
1371
|
function scaleStencil(s, k) {
|
|
1356
1372
|
const p = (q) => q ? { x: q.x * k, y: q.y * k } : void 0;
|
|
1357
1373
|
return {
|
|
@@ -1502,7 +1518,7 @@ var TRACING_NODE_ID = "__tracing";
|
|
|
1502
1518
|
var PAPER_EFFECT_NODE_ID = "__paper_effect";
|
|
1503
1519
|
function paperKey(p, light) {
|
|
1504
1520
|
const metal = p.metallic ?? 0;
|
|
1505
|
-
return JSON.stringify([p.pattern, p.seed, p.color, p.scale, p.tiling, p.shading, p.dry, metal, metal > 0 && light ? [light.azimuth, light.elevation] : null]);
|
|
1521
|
+
return JSON.stringify([p.pattern, p.seed, p.color, p.scale, p.tiling, p.shading, p.dry, p.offset ?? null, metal, metal > 0 && light ? [light.azimuth, light.elevation] : null]);
|
|
1506
1522
|
}
|
|
1507
1523
|
var paperNodes = /* @__PURE__ */ new WeakMap();
|
|
1508
1524
|
function paperNode(paper, light) {
|
|
@@ -1517,7 +1533,7 @@ function paperNode(paper, light) {
|
|
|
1517
1533
|
var grainCache = /* @__PURE__ */ new Map();
|
|
1518
1534
|
var tileCache = /* @__PURE__ */ new Map();
|
|
1519
1535
|
function paperGrain(paper) {
|
|
1520
|
-
const key = JSON.stringify([paper.pattern, paper.seed, paper.scale, paper.tiling, paper.dry]);
|
|
1536
|
+
const key = JSON.stringify([paper.pattern, paper.seed, paper.scale, paper.tiling, paper.dry, paper.offset ?? null]);
|
|
1521
1537
|
let g = grainCache.get(key);
|
|
1522
1538
|
if (!g) {
|
|
1523
1539
|
g = new PaperGrain(paper, "dry");
|
|
@@ -1607,7 +1623,7 @@ function downsampleRgba(data, width, height, tw, th) {
|
|
|
1607
1623
|
// src/paint-editor/serialize.ts
|
|
1608
1624
|
var PAINT_FORMAT = "pixerate-paint";
|
|
1609
1625
|
var PAINT_SCHEMA_VERSION = 1;
|
|
1610
|
-
var KNOWN = /* @__PURE__ */ new Set(["format", "version", "width", "height", "dpi", "paper", "layers", "assets", "activeLayerId", "strokes", "guides", "swatches", "mixingPad", "wet", "wetEvents", "paintEvents"]);
|
|
1626
|
+
var KNOWN = /* @__PURE__ */ new Set(["format", "version", "width", "height", "dpi", "paper", "layers", "assets", "activeLayerId", "strokes", "guides", "swatches", "mixingPad", "wet", "wetEvents", "paintEvents", "origin"]);
|
|
1611
1627
|
function documentExtensions(raw) {
|
|
1612
1628
|
const out = {};
|
|
1613
1629
|
for (const [k, v] of Object.entries(raw)) if (!KNOWN.has(k)) out[k] = v;
|
|
@@ -1631,6 +1647,7 @@ function header(state, options) {
|
|
|
1631
1647
|
if (options.wet !== void 0) out.wet = JSON.parse(JSON.stringify(options.wet));
|
|
1632
1648
|
if (state.wetEvents?.length) out.wetEvents = JSON.parse(JSON.stringify(state.wetEvents));
|
|
1633
1649
|
if (state.paintEvents?.length) out.paintEvents = JSON.parse(JSON.stringify(state.paintEvents));
|
|
1650
|
+
if (state.origin && (state.origin.x || state.origin.y)) out.origin = { ...state.origin };
|
|
1634
1651
|
return out;
|
|
1635
1652
|
}
|
|
1636
1653
|
function serializePaintDocument(state, options = {}) {
|
|
@@ -1683,6 +1700,8 @@ function deserializePaintDocument(raw) {
|
|
|
1683
1700
|
if (wetEvents.length) state.wetEvents = wetEvents;
|
|
1684
1701
|
const paintEvents = Array.isArray(r.paintEvents) ? r.paintEvents.filter((e) => e && typeof e === "object" && Number.isInteger(e.at) && typeof e.layerId === "string" && ["fill", "clear", "flatten", "stickers"].includes(e.kind)) : [];
|
|
1685
1702
|
if (paintEvents.length) state.paintEvents = paintEvents;
|
|
1703
|
+
const o = r.origin;
|
|
1704
|
+
if (o && typeof o === "object" && Number.isFinite(o.x) && Number.isFinite(o.y) && (o.x || o.y)) state.origin = { x: Math.round(o.x), y: Math.round(o.y) };
|
|
1686
1705
|
return { state, extensions: documentExtensions(r), guides: r.guides, swatches: r.swatches, mixingPad: r.mixingPad, wet: r.wet, problems };
|
|
1687
1706
|
}
|
|
1688
1707
|
async function deserializePaintDocumentAsync(raw, store) {
|
|
@@ -2133,7 +2152,10 @@ var PaintWetLayers = class {
|
|
|
2133
2152
|
/** The world (made on first use, at the canvas size). */
|
|
2134
2153
|
ensureWorld() {
|
|
2135
2154
|
const { width, height } = this.host.canvas();
|
|
2136
|
-
if (this.world && (this.world.width !== width || this.world.height !== height))
|
|
2155
|
+
if (this.world && (this.world.width !== width || this.world.height !== height)) {
|
|
2156
|
+
if (this.world.laneIds().length) throw new Error("The wet world is the wrong size; dry the paint before resizing the canvas.");
|
|
2157
|
+
this.dropWorld();
|
|
2158
|
+
}
|
|
2137
2159
|
if (!this.world) {
|
|
2138
2160
|
this.world = new WetWorld(width, height, wetPaperInput(this.host.paper()), { kernel: this.options.kernel, threads: this.options.threads });
|
|
2139
2161
|
this.world.setTilt(this.tilt);
|
|
@@ -2396,9 +2418,16 @@ var PaintWetLayers = class {
|
|
|
2396
2418
|
if (this.world) for (const id of this.world.laneIds()) this.world.removeLane(id);
|
|
2397
2419
|
return;
|
|
2398
2420
|
}
|
|
2421
|
+
const { width, height } = this.host.canvas();
|
|
2422
|
+
if (this.world && (this.world.width !== width || this.world.height !== height)) this.dropWorld();
|
|
2399
2423
|
this.ensureWorld().restore(snap);
|
|
2400
2424
|
this.syncDisplays();
|
|
2401
2425
|
}
|
|
2426
|
+
dropWorld() {
|
|
2427
|
+
this.world?.destroy();
|
|
2428
|
+
this.world = null;
|
|
2429
|
+
this.driver = null;
|
|
2430
|
+
}
|
|
2402
2431
|
/** Forgets everything wet (a new canvas, a resize, opening a document). */
|
|
2403
2432
|
reset() {
|
|
2404
2433
|
this.world?.destroy();
|
|
@@ -3481,6 +3510,20 @@ var PaintEditorController = class _PaintEditorController {
|
|
|
3481
3510
|
return d;
|
|
3482
3511
|
}
|
|
3483
3512
|
// --- History -------------------------------------------------------------------
|
|
3513
|
+
docRevision = 0;
|
|
3514
|
+
/**
|
|
3515
|
+
* Bumped on every change to the painting (a committed or amended step, undo, redo, opening a
|
|
3516
|
+
* document), not on wet-paint frames or view changes. `createPaintEditorDocument` uses it.
|
|
3517
|
+
*/
|
|
3518
|
+
get documentRevision() {
|
|
3519
|
+
return this.docRevision;
|
|
3520
|
+
}
|
|
3521
|
+
/** Why the document cannot be replaced or stepped through right now, or `null`. */
|
|
3522
|
+
busyReason() {
|
|
3523
|
+
if (this.session) return "A stroke is in progress; end or cancel it first.";
|
|
3524
|
+
if (this.wet.busy) return "Wet paint is mid-step; wait a moment (wetIdle()) first.";
|
|
3525
|
+
return null;
|
|
3526
|
+
}
|
|
3484
3527
|
requireIdle(what) {
|
|
3485
3528
|
if (this.session) throw new PaintEditorError(`A stroke is in progress; end or cancel it before ${what}.`);
|
|
3486
3529
|
}
|
|
@@ -3494,6 +3537,7 @@ var PaintEditorController = class _PaintEditorController {
|
|
|
3494
3537
|
if (this.undoStack.length > this.historyLimit) this.undoStack.shift();
|
|
3495
3538
|
this.redoStack = [];
|
|
3496
3539
|
this.doc = next;
|
|
3540
|
+
this.docRevision++;
|
|
3497
3541
|
this.trimWetHistory();
|
|
3498
3542
|
this.emit();
|
|
3499
3543
|
}
|
|
@@ -3510,6 +3554,7 @@ var PaintEditorController = class _PaintEditorController {
|
|
|
3510
3554
|
last.after = next;
|
|
3511
3555
|
this.redoStack = [];
|
|
3512
3556
|
this.doc = next;
|
|
3557
|
+
this.docRevision++;
|
|
3513
3558
|
this.emit();
|
|
3514
3559
|
}
|
|
3515
3560
|
/** Keeps the wet snapshots in history within the wet undo memory, dropping the oldest. */
|
|
@@ -3572,6 +3617,8 @@ var PaintEditorController = class _PaintEditorController {
|
|
|
3572
3617
|
this.redoStack.push(e);
|
|
3573
3618
|
const paper = this.doc.paper;
|
|
3574
3619
|
this.doc = e.before;
|
|
3620
|
+
this.docRevision++;
|
|
3621
|
+
if (e.guides) this.restoreGuides(e.guides.before);
|
|
3575
3622
|
if (e.wet) {
|
|
3576
3623
|
if (paper !== this.doc.paper) this.wet.paperChanged();
|
|
3577
3624
|
this.wet.restore(e.wet.lost ? null : e.wet.before);
|
|
@@ -3589,6 +3636,8 @@ var PaintEditorController = class _PaintEditorController {
|
|
|
3589
3636
|
this.undoStack.push(e);
|
|
3590
3637
|
const paper = this.doc.paper;
|
|
3591
3638
|
this.doc = e.after;
|
|
3639
|
+
this.docRevision++;
|
|
3640
|
+
if (e.guides) this.restoreGuides(e.guides.after);
|
|
3592
3641
|
if (e.wet) {
|
|
3593
3642
|
this.wet.restore(e.wet.lost ? null : e.wet.after);
|
|
3594
3643
|
if (paper !== this.doc.paper) this.wet.paperChanged();
|
|
@@ -3596,6 +3645,10 @@ var PaintEditorController = class _PaintEditorController {
|
|
|
3596
3645
|
this.emit();
|
|
3597
3646
|
return e.label;
|
|
3598
3647
|
}
|
|
3648
|
+
restoreGuides(guides) {
|
|
3649
|
+
if (guides.tracing !== this.guides.tracing) this.tracingVersion++;
|
|
3650
|
+
this.guides = guides;
|
|
3651
|
+
}
|
|
3599
3652
|
/** Labels of the undo steps, oldest first. */
|
|
3600
3653
|
historyLabels() {
|
|
3601
3654
|
return { undo: this.undoStack.map((e) => e.label), redo: this.redoStack.map((e) => e.label).reverse() };
|
|
@@ -3616,7 +3669,7 @@ var PaintEditorController = class _PaintEditorController {
|
|
|
3616
3669
|
width: options.width ?? this.doc.canvas.width,
|
|
3617
3670
|
height: options.height ?? this.doc.canvas.height,
|
|
3618
3671
|
dpi: options.dpi ?? this.doc.canvas.dpi,
|
|
3619
|
-
paper: options.paper === void 0 ? this.doc.paper : this.resolvePaper(options.paper)
|
|
3672
|
+
paper: options.paper === void 0 ? { ...this.doc.paper, offset: void 0 } : this.resolvePaper(options.paper)
|
|
3620
3673
|
});
|
|
3621
3674
|
this.extensions = {};
|
|
3622
3675
|
this.guides = { ...this.guides, symmetry: { ...this.guides.symmetry, center: { x: next.canvas.width / 2, y: next.canvas.height / 2 } }, tracing: null };
|
|
@@ -3643,6 +3696,54 @@ var PaintEditorController = class _PaintEditorController {
|
|
|
3643
3696
|
this.commit("Canvas size", this.withWetEvents({ ...this.doc, canvas }));
|
|
3644
3697
|
return canvas;
|
|
3645
3698
|
}
|
|
3699
|
+
/**
|
|
3700
|
+
* Grows the canvas by `left`, `top`, `right` and `bottom` pixels without moving anything
|
|
3701
|
+
* relative to anything else: the layers (with their masks, relief and stickers), the paper grain,
|
|
3702
|
+
* the effect paper, guides, stencils, the tracing image and the recorded strokes and events all
|
|
3703
|
+
* move by (left, top) in document pixels, `document.origin` moves by (−left, −top), and the view
|
|
3704
|
+
* pans so the picture stays still on screen.
|
|
3705
|
+
*
|
|
3706
|
+
* Wet paint dries first (the wet simulation is canvas-sized), as with `setCanvas`; one undo step
|
|
3707
|
+
* (undo brings back the old size, origin and guides, and the wet paint). Returns the new canvas,
|
|
3708
|
+
* where it sits in the previous document's pixels, and the origin.
|
|
3709
|
+
*/
|
|
3710
|
+
extendCanvas(by) {
|
|
3711
|
+
this.requireIdle("extending the canvas");
|
|
3712
|
+
const side = (k) => {
|
|
3713
|
+
const v = by[k] ?? 0;
|
|
3714
|
+
if (!Number.isInteger(v) || v < 0) throw new PaintEditorError(`${k} is ${JSON.stringify(by[k])}; use a whole number of pixels to add, 0 or more.`);
|
|
3715
|
+
return v;
|
|
3716
|
+
};
|
|
3717
|
+
const left = side("left");
|
|
3718
|
+
const top = side("top");
|
|
3719
|
+
const right = side("right");
|
|
3720
|
+
const bottom = side("bottom");
|
|
3721
|
+
if (!(left || top || right || bottom)) throw new PaintEditorError("Nothing to extend: give left, top, right or bottom (pixels to add on that side) above 0.");
|
|
3722
|
+
const was = this.doc.canvas;
|
|
3723
|
+
const width = was.width + left + right;
|
|
3724
|
+
const height = was.height + top + bottom;
|
|
3725
|
+
if (width > MAX_CANVAS_SIDE || height > MAX_CANVAS_SIDE) {
|
|
3726
|
+
throw new PaintEditorError(`The canvas would be ${width}\xD7${height}; the limit is ${MAX_CANVAS_SIDE} px per side. Extend by less (it is ${was.width}\xD7${was.height} now).`);
|
|
3727
|
+
}
|
|
3728
|
+
if (this.wet.world) {
|
|
3729
|
+
this.requireWetIdle("extending the canvas");
|
|
3730
|
+
this.dryLayersFirst(this.wet.wetLayerIds());
|
|
3731
|
+
const settings = [...this.wet.settings];
|
|
3732
|
+
this.wet.reset();
|
|
3733
|
+
for (const [id, s] of settings) this.wet.settings.set(id, s.flow.center ? { ...s, flow: { ...s.flow, center: { x: s.flow.center.x + left, y: s.flow.center.y + top } } } : s);
|
|
3734
|
+
}
|
|
3735
|
+
const next = { ...shiftDocument(this.withWetEvents(this.doc), left, top), canvas: { ...was, width, height } };
|
|
3736
|
+
const m = viewMatrix(this.view, was, { width: 0, height: 0 });
|
|
3737
|
+
const dx = (right - left) / 2;
|
|
3738
|
+
const dy = (bottom - top) / 2;
|
|
3739
|
+
this.view = { ...this.view, pan: { x: this.view.pan.x + m[0] * dx + m[1] * dy, y: this.view.pan.y + m[2] * dx + m[3] * dy } };
|
|
3740
|
+
const guidesBefore = this.guides;
|
|
3741
|
+
if (left || top) this.restoreGuides(shiftGuides(this.guides, left, top));
|
|
3742
|
+
this.commit("Extend canvas", next);
|
|
3743
|
+
if (this.guides !== guidesBefore) this.undoStack[this.undoStack.length - 1].guides = { before: guidesBefore, after: this.guides };
|
|
3744
|
+
const origin = { ...next.origin ?? { x: 0, y: 0 } };
|
|
3745
|
+
return { canvas: { ...next.canvas }, rect: { x: 0 - left, y: 0 - top, width, height }, origin };
|
|
3746
|
+
}
|
|
3646
3747
|
/** Changes the paper: a library preset (`preset`), and / or any of its settings (one undo step). */
|
|
3647
3748
|
setPaper(update) {
|
|
3648
3749
|
this.requireIdle("changing the paper");
|
|
@@ -3654,7 +3755,7 @@ var PaintEditorController = class _PaintEditorController {
|
|
|
3654
3755
|
}
|
|
3655
3756
|
const { preset: _p, ...rest } = update;
|
|
3656
3757
|
if (rest.color !== void 0) parseColor(rest.color, "paper color");
|
|
3657
|
-
const paper = normalizePaper({ ...rest, preset: base.preset }, base);
|
|
3758
|
+
const paper = normalizePaper({ ...rest, preset: base.preset, offset: rest.offset ?? this.doc.paper.offset }, base);
|
|
3658
3759
|
const changedKeys = Object.keys(rest).filter((k) => k !== "name");
|
|
3659
3760
|
if (changedKeys.length && update.preset === void 0) paper.preset = base.preset;
|
|
3660
3761
|
if (this.wet.world) this.beginWetOp();
|
|
@@ -5220,6 +5321,7 @@ var PaintEditorController = class _PaintEditorController {
|
|
|
5220
5321
|
this.undoStack = [];
|
|
5221
5322
|
this.redoStack = [];
|
|
5222
5323
|
this.doc = this.readThick(loaded.state, loaded.extensions, loaded.problems);
|
|
5324
|
+
this.docRevision++;
|
|
5223
5325
|
delete this.extensions.layerChannels;
|
|
5224
5326
|
delete this.extensions.lighting;
|
|
5225
5327
|
delete this.extensions.stickers;
|
|
@@ -5815,6 +5917,60 @@ var PaintEditorController = class _PaintEditorController {
|
|
|
5815
5917
|
}
|
|
5816
5918
|
};
|
|
5817
5919
|
var MAX_IMAGE_SIDE = 8192;
|
|
5920
|
+
var movePoint = (p, dx, dy) => ({ x: p.x + dx, y: p.y + dy });
|
|
5921
|
+
var moveRect = (r, dx, dy) => ({ ...r, x: r.x + dx, y: r.y + dy });
|
|
5922
|
+
function shiftDocument(doc, dx, dy) {
|
|
5923
|
+
if (!dx && !dy) return doc;
|
|
5924
|
+
let layers = doc.layers;
|
|
5925
|
+
for (const l of flattenLayers(doc.layers)) if (l.type === "pixel") layers = mapLayer(layers, l.id, () => ({ ...l, offset: movePoint(l.offset, dx, dy) }));
|
|
5926
|
+
const m = [1, 0, 0, 1, dx, dy];
|
|
5927
|
+
const moveStickers = (list) => list.map((s) => ({ ...s, x: Math.round((s.x + dx) * 100) / 100, y: Math.round((s.y + dy) * 100) / 100 }));
|
|
5928
|
+
const out = {
|
|
5929
|
+
...doc,
|
|
5930
|
+
layers,
|
|
5931
|
+
paper: { ...doc.paper, offset: movePoint(doc.paper.offset ?? { x: 0, y: 0 }, dx, dy) },
|
|
5932
|
+
origin: movePoint(doc.origin ?? { x: 0, y: 0 }, -dx, -dy),
|
|
5933
|
+
strokes: doc.strokes.map((s) => {
|
|
5934
|
+
const moved = varyStroke(s, m, {});
|
|
5935
|
+
return s.stencils ? { ...moved, stencils: s.stencils.map((x) => translateStencil(x, dx, dy)) } : moved;
|
|
5936
|
+
})
|
|
5937
|
+
};
|
|
5938
|
+
if (doc.stickers) out.stickers = Object.fromEntries(Object.entries(doc.stickers).map(([id, list]) => [id, moveStickers(list)]));
|
|
5939
|
+
if (doc.paintEvents) {
|
|
5940
|
+
out.paintEvents = doc.paintEvents.map((e) => {
|
|
5941
|
+
const list = e.kind === "stickers" ? e.data?.stickers : void 0;
|
|
5942
|
+
return list ? { ...e, data: { ...e.data, stickers: moveStickers(list) } } : e;
|
|
5943
|
+
});
|
|
5944
|
+
}
|
|
5945
|
+
if (doc.wetEvents) {
|
|
5946
|
+
out.wetEvents = doc.wetEvents.map((e) => {
|
|
5947
|
+
const d = e.data;
|
|
5948
|
+
if (!d) return e;
|
|
5949
|
+
if (e.kind === "dry" && typeof d.x === "number") return { ...e, data: moveRect(d, dx, dy) };
|
|
5950
|
+
if (e.kind === "wet-paper" && d.rect) return { ...e, data: { ...d, rect: moveRect(d.rect, dx, dy) } };
|
|
5951
|
+
if (e.kind === "flow" && d.center) return { ...e, data: { ...d, center: movePoint(d.center, dx, dy) } };
|
|
5952
|
+
return e;
|
|
5953
|
+
});
|
|
5954
|
+
}
|
|
5955
|
+
if (doc.paperEffect) {
|
|
5956
|
+
const src = doc.paperEffect.image;
|
|
5957
|
+
const width = src.width + dx;
|
|
5958
|
+
const height = src.height + dy;
|
|
5959
|
+
const data = new Uint8ClampedArray(width * height * 4);
|
|
5960
|
+
for (let y = 0; y < src.height; y++) data.set(src.data.subarray(y * src.width * 4, (y + 1) * src.width * 4), ((y + dy) * width + dx) * 4);
|
|
5961
|
+
out.paperEffect = { ...doc.paperEffect, image: { width, height, data }, version: nextPaperEffectVersion() };
|
|
5962
|
+
}
|
|
5963
|
+
return out;
|
|
5964
|
+
}
|
|
5965
|
+
function shiftGuides(g, dx, dy) {
|
|
5966
|
+
return {
|
|
5967
|
+
...g,
|
|
5968
|
+
symmetry: { ...g.symmetry, center: movePoint(g.symmetry.center, dx, dy) },
|
|
5969
|
+
perspective: { ...g.perspective, points: g.perspective.points.map((p) => movePoint(p, dx, dy)) },
|
|
5970
|
+
tracing: g.tracing ? { ...g.tracing, rect: moveRect(g.tracing.rect, dx, dy) } : null,
|
|
5971
|
+
stencils: g.stencils.map((s) => translateStencil(s, dx, dy))
|
|
5972
|
+
};
|
|
5973
|
+
}
|
|
5818
5974
|
function resampleInto(src, rect, width, height) {
|
|
5819
5975
|
const out = new Uint8ClampedArray(width * height * 4);
|
|
5820
5976
|
const x0 = Math.max(0, Math.floor(rect.x));
|
|
@@ -7143,7 +7299,7 @@ function createPaintEditorToolProvider(controller, options = {}) {
|
|
|
7143
7299
|
add({
|
|
7144
7300
|
name: "get_canvas_info",
|
|
7145
7301
|
title: "Get the canvas",
|
|
7146
|
-
description: "Reads the canvas: size in pixels and dpi, the paper (preset, colour, grain depth), the active layer, the active brush id, the paint colour, the view (zoom, rotation, mirror), the hand, how many strokes are recorded, the undo / redo labels, and the blend modes. Call it first.",
|
|
7302
|
+
description: "Reads the canvas: size in pixels and dpi, its origin (where (0, 0) is relative to the canvas it was extended from), the paper (preset, colour, grain depth), the active layer, the active brush id, the paint colour, the view (zoom, rotation, mirror), the hand, how many strokes are recorded, the undo / redo labels, and the blend modes. Call it first.",
|
|
7147
7303
|
inputSchema: EMPTY3,
|
|
7148
7304
|
annotations: { title: "Get the canvas", ...READ6 },
|
|
7149
7305
|
handler: () => {
|
|
@@ -7153,6 +7309,7 @@ function createPaintEditorToolProvider(controller, options = {}) {
|
|
|
7153
7309
|
width: d.canvas.width,
|
|
7154
7310
|
height: d.canvas.height,
|
|
7155
7311
|
dpi: d.canvas.dpi,
|
|
7312
|
+
origin: { ...d.origin ?? { x: 0, y: 0 } },
|
|
7156
7313
|
paper: { preset: d.paper.preset, name: d.paper.name, color: d.paper.color, depth: d.paper.depth },
|
|
7157
7314
|
activeLayerId: d.activeLayerId,
|
|
7158
7315
|
layerCount: c.listLayers().length,
|
|
@@ -7206,6 +7363,19 @@ function createPaintEditorToolProvider(controller, options = {}) {
|
|
|
7206
7363
|
annotations: { title: "Resize the canvas", ...SET4 },
|
|
7207
7364
|
handler: (a) => c.setCanvas({ width: a.width, height: a.height, dpi: a.dpi })
|
|
7208
7365
|
});
|
|
7366
|
+
const EXTEND_SIDE = (side) => ({ type: "integer", minimum: 0, maximum: MAX_CANVAS_SIDE - 1, description: `Pixels to add on the ${side} (default 0).` });
|
|
7367
|
+
add({
|
|
7368
|
+
name: "extend_canvas",
|
|
7369
|
+
title: "Extend the canvas",
|
|
7370
|
+
description: "Grows the canvas by adding pixels on any side, keeping everything painted. Adding on the left or top moves the whole picture (layers, paper grain, guides, stencils, recorded strokes) right or down by that much in document pixels, so coordinates you read before change by (+left, +top). Wet paint dries first. One undo step. Returns the canvas, rect (the new canvas in the previous coordinates: x -left, y -top) and origin.",
|
|
7371
|
+
inputSchema: {
|
|
7372
|
+
type: "object",
|
|
7373
|
+
properties: { left: EXTEND_SIDE("left"), top: EXTEND_SIDE("top"), right: EXTEND_SIDE("right"), bottom: EXTEND_SIDE("bottom") },
|
|
7374
|
+
additionalProperties: false
|
|
7375
|
+
},
|
|
7376
|
+
annotations: { title: "Extend the canvas", ...WRITE5 },
|
|
7377
|
+
handler: (a) => c.extendCanvas({ left: a.left, top: a.top, right: a.right, bottom: a.bottom })
|
|
7378
|
+
});
|
|
7209
7379
|
add({
|
|
7210
7380
|
name: "list_papers",
|
|
7211
7381
|
title: "List papers",
|
|
@@ -7810,7 +7980,7 @@ function createPaintEditorToolProvider(controller, options = {}) {
|
|
|
7810
7980
|
add({
|
|
7811
7981
|
name: "save_document",
|
|
7812
7982
|
title: "Save the document",
|
|
7813
|
-
description: "Returns the document as JSON (format pixerate-paint, version 1): canvas, paper, layers with PNG data URLs, and the recorded strokes unless strokes is false. Changes nothing.",
|
|
7983
|
+
description: "Deprecated (kept for one minor version): use get_paint_document, which returns the painting with images as asset references, or save_paint_document to store it. Returns the document as JSON (format pixerate-paint, version 1): canvas, paper, layers with PNG data URLs, and the recorded strokes unless strokes is false. Changes nothing.",
|
|
7814
7984
|
inputSchema: { type: "object", properties: { strokes: { type: "boolean", description: "Include the recorded strokes (default true)." } }, additionalProperties: false },
|
|
7815
7985
|
annotations: { title: "Save the document", ...READ6 },
|
|
7816
7986
|
handler: ({ strokes }) => c.toJSON({ strokes })
|
|
@@ -7818,7 +7988,7 @@ function createPaintEditorToolProvider(controller, options = {}) {
|
|
|
7818
7988
|
add({
|
|
7819
7989
|
name: "open_document",
|
|
7820
7990
|
title: "Open a document",
|
|
7821
|
-
description: "Opens a saved paint document (as save_document returns), replacing the painting and clearing the undo history. Returns the canvas, the layers and anything that could not be read.",
|
|
7991
|
+
description: "Deprecated (kept for one minor version): use load_paint_document or open_saved_paint_document. Opens a saved paint document (as save_document returns), replacing the painting and clearing the undo history. Returns the canvas, the layers and anything that could not be read.",
|
|
7822
7992
|
inputSchema: { type: "object", properties: { document: { type: "object", description: "The saved document JSON." } }, required: ["document"], additionalProperties: false },
|
|
7823
7993
|
annotations: { title: "Open a document", ...DESTRUCTIVE3 },
|
|
7824
7994
|
handler: ({ document }) => {
|
|
@@ -8560,6 +8730,37 @@ function createPaintEffectsToolProvider(controller, options = {}) {
|
|
|
8560
8730
|
]
|
|
8561
8731
|
}), { director: false, surface: options.playbackSurface });
|
|
8562
8732
|
}
|
|
8733
|
+
|
|
8734
|
+
// src/paint-editor/editorDocument.ts
|
|
8735
|
+
function createPaintEditorDocument(controller, options = {}) {
|
|
8736
|
+
return defineEditorDocument({
|
|
8737
|
+
kind: "paint",
|
|
8738
|
+
recordsLoads: false,
|
|
8739
|
+
assets: options.assets ?? createEditorAssetRegistry(),
|
|
8740
|
+
get: () => controller.toJSON({ strokes: options.strokes ?? true }),
|
|
8741
|
+
load: (document) => {
|
|
8742
|
+
const problems = controller.loadJSON(document);
|
|
8743
|
+
return { problems: problems.map((message) => ({ section: "painting", message })) };
|
|
8744
|
+
},
|
|
8745
|
+
subscribe: (listener) => {
|
|
8746
|
+
let revision = controller.documentRevision;
|
|
8747
|
+
return controller.subscribe(() => {
|
|
8748
|
+
if (controller.documentRevision === revision) return;
|
|
8749
|
+
revision = controller.documentRevision;
|
|
8750
|
+
listener();
|
|
8751
|
+
});
|
|
8752
|
+
},
|
|
8753
|
+
history: {
|
|
8754
|
+
undo: () => controller.undo() !== null,
|
|
8755
|
+
redo: () => controller.redo() !== null,
|
|
8756
|
+
getState: () => {
|
|
8757
|
+
const s = controller.getState();
|
|
8758
|
+
return { canUndo: s.canUndo, canRedo: s.canRedo, ...s.undoLabel ? { undoName: s.undoLabel } : {}, ...s.redoLabel ? { redoName: s.redoLabel } : {} };
|
|
8759
|
+
}
|
|
8760
|
+
},
|
|
8761
|
+
readOnly: () => controller.busyReason() ?? false
|
|
8762
|
+
});
|
|
8763
|
+
}
|
|
8563
8764
|
export {
|
|
8564
8765
|
BLEND_MENU_GROUPS,
|
|
8565
8766
|
BLEND_MODES,
|
|
@@ -8699,6 +8900,7 @@ export {
|
|
|
8699
8900
|
contactSheet,
|
|
8700
8901
|
createPaintCompositor,
|
|
8701
8902
|
createPaintDocument,
|
|
8903
|
+
createPaintEditorDocument,
|
|
8702
8904
|
createPaintEditorToolProvider,
|
|
8703
8905
|
createPaintEffectsToolProvider,
|
|
8704
8906
|
createPaintLayer,
|
|
@@ -8848,6 +9050,7 @@ export {
|
|
|
8848
9050
|
timeLapseZip,
|
|
8849
9051
|
toStrokePoint,
|
|
8850
9052
|
toggleStamp,
|
|
9053
|
+
translateStencil,
|
|
8851
9054
|
updatePaintGradientStop,
|
|
8852
9055
|
usesPaintExchange,
|
|
8853
9056
|
variationAffine,
|
|
@@ -40,6 +40,14 @@ export interface PaperSettings {
|
|
|
40
40
|
roughness: number;
|
|
41
41
|
/** Metallic paper (gold or silver leaf, pearlescent card): the grain catches the light, 0..1 (default 0). */
|
|
42
42
|
metallic?: number;
|
|
43
|
+
/**
|
|
44
|
+
* Where the grain's (0, 0) sits in document pixels (missing = the top-left). `extendCanvas` moves
|
|
45
|
+
* it with the paint, so the texture stays under what was painted on it.
|
|
46
|
+
*/
|
|
47
|
+
offset?: {
|
|
48
|
+
x: number;
|
|
49
|
+
y: number;
|
|
50
|
+
};
|
|
43
51
|
}
|
|
44
52
|
type PaperPreset = PaperSettings;
|
|
45
53
|
/** The built-in paper library: generated papers in families. */
|
|
@@ -40,6 +40,14 @@ export interface PaperSettings {
|
|
|
40
40
|
roughness: number;
|
|
41
41
|
/** Metallic paper (gold or silver leaf, pearlescent card): the grain catches the light, 0..1 (default 0). */
|
|
42
42
|
metallic?: number;
|
|
43
|
+
/**
|
|
44
|
+
* Where the grain's (0, 0) sits in document pixels (missing = the top-left). `extendCanvas` moves
|
|
45
|
+
* it with the paint, so the texture stays under what was painted on it.
|
|
46
|
+
*/
|
|
47
|
+
offset?: {
|
|
48
|
+
x: number;
|
|
49
|
+
y: number;
|
|
50
|
+
};
|
|
43
51
|
}
|
|
44
52
|
type PaperPreset = PaperSettings;
|
|
45
53
|
/** The built-in paper library: generated papers in families. */
|
|
@@ -76,5 +76,7 @@ export declare function stencilMask(stencils: readonly Stencil[], width: number,
|
|
|
76
76
|
height: number;
|
|
77
77
|
data: Uint8Array;
|
|
78
78
|
} | null;
|
|
79
|
+
/** A stencil moved by (dx, dy) document pixels (for a canvas extended to the left or top). */
|
|
80
|
+
export declare function translateStencil(s: Stencil, dx: number, dy: number): Stencil;
|
|
79
81
|
/** A stencil scaled about the origin by `k` (for repainting at a larger size). */
|
|
80
82
|
export declare function scaleStencil(s: Stencil, k: number): Stencil;
|
|
@@ -76,5 +76,7 @@ export declare function stencilMask(stencils: readonly Stencil[], width: number,
|
|
|
76
76
|
height: number;
|
|
77
77
|
data: Uint8Array;
|
|
78
78
|
} | null;
|
|
79
|
+
/** A stencil moved by (dx, dy) document pixels (for a canvas extended to the left or top). */
|
|
80
|
+
export declare function translateStencil(s: Stencil, dx: number, dy: number): Stencil;
|
|
79
81
|
/** A stencil scaled about the origin by `k` (for repainting at a larger size). */
|
|
80
82
|
export declare function scaleStencil(s: Stencil, k: number): Stencil;
|
|
@@ -266,6 +266,7 @@ export declare class PaintWetLayers {
|
|
|
266
266
|
snapshot(): WetSnapshot | null;
|
|
267
267
|
/** Puts a snapshot back (`null`: no wet layers). Returns the layer surfaces it implies. */
|
|
268
268
|
restore(snap: WetSnapshot | null): void;
|
|
269
|
+
private dropWorld;
|
|
269
270
|
/** Forgets everything wet (a new canvas, a resize, opening a document). */
|
|
270
271
|
reset(): void;
|
|
271
272
|
/** Water overlay tiles (RGBA) over a document rectangle, for the view. */
|
|
@@ -266,6 +266,7 @@ export declare class PaintWetLayers {
|
|
|
266
266
|
snapshot(): WetSnapshot | null;
|
|
267
267
|
/** Puts a snapshot back (`null`: no wet layers). Returns the layer surfaces it implies. */
|
|
268
268
|
restore(snap: WetSnapshot | null): void;
|
|
269
|
+
private dropWorld;
|
|
269
270
|
/** Forgets everything wet (a new canvas, a resize, opening a document). */
|
|
270
271
|
reset(): void;
|
|
271
272
|
/** Water overlay tiles (RGBA) over a document rectangle, for the view. */
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; } var _class;
|
|
2
2
|
|
|
3
|
-
var
|
|
4
|
-
require('../chunk-L5OWG2NA.cjs');
|
|
3
|
+
var _chunkM4MIZPQIcjs = require('../chunk-M4MIZPQI.cjs');
|
|
5
4
|
require('../chunk-2BSBFBOA.cjs');
|
|
6
5
|
|
|
7
6
|
|
|
@@ -1328,7 +1327,7 @@ function mapRange(value, from, to, options) {
|
|
|
1328
1327
|
// src/parameters/editorDocument.ts
|
|
1329
1328
|
var isRecord = (value) => !!value && typeof value === "object" && !Array.isArray(value);
|
|
1330
1329
|
function createParametersEditorDocument(controller) {
|
|
1331
|
-
return
|
|
1330
|
+
return _chunkM4MIZPQIcjs.defineEditorDocument.call(void 0, {
|
|
1332
1331
|
kind: "parameters",
|
|
1333
1332
|
get: () => controller.toJSON(),
|
|
1334
1333
|
load: (document, { history }) => {
|
package/dist/parameters/index.js
CHANGED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { StorageAdapter } from "./storage.cjs";
|
|
2
|
+
/** Prefix of an asset reference in a document (`pixerate-asset:<length>-<hash>`). */
|
|
3
|
+
export declare const EDITOR_ASSET_PREFIX = "pixerate-asset:";
|
|
4
|
+
/**
|
|
5
|
+
* Inline binary data kept out of an editor document: PNG data URLs and long base64 strings
|
|
6
|
+
* (layer pixels, masks, thick-paint channels, patterns…) are replaced by short, content-addressed
|
|
7
|
+
* references, and the bytes live here. `put` and `get` are synchronous, so an `EditorDocument`
|
|
8
|
+
* can stay synchronous; `persistEditorAssets` / `preloadEditorAssets` move the bytes to and from
|
|
9
|
+
* storage around a save or an open.
|
|
10
|
+
*/
|
|
11
|
+
export interface EditorAssetRegistry {
|
|
12
|
+
/** Stores `data` (once per content) and returns its reference. */
|
|
13
|
+
put(data: string): string;
|
|
14
|
+
/** The data for `ref`, or `undefined` when it is not loaded. */
|
|
15
|
+
get(ref: string): string | undefined;
|
|
16
|
+
has(ref: string): boolean;
|
|
17
|
+
/** Puts data read back from storage under its reference. */
|
|
18
|
+
set(ref: string, data: string): void;
|
|
19
|
+
readonly size: number;
|
|
20
|
+
}
|
|
21
|
+
/** Whether a string is inline binary data worth keeping out of the document. */
|
|
22
|
+
export declare function isInlineAsset(value: string): boolean;
|
|
23
|
+
export declare function createEditorAssetRegistry(): EditorAssetRegistry;
|
|
24
|
+
/** A deep copy of `value` with every inline asset string replaced by its reference. */
|
|
25
|
+
export declare function externalizeAssets<T>(value: T, registry: EditorAssetRegistry): T;
|
|
26
|
+
/** Every asset reference in a document (or its JSON text). */
|
|
27
|
+
export declare function collectAssetRefs(value: unknown): string[];
|
|
28
|
+
/**
|
|
29
|
+
* A deep copy of `value` with every asset reference replaced by its data. Throws an error naming the references that are not loaded (and changes nothing).
|
|
30
|
+
*/
|
|
31
|
+
export declare function internalizeAssets<T>(value: T, registry: EditorAssetRegistry): T;
|
|
32
|
+
export interface EditorAssetStorageOptions {
|
|
33
|
+
/** Key prefix of stored assets (default `pixerate.assets:`); one shared pool, as assets are content-addressed. */
|
|
34
|
+
prefix?: string;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Writes the assets `document` (or its JSON text) refers to into `storage`, skipping those
|
|
38
|
+
* this storage already has. Call before writing the document, so it never points at missing data.
|
|
39
|
+
*/
|
|
40
|
+
export declare function persistEditorAssets(storage: StorageAdapter, registry: EditorAssetRegistry, document: unknown, options?: EditorAssetStorageOptions): Promise<void>;
|
|
41
|
+
/**
|
|
42
|
+
* Reads the assets `document` (or its JSON text) refers to from `storage` into the registry.
|
|
43
|
+
* Returns the references that could not be found.
|
|
44
|
+
*/
|
|
45
|
+
export declare function preloadEditorAssets(storage: StorageAdapter, registry: EditorAssetRegistry, document: unknown, options?: EditorAssetStorageOptions): Promise<string[]>;
|
|
46
|
+
/**
|
|
47
|
+
* `storage` with assets handled: every `set` first writes the assets the value refers to, and
|
|
48
|
+
* every `get` reads them into the registry before returning. Wrap the storage an autosave or a
|
|
49
|
+
* document store uses for an editor whose document has `assets`.
|
|
50
|
+
*/
|
|
51
|
+
export declare function withEditorAssets(storage: StorageAdapter, registry: EditorAssetRegistry, options?: EditorAssetStorageOptions): StorageAdapter;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { StorageAdapter } from "./storage.js";
|
|
2
|
+
/** Prefix of an asset reference in a document (`pixerate-asset:<length>-<hash>`). */
|
|
3
|
+
export declare const EDITOR_ASSET_PREFIX = "pixerate-asset:";
|
|
4
|
+
/**
|
|
5
|
+
* Inline binary data kept out of an editor document: PNG data URLs and long base64 strings
|
|
6
|
+
* (layer pixels, masks, thick-paint channels, patterns…) are replaced by short, content-addressed
|
|
7
|
+
* references, and the bytes live here. `put` and `get` are synchronous, so an `EditorDocument`
|
|
8
|
+
* can stay synchronous; `persistEditorAssets` / `preloadEditorAssets` move the bytes to and from
|
|
9
|
+
* storage around a save or an open.
|
|
10
|
+
*/
|
|
11
|
+
export interface EditorAssetRegistry {
|
|
12
|
+
/** Stores `data` (once per content) and returns its reference. */
|
|
13
|
+
put(data: string): string;
|
|
14
|
+
/** The data for `ref`, or `undefined` when it is not loaded. */
|
|
15
|
+
get(ref: string): string | undefined;
|
|
16
|
+
has(ref: string): boolean;
|
|
17
|
+
/** Puts data read back from storage under its reference. */
|
|
18
|
+
set(ref: string, data: string): void;
|
|
19
|
+
readonly size: number;
|
|
20
|
+
}
|
|
21
|
+
/** Whether a string is inline binary data worth keeping out of the document. */
|
|
22
|
+
export declare function isInlineAsset(value: string): boolean;
|
|
23
|
+
export declare function createEditorAssetRegistry(): EditorAssetRegistry;
|
|
24
|
+
/** A deep copy of `value` with every inline asset string replaced by its reference. */
|
|
25
|
+
export declare function externalizeAssets<T>(value: T, registry: EditorAssetRegistry): T;
|
|
26
|
+
/** Every asset reference in a document (or its JSON text). */
|
|
27
|
+
export declare function collectAssetRefs(value: unknown): string[];
|
|
28
|
+
/**
|
|
29
|
+
* A deep copy of `value` with every asset reference replaced by its data. Throws an error naming the references that are not loaded (and changes nothing).
|
|
30
|
+
*/
|
|
31
|
+
export declare function internalizeAssets<T>(value: T, registry: EditorAssetRegistry): T;
|
|
32
|
+
export interface EditorAssetStorageOptions {
|
|
33
|
+
/** Key prefix of stored assets (default `pixerate.assets:`); one shared pool, as assets are content-addressed. */
|
|
34
|
+
prefix?: string;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Writes the assets `document` (or its JSON text) refers to into `storage`, skipping those
|
|
38
|
+
* this storage already has. Call before writing the document, so it never points at missing data.
|
|
39
|
+
*/
|
|
40
|
+
export declare function persistEditorAssets(storage: StorageAdapter, registry: EditorAssetRegistry, document: unknown, options?: EditorAssetStorageOptions): Promise<void>;
|
|
41
|
+
/**
|
|
42
|
+
* Reads the assets `document` (or its JSON text) refers to from `storage` into the registry.
|
|
43
|
+
* Returns the references that could not be found.
|
|
44
|
+
*/
|
|
45
|
+
export declare function preloadEditorAssets(storage: StorageAdapter, registry: EditorAssetRegistry, document: unknown, options?: EditorAssetStorageOptions): Promise<string[]>;
|
|
46
|
+
/**
|
|
47
|
+
* `storage` with assets handled: every `set` first writes the assets the value refers to, and
|
|
48
|
+
* every `get` reads them into the registry before returning. Wrap the storage an autosave or a
|
|
49
|
+
* document store uses for an editor whose document has `assets`.
|
|
50
|
+
*/
|
|
51
|
+
export declare function withEditorAssets(storage: StorageAdapter, registry: EditorAssetRegistry, options?: EditorAssetStorageOptions): StorageAdapter;
|