@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.
Files changed (86) hide show
  1. package/dist/animation-editor/index.cjs +60 -61
  2. package/dist/animation-editor/index.js +5 -6
  3. package/dist/{brushstrokeRender-YAZVQUXY.cjs → brushstrokeRender-5WANQIG4.cjs} +10 -10
  4. package/dist/{brushstrokeRender-EUDQTDYS.js → brushstrokeRender-Q4LI22QV.js} +3 -3
  5. package/dist/canvas/index.cjs +23 -24
  6. package/dist/canvas/index.js +3 -4
  7. package/dist/chart/index.cjs +2 -3
  8. package/dist/chart/index.js +1 -2
  9. package/dist/{chunk-AGZ47XY2.js → chunk-2N2QPO3H.js} +6 -2
  10. package/dist/{chunk-NMXPOKF3.js → chunk-45XXVHVG.js} +1 -1
  11. package/dist/{chunk-FLOGD3QB.js → chunk-6UPGM3JB.js} +2 -2
  12. package/dist/{chunk-6FUAHCVI.cjs → chunk-7NW46N7I.cjs} +16 -16
  13. package/dist/{chunk-ORDXMBD4.js → chunk-A5UCR2TO.js} +10 -3
  14. package/dist/{chunk-PI45JMP7.cjs → chunk-CD7PLU77.cjs} +81 -81
  15. package/dist/{chunk-LRRCMRSL.cjs → chunk-EBDCRTZI.cjs} +20 -16
  16. package/dist/{chunk-OLCYQLFE.js → chunk-EL5PAHNP.js} +1 -1
  17. package/dist/{chunk-PUC43JPT.cjs → chunk-EYVVIZJ3.cjs} +1 -1
  18. package/dist/{chunk-BXMIBMVG.cjs → chunk-JGKCC5OF.cjs} +65 -58
  19. package/dist/chunk-M4MIZPQI.cjs +393 -0
  20. package/dist/{chunk-LIXLD433.cjs → chunk-MHEXLG3M.cjs} +35 -35
  21. package/dist/chunk-NHLYK3AX.js +393 -0
  22. package/dist/{chunk-RHXW4TA5.cjs → chunk-QMI5HZWA.cjs} +7 -7
  23. package/dist/{chunk-XFDQEAOS.cjs → chunk-RQCMSEM5.cjs} +2 -2
  24. package/dist/{chunk-BCY22GBH.js → chunk-SDHZG5TF.js} +1 -1
  25. package/dist/{chunk-HVX35M2I.cjs → chunk-WI7QJA7A.cjs} +2 -2
  26. package/dist/{chunk-UILRJQFD.js → chunk-WNAAWLZ5.js} +1 -1
  27. package/dist/{chunk-4J4MC3F5.js → chunk-YXOW456B.js} +1 -1
  28. package/dist/{chunk-Y64YPIFT.js → chunk-ZQWLWDZI.js} +1 -1
  29. package/dist/diagram/index.cjs +2 -3
  30. package/dist/diagram/index.js +1 -2
  31. package/dist/image-editor/controller.d.cts +8 -0
  32. package/dist/image-editor/controller.d.ts +8 -0
  33. package/dist/image-editor/editorDocument.d.cts +16 -0
  34. package/dist/image-editor/editorDocument.d.ts +16 -0
  35. package/dist/image-editor/index.cjs +787 -737
  36. package/dist/image-editor/index.d.cts +1 -0
  37. package/dist/image-editor/index.d.ts +1 -0
  38. package/dist/image-editor/index.js +56 -6
  39. package/dist/index.cjs +4 -5
  40. package/dist/index.js +2 -3
  41. package/dist/paint/grain.d.cts +7 -0
  42. package/dist/paint/grain.d.ts +7 -0
  43. package/dist/paint/index.cjs +3 -3
  44. package/dist/paint/index.js +2 -2
  45. package/dist/paint-editor/controller.d.cts +38 -0
  46. package/dist/paint-editor/controller.d.ts +38 -0
  47. package/dist/paint-editor/document.d.cts +8 -0
  48. package/dist/paint-editor/document.d.ts +8 -0
  49. package/dist/paint-editor/editorDocument.d.cts +17 -0
  50. package/dist/paint-editor/editorDocument.d.ts +17 -0
  51. package/dist/paint-editor/index.cjs +476 -273
  52. package/dist/paint-editor/index.d.cts +1 -0
  53. package/dist/paint-editor/index.d.ts +1 -0
  54. package/dist/paint-editor/index.js +216 -13
  55. package/dist/paint-editor/paper.d.cts +8 -0
  56. package/dist/paint-editor/paper.d.ts +8 -0
  57. package/dist/paint-editor/stencils.d.cts +2 -0
  58. package/dist/paint-editor/stencils.d.ts +2 -0
  59. package/dist/paint-editor/wet.d.cts +1 -0
  60. package/dist/paint-editor/wet.d.ts +1 -0
  61. package/dist/parameters/index.cjs +2 -3
  62. package/dist/parameters/index.js +1 -2
  63. package/dist/persistence/assets.d.cts +51 -0
  64. package/dist/persistence/assets.d.ts +51 -0
  65. package/dist/persistence/editorDocument.d.cts +12 -0
  66. package/dist/persistence/editorDocument.d.ts +12 -0
  67. package/dist/persistence/index.cjs +32 -8
  68. package/dist/persistence/index.d.cts +1 -0
  69. package/dist/persistence/index.d.ts +1 -0
  70. package/dist/persistence/index.js +34 -10
  71. package/dist/spreadsheet/index.cjs +3 -4
  72. package/dist/spreadsheet/index.js +2 -3
  73. package/dist/tokens/index.cjs +3 -4
  74. package/dist/tokens/index.js +2 -3
  75. package/dist/vector/index.cjs +952 -953
  76. package/dist/vector/index.js +5 -6
  77. package/dist/video-editor/index.cjs +2 -3
  78. package/dist/video-editor/index.js +1 -2
  79. package/package.json +1 -1
  80. package/skills/pixerate-image-editor/SKILL.md +16 -24
  81. package/skills/pixerate-image-editor/references/smart-objects.md +28 -0
  82. package/skills/pixerate-paint-editor/SKILL.md +26 -6
  83. package/dist/chunk-5H2C3QSF.cjs +0 -134
  84. package/dist/chunk-GNV54DLO.js +0 -134
  85. package/dist/chunk-L5OWG2NA.cjs +0 -135
  86. package/dist/chunk-WHKNYFM2.js +0 -135
@@ -38,7 +38,7 @@ import {
38
38
  reverseMorphContour,
39
39
  subpathToMorphContour,
40
40
  turnMorphContour
41
- } from "../chunk-UILRJQFD.js";
41
+ } from "../chunk-WNAAWLZ5.js";
42
42
  import {
43
43
  DEFAULT_VECTOR_FONT,
44
44
  IDENTITY_MATRIX,
@@ -507,8 +507,8 @@ import {
507
507
  withVectorStrokes,
508
508
  withVectorTargetPaint,
509
509
  zigzagPaths
510
- } from "../chunk-FLOGD3QB.js";
511
- import "../chunk-ORDXMBD4.js";
510
+ } from "../chunk-6UPGM3JB.js";
511
+ import "../chunk-A5UCR2TO.js";
512
512
  import {
513
513
  createProvenance
514
514
  } from "../chunk-M6DNZNVY.js";
@@ -545,7 +545,7 @@ import {
545
545
  import "../chunk-V2ZCJRDM.js";
546
546
  import {
547
547
  GRAIN_PATTERNS
548
- } from "../chunk-AGZ47XY2.js";
548
+ } from "../chunk-2N2QPO3H.js";
549
549
  import "../chunk-ODBT55Q7.js";
550
550
  import {
551
551
  bytesToBase64,
@@ -560,8 +560,7 @@ import "../chunk-SIFAIGB3.js";
560
560
  import "../chunk-VDGBHYC7.js";
561
561
  import {
562
562
  defineEditorDocument
563
- } from "../chunk-GNV54DLO.js";
564
- import "../chunk-WHKNYFM2.js";
563
+ } from "../chunk-NHLYK3AX.js";
565
564
  import "../chunk-HF5QZS7Z.js";
566
565
  import {
567
566
  HistoryManager
@@ -147,8 +147,7 @@ var _chunkRUZQMYXHcjs = require('../chunk-RUZQMYXH.cjs');
147
147
  require('../chunk-WOFXZ3XU.cjs');
148
148
 
149
149
 
150
- var _chunk5H2C3QSFcjs = require('../chunk-5H2C3QSF.cjs');
151
- require('../chunk-L5OWG2NA.cjs');
150
+ var _chunkM4MIZPQIcjs = require('../chunk-M4MIZPQI.cjs');
152
151
  require('../chunk-2BSBFBOA.cjs');
153
152
 
154
153
 
@@ -4871,7 +4870,7 @@ function createVideoEffectsToolProvider(controller, options = {}) {
4871
4870
 
4872
4871
  // src/video-editor/editorDocument.ts
4873
4872
  function createVideoEditorDocument(controller) {
4874
- return _chunk5H2C3QSFcjs.defineEditorDocument.call(void 0, {
4873
+ return _chunkM4MIZPQIcjs.defineEditorDocument.call(void 0, {
4875
4874
  kind: "video",
4876
4875
  get: () => controller.getProject(),
4877
4876
  load: (document2, { history }) => {
@@ -147,8 +147,7 @@ import {
147
147
  import "../chunk-V2ZCJRDM.js";
148
148
  import {
149
149
  defineEditorDocument
150
- } from "../chunk-GNV54DLO.js";
151
- import "../chunk-WHKNYFM2.js";
150
+ } from "../chunk-NHLYK3AX.js";
152
151
  import "../chunk-HF5QZS7Z.js";
153
152
  import {
154
153
  HistoryManager
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pixerate/editor",
3
- "version": "0.154.0",
3
+ "version": "0.156.0",
4
4
  "description": "UI-agnostic rich text and media editing core library",
5
5
  "author": "Pixerate",
6
6
  "license": "MIT",
@@ -96,30 +96,10 @@ batching.
96
96
 
97
97
  ### Smart objects
98
98
 
99
- 1. **Convert before repeated or uncertain changes**: `convert_to_smart_object` (one pixel layer, or
100
- several siblings that nest as a layer document). A converted pixel layer looks exactly the same
101
- and keeps its id; its filters become smart filters. From then on every transform and filter is
102
- re-rendered from the contents, so scaling down and back up, or rotating twelve times, loses
103
- nothing.
104
- 2. **Read it**: `get_smart_object` gives the contents (size, kind, instances), the placement
105
- (corners, warp, and scale / rotate in percent and degrees of the contents' own size) and the
106
- smart filters with their ids.
107
- 3. **Transform**: `set_smart_object_transform` sets the transform absolutely (scale 100 = the
108
- contents' own size, `x`, `y` = where its centre goes; or `corners`, `matrix`, and a `warp` in
109
- contents pixels, `null` to remove). `transform_layer`, `warp_layer`, `flip_layer` and
110
- `rotate_layer_90` also work on a smart object, relative to its current placement, and never
111
- degrade it. `offset_layer` moves it by whole pixels.
112
- 4. **Filter non-destructively**: `add_smart_filter` (`develop` or `blur`, each with its own
113
- `blend` and `opacity`; kinds can repeat), then `update_smart_filter`, `move_smart_filter`,
114
- `remove_smart_filter` and `set_smart_filters_enabled`. `set_smart_filter_mask` limits where the
115
- filters show; paint it with `paint_stroke` / `fill_pixels` `channel: "filter-mask"`.
116
- 5. **Contents**: `copy_smart_object` with `linked: true` shares the contents (edit once, both
117
- change), `linked: false` makes them independent. `edit_smart_object_contents` opens the
118
- contents as their own document: every tool then works inside them until
119
- `close_smart_object_contents` (`save: false` discards). `replace_smart_object_contents` swaps
120
- in a new image for every linked instance; `export_smart_object_contents` returns the file.
121
- 6. **Finish**: `rasterize_smart_object` only when the user wants plain pixels to paint on; the
122
- placement and filters are then gone.
99
+ Convert a pixel layer with `convert_to_smart_object` before repeated or uncertain transforms and
100
+ filters: from then on everything re-renders from the contents and nothing degrades. Read
101
+ [references/smart-objects.md](references/smart-objects.md) for the steps (reading, transforming,
102
+ smart filters, linked contents, rasterizing).
123
103
 
124
104
  ### Shapes and text
125
105
 
@@ -413,6 +393,18 @@ in the source frame at the document's bit depth, colour mode and profile. Read
413
393
  | `add_effect_layer` | Add a layer drawn by a generator, overlay or title effect at the document's size and bit depth (`values`, `time`, `name`, `aboveId` / `belowId`, `opacity`, `blend`). Returns the layer id. |
414
394
  | `apply_selection_as_mask` | Mask a layer with the current selection: pass `layerId` to mask an existing layer, or `kind` (plus optional `params`) to create a new masked layer in one step. `density` 0..1 and `feather` px soften the mask non-destructively. Returns `layerId`, kind, mask info and the selection summary. |
415
395
 
396
+ ### Save and open
397
+
398
+ The host may add the shared document tools (see `pixerate-agent-tools`). An image document is the saved state (layers, adjustments, crop, selection...) with layer pixels, masks and patterns as asset references; the source image stays the host's.
399
+
400
+ | Tool | Use |
401
+ | --- | --- |
402
+ | `get_image_document` | Take a copy of the whole image document as `{ kind, formatVersion, revision, document }` before a risky change, or to hand it on. Images in it are asset references (`pixerate-asset:...`), never pixel data, so it stays small. To look at the pixels use `render_preview` or `capture_view`. |
403
+ | `load_image_document` | Put back a copy from `get_image_document` in this session (its `document`, or the whole result). Checked first: a document whose assets are not loaded, or from a newer build, is refused and nothing changes. One undo step (`undo_image` brings the previous document back). To change part of the image use the layer and adjustment tools. |
404
+ | `save_image_document` | Save the image document and its assets under a `name` in the host's storage (the same name replaces that copy). Returns the `key`. Only listed when the host has storage. Save when the user asks, not after every edit. |
405
+ | `list_saved_image_documents` | List saved image documents (`key`, `name`, `savedAt`), newest first. |
406
+ | `open_saved_image_document` | Replace the image document with a saved one by `key`; its assets are read from storage first. Ask before opening over changes the user has not saved. |
407
+
416
408
  ## Pitfalls
417
409
 
418
410
  - Agent playback (if on) opens each write's panel, then traces `points`, drags boxes or visits the layers and sliders named; results are unchanged.
@@ -0,0 +1,28 @@
1
+ # Smart objects
2
+
3
+ How to work with smart objects in the image editor (see the main skill for the tool reference).
4
+
5
+ 1. **Convert before repeated or uncertain changes**: `convert_to_smart_object` (one pixel layer, or
6
+ several siblings that nest as a layer document). A converted pixel layer looks exactly the same
7
+ and keeps its id; its filters become smart filters. From then on every transform and filter is
8
+ re-rendered from the contents, so scaling down and back up, or rotating twelve times, loses
9
+ nothing.
10
+ 2. **Read it**: `get_smart_object` gives the contents (size, kind, instances), the placement
11
+ (corners, warp, and scale / rotate in percent and degrees of the contents' own size) and the
12
+ smart filters with their ids.
13
+ 3. **Transform**: `set_smart_object_transform` sets the transform absolutely (scale 100 = the
14
+ contents' own size, `x`, `y` = where its centre goes; or `corners`, `matrix`, and a `warp` in
15
+ contents pixels, `null` to remove). `transform_layer`, `warp_layer`, `flip_layer` and
16
+ `rotate_layer_90` also work on a smart object, relative to its current placement, and never
17
+ degrade it. `offset_layer` moves it by whole pixels.
18
+ 4. **Filter non-destructively**: `add_smart_filter` (`develop` or `blur`, each with its own
19
+ `blend` and `opacity`; kinds can repeat), then `update_smart_filter`, `move_smart_filter`,
20
+ `remove_smart_filter` and `set_smart_filters_enabled`. `set_smart_filter_mask` limits where the
21
+ filters show; paint it with `paint_stroke` / `fill_pixels` `channel: "filter-mask"`.
22
+ 5. **Contents**: `copy_smart_object` with `linked: true` shares the contents (edit once, both
23
+ change), `linked: false` makes them independent. `edit_smart_object_contents` opens the
24
+ contents as their own document: every tool then works inside them until
25
+ `close_smart_object_contents` (`save: false` discards). `replace_smart_object_contents` swaps
26
+ in a new image for every linked instance; `export_smart_object_contents` returns the file.
27
+ 6. **Finish**: `rasterize_smart_object` only when the user wants plain pixels to paint on; the
28
+ placement and filters are then gone.
@@ -85,7 +85,7 @@ is never part of a layer. Coordinates are document pixels, (0, 0) top-left, y do
85
85
  9. **Watercolour** is its own way of working (see "Wet media" below): wet the paper or not,
86
86
  lay washes light to dark, let them flow (`advance_wet_paint`), dry them (`dry_paint`), glaze.
87
87
  10. **Deliver**: `export_image` (PNG, or JPEG / WebP in a browser), `export_psd` for Photoshop,
88
- `save_document` to keep the painting with its strokes.
88
+ `save_paint_document` to keep the painting with its strokes (when the host has storage).
89
89
 
90
90
  ## Best practices
91
91
 
@@ -133,6 +133,13 @@ is never part of a layer. Coordinates are document pixels, (0, 0) top-left, y do
133
133
 
134
134
  PSD has none of them: `export_psd` writes the nearest standard mode (watercolor and shadow
135
135
  as Multiply, tint as Color, highlight as Screen) and warns; the merged image stays exact.
136
+ - **Grow the canvas instead of starting big**: when the picture needs more room, call
137
+ `extend_canvas` with the pixels to add on each side (nothing painted moves relative to
138
+ anything else). Adding on the `left` or `top` shifts every coordinate you hold (stroke points,
139
+ guide and stencil positions, layer bounds) by (+left, +top); re-read with `list_layers` or use
140
+ the returned `rect` (the new canvas in the old coordinates). Wet paint dries first, so finish
141
+ or dry a wash (`dry_paint`) before extending. `set_canvas` only crops or grows at the
142
+ bottom-right and never moves the picture.
136
143
  - **Save brushes you build** (`set_brush` then `save_brush`), and share them with
137
144
  `export_brushes` / `import_brushes`.
138
145
 
@@ -264,9 +271,10 @@ for the controls and presets.
264
271
 
265
272
  | Tool | Use |
266
273
  | --- | --- |
267
- | `get_canvas_info` | Read the canvas size and dpi, paper (preset, colour, depth), active layer, layer count, active brush id, colours, view, hand, number of recorded strokes, undo / redo labels and the blend modes. Call first. |
274
+ | `get_canvas_info` | Read the canvas size and dpi, its `origin` (moved by `extend_canvas`), paper (preset, colour, depth), active layer, layer count, active brush id, colours, view, hand, number of recorded strokes, undo / redo labels and the blend modes. Call first. |
268
275
  | `new_canvas` | Start over with a blank canvas: one empty layer over the paper. `width`, `height` (1..16384 px), `dpi`, `paper` preset id; missing ones keep the current values. Undo brings the old painting back. Returns the size, paper and layer id. |
269
276
  | `set_canvas` | Resize the canvas (pixels stay put, anchored top-left) or change its `dpi`. Returns the canvas. |
277
+ | `extend_canvas` | Add pixels on any side (`left`, `top`, `right`, `bottom`, default 0) keeping everything painted, the paper grain and the guides in place relative to each other. `left` / `top` move all document coordinates by that much. Wet paint dries first; one undo step. Returns `canvas`, `rect` (the new canvas in the previous coordinates) and `origin` (also in `get_canvas_info`). |
270
278
  | `list_papers` | Read the paper library: id, name, family, grain pattern, colour, depth and the wet-media properties (absorption, permeability, roughness, stored for later wet media). Call before `set_paper`. |
271
279
  | `get_paper` | Read every setting of the current paper (pattern, seed, scale, tiling, depth, shading, dry and wet tuning). |
272
280
  | `set_paper` | Change the paper: `preset`, then any of `color`, `depth` (0..1), `scale` (0.25..8), `seed` (another sheet), `pattern`, `tiling`, `shading`, `dry` / `wet` tuning (`contrast` 0..3, `invert`). Affects new strokes' grain and how the paper shows; strokes already painted keep their pixels. Returns the paper. |
@@ -412,8 +420,20 @@ for the controls and presets.
412
420
  | `export_psd` | Export a PSD with every layer, the paper as a locked bottom layer, and the composite. Pigment and painterly modes are written as their nearest standard mode (`list_blend_modes` `psd`). Returns base64, size, `psb` and `warnings`. |
413
421
  | `export_time_lapse` | Repaint the painting from its recorded strokes and events as a time-lapse: `frames` (about how many), `fps`, `maxSide`, `wetStepsPerFrame` (frames while wet paint flows), `holdMs`. Returns a contact sheet image, the frame count, size, duration, a video project of the frames and the ffmpeg command that renders `timelapse.mp4`; `includeFrames` adds every frame as base64 PNG. Changes nothing. |
414
422
  | `export_high_res` | Repaint the painting from its strokes at `scale` 1..8 (brushes, grain, fills and wet events scaled): a PNG with real detail instead of upscaled pixels. Returns base64, size. Layers not painted with strokes come out empty. |
415
- | `save_document` | Get the document JSON (canvas, paper, layers as PNG data URLs, strokes unless `strokes: false`). |
416
- | `open_document` | Open a saved document JSON, replacing the painting and clearing the undo history. Returns the canvas, layers and any `problems`. |
423
+ | `save_document` | Deprecated, kept for one minor version: use `get_paint_document` or `save_paint_document`. Returns the document JSON with PNG data URLs inline (large). |
424
+ | `open_document` | Deprecated, kept for one minor version: use `load_paint_document` or `open_saved_paint_document`. Opens a document JSON from `save_document`, clearing the undo history. |
425
+
426
+ ### Save and open
427
+
428
+ The host may add the shared document tools (see `pixerate-agent-tools`). A paint document is the saved painting (canvas, paper, layers, thick paint, guides, swatches, mixing pad and recorded strokes) with every image as an asset reference; wet paint is saved dry.
429
+
430
+ | Tool | Use |
431
+ | --- | --- |
432
+ | `get_paint_document` | Take a copy of the whole painting as `{ kind, formatVersion, revision, document }` before a risky change, or to hand it on. Images in it are asset references (`pixerate-asset:...`), never pixel data, so it stays small. To look at the pixels use `render_preview` or `export_image`. |
433
+ | `load_paint_document` | Put back a copy from `get_paint_document` in this session (its `document`, or the whole result). Checked first: a document whose assets are not loaded, or from a newer build, is refused and nothing changes. Opening clears the undo history (`undoable: false`), so get a copy first if the user may want the current painting back. Refused while a stroke or a wet-paint step is in progress. To change part of the painting use the layer and painting tools. |
434
+ | `save_paint_document` | Save the painting and its assets under a `name` in the host's storage (the same name replaces that copy). Returns the `key`. Only listed when the host has storage. Save when the user asks, not after every edit. |
435
+ | `list_saved_paint_documents` | List saved paintings (`key`, `name`, `savedAt`), newest first. |
436
+ | `open_saved_paint_document` | Replace the painting with a saved one by `key`; its assets are read from storage first. Ask before opening over changes the user has not saved. |
417
437
 
418
438
  ## Pitfalls
419
439
 
@@ -458,5 +478,5 @@ for the controls and presets.
458
478
  - **Effects are pixels in replays.** Effect paper, effect layers, effect fills and baked light
459
479
  need the provider when they are made; replays and time-lapses do not repeat them. Add them
460
480
  after the painting, or keep the effect layer and redraw it.
461
- - **Recorded strokes are kept in saved documents**; `save_document` with `strokes: false` when
462
- only the pixels matter (smaller JSON).
481
+ - **Recorded strokes are kept in saved documents** (`save_paint_document`, `get_paint_document`),
482
+ so a painting can be replayed and repainted at a higher resolution.
@@ -1,134 +0,0 @@
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; }
2
-
3
-
4
- var _chunkL5OWG2NAcjs = require('./chunk-L5OWG2NA.cjs');
5
-
6
-
7
- var _chunk2BSBFBOAcjs = require('./chunk-2BSBFBOA.cjs');
8
-
9
- // src/persistence/editorDocument.ts
10
- var EditorDocumentLoadError = class extends Error {
11
- /** The `LoadReport` when the input was a file that could not be migrated. */
12
-
13
- constructor(message, report) {
14
- super(message);
15
- this.name = "EditorDocumentLoadError";
16
- this.report = report;
17
- }
18
- };
19
- var EDITOR_DOCUMENT_FORMAT = "pixerate.editor-document";
20
- var KIND = /^[a-z][a-z0-9_]*$/;
21
- function isEditorDocumentFile(value) {
22
- if (!value || typeof value !== "object" || Array.isArray(value)) return false;
23
- const v = value;
24
- return v.format === EDITOR_DOCUMENT_FORMAT && typeof v.kind === "string" && typeof v.formatVersion === "number" && "document" in v;
25
- }
26
- function toEditorDocumentFile(doc, name) {
27
- return {
28
- format: EDITOR_DOCUMENT_FORMAT,
29
- kind: doc.kind,
30
- formatVersion: doc.formatVersion,
31
- ...name ? { name } : {},
32
- document: doc.getDocument()
33
- };
34
- }
35
- function serializeEditorDocument(doc, options = {}) {
36
- return JSON.stringify(toEditorDocumentFile(doc, options.name), null, options.space);
37
- }
38
- function parseEditorDocument(text, options = {}) {
39
- let value;
40
- try {
41
- value = _chunk2BSBFBOAcjs.stripPrototypeKeys.call(void 0, JSON.parse(text));
42
- } catch (error) {
43
- throw new EditorDocumentLoadError(`The file is not valid JSON: ${error instanceof Error ? error.message : String(error)}`);
44
- }
45
- if (!isEditorDocumentFile(value)) {
46
- throw new EditorDocumentLoadError(`The file is not a saved editor document (expected "format": "${EDITOR_DOCUMENT_FORMAT}").`);
47
- }
48
- if (options.kind && value.kind !== options.kind) {
49
- throw new EditorDocumentLoadError(`The file is a ${value.kind} document, not a ${options.kind} document.`);
50
- }
51
- return value;
52
- }
53
- function defineEditorDocument(spec) {
54
- if (!KIND.test(spec.kind)) throw new Error(`Editor document kind "${spec.kind}" must match ${KIND}.`);
55
- const formatVersion = _nullishCoalesce(spec.formatVersion, () => ( 1));
56
- const listeners = /* @__PURE__ */ new Set();
57
- let revision = 0;
58
- let unsubscribe = null;
59
- let connected = false;
60
- const connect = () => {
61
- if (unsubscribe) return;
62
- if (connected) revision++;
63
- connected = true;
64
- unsubscribe = spec.subscribe(() => {
65
- revision++;
66
- listeners.forEach((listener) => {
67
- try {
68
- listener();
69
- } catch (error) {
70
- console.error(`[${spec.kind} document] Error in change listener:`, error);
71
- }
72
- });
73
- });
74
- };
75
- connect();
76
- const unwrap = (input) => {
77
- if (!isEditorDocumentFile(input)) return { document: input, report: _chunkL5OWG2NAcjs.createLoadReport.call(void 0, { version: formatVersion }) };
78
- if (input.kind !== spec.kind) throw new EditorDocumentLoadError(`This is a ${input.kind} document; it cannot be opened in the ${spec.kind} editor.`);
79
- const steps = Object.fromEntries(
80
- Object.entries(_nullishCoalesce(spec.migrations, () => ( {}))).map(([v, step]) => [v, (w) => ({ ...w, document: step(w.document) })])
81
- );
82
- const { doc, report } = _chunkL5OWG2NAcjs.migrateDocument.call(void 0,
83
- { formatVersion: input.formatVersion, document: input.document },
84
- { currentVersion: formatVersion, versionKey: "formatVersion", steps }
85
- );
86
- if (report.readOnly) throw new EditorDocumentLoadError(`Cannot open the ${spec.kind} document: ${report.readOnly.message}.`, report);
87
- return { document: doc.document, report };
88
- };
89
- return {
90
- kind: spec.kind,
91
- formatVersion,
92
- recordsLoads: spec.recordsLoads !== false,
93
- getDocument: () => spec.get(),
94
- loadDocument(input, options = {}) {
95
- const { document, report } = unwrap(input);
96
- if (document === void 0 || document === null) throw new EditorDocumentLoadError(`No ${spec.kind} document was given.`);
97
- try {
98
- const result = spec.load(document, { history: _nullishCoalesce(options.history, () => ( "reset")) });
99
- if (_optionalChain([result, 'optionalAccess', _ => _.problems, 'optionalAccess', _2 => _2.length])) report.problems.push(...result.problems);
100
- } catch (error) {
101
- if (error instanceof EditorDocumentLoadError) throw error;
102
- throw new EditorDocumentLoadError(error instanceof Error ? error.message : String(error));
103
- }
104
- return report;
105
- },
106
- revision: () => {
107
- connect();
108
- return revision;
109
- },
110
- subscribe(listener) {
111
- connect();
112
- listeners.add(listener);
113
- return () => {
114
- listeners.delete(listener);
115
- };
116
- },
117
- history: spec.history,
118
- readOnly: () => _nullishCoalesce(_optionalChain([spec, 'access', _3 => _3.readOnly, 'optionalCall', _4 => _4()]), () => ( false)),
119
- destroy() {
120
- _optionalChain([unsubscribe, 'optionalCall', _5 => _5()]);
121
- unsubscribe = null;
122
- }
123
- };
124
- }
125
-
126
-
127
-
128
-
129
-
130
-
131
-
132
-
133
-
134
- exports.EditorDocumentLoadError = EditorDocumentLoadError; exports.EDITOR_DOCUMENT_FORMAT = EDITOR_DOCUMENT_FORMAT; exports.isEditorDocumentFile = isEditorDocumentFile; exports.toEditorDocumentFile = toEditorDocumentFile; exports.serializeEditorDocument = serializeEditorDocument; exports.parseEditorDocument = parseEditorDocument; exports.defineEditorDocument = defineEditorDocument;
@@ -1,134 +0,0 @@
1
- import {
2
- createLoadReport,
3
- migrateDocument
4
- } from "./chunk-WHKNYFM2.js";
5
- import {
6
- stripPrototypeKeys
7
- } from "./chunk-HF5QZS7Z.js";
8
-
9
- // src/persistence/editorDocument.ts
10
- var EditorDocumentLoadError = class extends Error {
11
- /** The `LoadReport` when the input was a file that could not be migrated. */
12
- report;
13
- constructor(message, report) {
14
- super(message);
15
- this.name = "EditorDocumentLoadError";
16
- this.report = report;
17
- }
18
- };
19
- var EDITOR_DOCUMENT_FORMAT = "pixerate.editor-document";
20
- var KIND = /^[a-z][a-z0-9_]*$/;
21
- function isEditorDocumentFile(value) {
22
- if (!value || typeof value !== "object" || Array.isArray(value)) return false;
23
- const v = value;
24
- return v.format === EDITOR_DOCUMENT_FORMAT && typeof v.kind === "string" && typeof v.formatVersion === "number" && "document" in v;
25
- }
26
- function toEditorDocumentFile(doc, name) {
27
- return {
28
- format: EDITOR_DOCUMENT_FORMAT,
29
- kind: doc.kind,
30
- formatVersion: doc.formatVersion,
31
- ...name ? { name } : {},
32
- document: doc.getDocument()
33
- };
34
- }
35
- function serializeEditorDocument(doc, options = {}) {
36
- return JSON.stringify(toEditorDocumentFile(doc, options.name), null, options.space);
37
- }
38
- function parseEditorDocument(text, options = {}) {
39
- let value;
40
- try {
41
- value = stripPrototypeKeys(JSON.parse(text));
42
- } catch (error) {
43
- throw new EditorDocumentLoadError(`The file is not valid JSON: ${error instanceof Error ? error.message : String(error)}`);
44
- }
45
- if (!isEditorDocumentFile(value)) {
46
- throw new EditorDocumentLoadError(`The file is not a saved editor document (expected "format": "${EDITOR_DOCUMENT_FORMAT}").`);
47
- }
48
- if (options.kind && value.kind !== options.kind) {
49
- throw new EditorDocumentLoadError(`The file is a ${value.kind} document, not a ${options.kind} document.`);
50
- }
51
- return value;
52
- }
53
- function defineEditorDocument(spec) {
54
- if (!KIND.test(spec.kind)) throw new Error(`Editor document kind "${spec.kind}" must match ${KIND}.`);
55
- const formatVersion = spec.formatVersion ?? 1;
56
- const listeners = /* @__PURE__ */ new Set();
57
- let revision = 0;
58
- let unsubscribe = null;
59
- let connected = false;
60
- const connect = () => {
61
- if (unsubscribe) return;
62
- if (connected) revision++;
63
- connected = true;
64
- unsubscribe = spec.subscribe(() => {
65
- revision++;
66
- listeners.forEach((listener) => {
67
- try {
68
- listener();
69
- } catch (error) {
70
- console.error(`[${spec.kind} document] Error in change listener:`, error);
71
- }
72
- });
73
- });
74
- };
75
- connect();
76
- const unwrap = (input) => {
77
- if (!isEditorDocumentFile(input)) return { document: input, report: createLoadReport({ version: formatVersion }) };
78
- if (input.kind !== spec.kind) throw new EditorDocumentLoadError(`This is a ${input.kind} document; it cannot be opened in the ${spec.kind} editor.`);
79
- const steps = Object.fromEntries(
80
- Object.entries(spec.migrations ?? {}).map(([v, step]) => [v, (w) => ({ ...w, document: step(w.document) })])
81
- );
82
- const { doc, report } = migrateDocument(
83
- { formatVersion: input.formatVersion, document: input.document },
84
- { currentVersion: formatVersion, versionKey: "formatVersion", steps }
85
- );
86
- if (report.readOnly) throw new EditorDocumentLoadError(`Cannot open the ${spec.kind} document: ${report.readOnly.message}.`, report);
87
- return { document: doc.document, report };
88
- };
89
- return {
90
- kind: spec.kind,
91
- formatVersion,
92
- recordsLoads: spec.recordsLoads !== false,
93
- getDocument: () => spec.get(),
94
- loadDocument(input, options = {}) {
95
- const { document, report } = unwrap(input);
96
- if (document === void 0 || document === null) throw new EditorDocumentLoadError(`No ${spec.kind} document was given.`);
97
- try {
98
- const result = spec.load(document, { history: options.history ?? "reset" });
99
- if (result?.problems?.length) report.problems.push(...result.problems);
100
- } catch (error) {
101
- if (error instanceof EditorDocumentLoadError) throw error;
102
- throw new EditorDocumentLoadError(error instanceof Error ? error.message : String(error));
103
- }
104
- return report;
105
- },
106
- revision: () => {
107
- connect();
108
- return revision;
109
- },
110
- subscribe(listener) {
111
- connect();
112
- listeners.add(listener);
113
- return () => {
114
- listeners.delete(listener);
115
- };
116
- },
117
- history: spec.history,
118
- readOnly: () => spec.readOnly?.() ?? false,
119
- destroy() {
120
- unsubscribe?.();
121
- unsubscribe = null;
122
- }
123
- };
124
- }
125
-
126
- export {
127
- EditorDocumentLoadError,
128
- EDITOR_DOCUMENT_FORMAT,
129
- isEditorDocumentFile,
130
- toEditorDocumentFile,
131
- serializeEditorDocument,
132
- parseEditorDocument,
133
- defineEditorDocument
134
- };
@@ -1,135 +0,0 @@
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; }
2
-
3
-
4
- var _chunk2BSBFBOAcjs = require('./chunk-2BSBFBOA.cjs');
5
-
6
- // src/persistence/migrate.ts
7
- function hasLoadProblems(report) {
8
- return report.problems.length > 0 || report.readOnly !== null;
9
- }
10
- function createLoadReport(partial = {}) {
11
- return {
12
- migratedFrom: null,
13
- version: null,
14
- problems: [],
15
- readOnly: null,
16
- unknownKeys: [],
17
- ...partial
18
- };
19
- }
20
- var isObject = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
21
- var deepClone = (value) => typeof structuredClone === "function" ? structuredClone(value) : JSON.parse(JSON.stringify(value));
22
- function toObject(json) {
23
- let value = json;
24
- if (typeof json === "string") {
25
- try {
26
- value = JSON.parse(json);
27
- } catch (e) {
28
- return void 0;
29
- }
30
- }
31
- return isObject(value) ? _chunk2BSBFBOAcjs.stripPrototypeKeys.call(void 0, deepClone(value)) : void 0;
32
- }
33
- function migrateDocument(json, options) {
34
- const versionKey = _nullishCoalesce(options.versionKey, () => ( "version"));
35
- const target = options.currentVersion;
36
- const original = toObject(json);
37
- if (!original) {
38
- return {
39
- doc: { [versionKey]: target },
40
- report: createLoadReport({
41
- readOnly: { kind: "invalid", message: "document is not a JSON object" }
42
- })
43
- };
44
- }
45
- const raw = original[versionKey];
46
- const found = typeof raw === "number" && Number.isInteger(raw) && raw >= 0 ? raw : null;
47
- const from = _nullishCoalesce(_nullishCoalesce(found, () => ( options.missingVersion)), () => ( target));
48
- const report = createLoadReport({ version: found });
49
- if (from > target) {
50
- report.readOnly = {
51
- kind: "newerVersion",
52
- version: from,
53
- supported: target,
54
- message: `document has version ${from}, newer than the supported ${target}`
55
- };
56
- return { doc: original, report };
57
- }
58
- if (from === target) {
59
- original[versionKey] = target;
60
- return { doc: original, report };
61
- }
62
- let doc = deepClone(original);
63
- for (let step = from; step < target; step++) {
64
- const fn = _optionalChain([options, 'access', _ => _.steps, 'optionalAccess', _2 => _2[step]]);
65
- if (!fn) {
66
- report.readOnly = {
67
- kind: "migrationFailed",
68
- from: step,
69
- message: `no migration step from version ${step}`
70
- };
71
- return { doc: original, report };
72
- }
73
- try {
74
- doc = _nullishCoalesce(fn(doc), () => ( doc));
75
- } catch (error) {
76
- report.readOnly = {
77
- kind: "migrationFailed",
78
- from: step,
79
- message: `migration from version ${step} failed: ${error instanceof Error ? error.message : String(error)}`
80
- };
81
- return { doc: original, report };
82
- }
83
- doc[versionKey] = step + 1;
84
- }
85
- report.migratedFrom = from;
86
- return { doc, report };
87
- }
88
- function loadSections(doc, sections, options = {}) {
89
- const source = isObject(doc) ? doc : {};
90
- const ignore = new Set(_nullishCoalesce(options.ignore, () => ( ["version"])));
91
- const value = {};
92
- const problems = [];
93
- for (const key of Object.keys(sections)) {
94
- const spec = sections[key];
95
- const fallback = () => typeof spec.defaults === "function" ? spec.defaults() : spec.defaults;
96
- if (!Object.prototype.hasOwnProperty.call(source, key)) {
97
- value[key] = fallback();
98
- continue;
99
- }
100
- try {
101
- const parsed = spec.parse(source[key]);
102
- if (parsed === void 0) throw new Error("invalid value");
103
- value[key] = parsed;
104
- } catch (error) {
105
- problems.push({
106
- section: key,
107
- message: error instanceof Error ? error.message : String(error)
108
- });
109
- value[key] = fallback();
110
- }
111
- }
112
- const unknown = {};
113
- for (const key of Object.keys(source)) {
114
- if (!(key in sections) && !ignore.has(key) && !_chunk2BSBFBOAcjs.isUnsafeJsonKey.call(void 0, key)) unknown[key] = source[key];
115
- }
116
- return { value, problems, unknown };
117
- }
118
- function loadDocument(json, sections, options) {
119
- const { doc, report } = migrateDocument(json, options);
120
- const ignore = _nullishCoalesce(options.ignore, () => ( [_nullishCoalesce(options.versionKey, () => ( "version"))]));
121
- const result = loadSections(_optionalChain([report, 'access', _3 => _3.readOnly, 'optionalAccess', _4 => _4.kind]) === "invalid" ? {} : doc, sections, {
122
- ignore
123
- });
124
- report.problems = result.problems;
125
- report.unknownKeys = Object.keys(result.unknown);
126
- return { ...result, report };
127
- }
128
-
129
-
130
-
131
-
132
-
133
-
134
-
135
- exports.hasLoadProblems = hasLoadProblems; exports.createLoadReport = createLoadReport; exports.migrateDocument = migrateDocument; exports.loadSections = loadSections; exports.loadDocument = loadDocument;