@pixerate/editor-svelte 0.22.0 → 0.23.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 (85) hide show
  1. package/README.md +4 -0
  2. package/dist/MergePreview.svelte +201 -0
  3. package/dist/MergePreview.svelte.d.ts +25 -0
  4. package/dist/MergePreview.svelte.d.ts.map +1 -0
  5. package/dist/TemplateRenderer.svelte +4 -10
  6. package/dist/TemplateRenderer.svelte.d.ts.map +1 -1
  7. package/dist/canvas/createCanvasArrange.svelte.d.ts.map +1 -1
  8. package/dist/canvas/createCanvasAutoLayout.svelte.d.ts +68 -0
  9. package/dist/canvas/createCanvasAutoLayout.svelte.d.ts.map +1 -0
  10. package/dist/canvas/createCanvasAutoLayout.svelte.js +78 -0
  11. package/dist/canvas/index.d.ts +2 -0
  12. package/dist/canvas/index.d.ts.map +1 -1
  13. package/dist/canvas/index.js +3 -0
  14. package/dist/find-replace/FindReplaceBar.svelte +203 -0
  15. package/dist/find-replace/FindReplaceBar.svelte.d.ts +15 -0
  16. package/dist/find-replace/FindReplaceBar.svelte.d.ts.map +1 -0
  17. package/dist/find-replace/findReplace.svelte.d.ts +27 -0
  18. package/dist/find-replace/findReplace.svelte.d.ts.map +1 -0
  19. package/dist/find-replace/findReplace.svelte.js +79 -0
  20. package/dist/find-replace/index.d.ts +6 -0
  21. package/dist/find-replace/index.d.ts.map +1 -0
  22. package/dist/find-replace/index.js +2 -0
  23. package/dist/generation/GenerationSettings.svelte +174 -0
  24. package/dist/generation/GenerationSettings.svelte.d.ts +23 -0
  25. package/dist/generation/GenerationSettings.svelte.d.ts.map +1 -0
  26. package/dist/generation/JobProgress.svelte +53 -0
  27. package/dist/generation/JobProgress.svelte.d.ts +17 -0
  28. package/dist/generation/JobProgress.svelte.d.ts.map +1 -0
  29. package/dist/generation/index.d.ts +10 -0
  30. package/dist/generation/index.d.ts.map +1 -0
  31. package/dist/generation/index.js +4 -0
  32. package/dist/generation/plan.svelte.d.ts +16 -0
  33. package/dist/generation/plan.svelte.d.ts.map +1 -0
  34. package/dist/generation/plan.svelte.js +26 -0
  35. package/dist/generation/progress.svelte.d.ts +25 -0
  36. package/dist/generation/progress.svelte.d.ts.map +1 -0
  37. package/dist/generation/progress.svelte.js +36 -0
  38. package/dist/image-editor/AdjustmentLayersPanel.svelte +309 -0
  39. package/dist/image-editor/AdjustmentLayersPanel.svelte.d.ts +11 -0
  40. package/dist/image-editor/AdjustmentLayersPanel.svelte.d.ts.map +1 -0
  41. package/dist/image-editor/ControlSlider.svelte +39 -0
  42. package/dist/image-editor/ControlSlider.svelte.d.ts +13 -0
  43. package/dist/image-editor/ControlSlider.svelte.d.ts.map +1 -0
  44. package/dist/image-editor/CurvesEditor.svelte +105 -0
  45. package/dist/image-editor/CurvesEditor.svelte.d.ts +13 -0
  46. package/dist/image-editor/CurvesEditor.svelte.d.ts.map +1 -0
  47. package/dist/image-editor/Histogram.svelte +49 -0
  48. package/dist/image-editor/Histogram.svelte.d.ts +12 -0
  49. package/dist/image-editor/Histogram.svelte.d.ts.map +1 -0
  50. package/dist/image-editor/ImageEditor.svelte +32 -140
  51. package/dist/image-editor/ImageEditor.svelte.d.ts.map +1 -1
  52. package/dist/image-editor/imageEditorState.svelte.d.ts +14 -1
  53. package/dist/image-editor/imageEditorState.svelte.d.ts.map +1 -1
  54. package/dist/image-editor/imageEditorState.svelte.js +40 -0
  55. package/dist/image-editor/index.d.ts +12 -0
  56. package/dist/image-editor/index.d.ts.map +1 -1
  57. package/dist/image-editor/index.js +4 -0
  58. package/dist/index.d.ts +8 -1
  59. package/dist/index.d.ts.map +1 -1
  60. package/dist/index.js +5 -0
  61. package/dist/merge.svelte.d.ts +51 -0
  62. package/dist/merge.svelte.d.ts.map +1 -0
  63. package/dist/merge.svelte.js +117 -0
  64. package/dist/persistence/RecoveryPrompt.svelte +57 -0
  65. package/dist/persistence/RecoveryPrompt.svelte.d.ts +18 -0
  66. package/dist/persistence/RecoveryPrompt.svelte.d.ts.map +1 -0
  67. package/dist/persistence/autosave.svelte.d.ts +63 -0
  68. package/dist/persistence/autosave.svelte.d.ts.map +1 -0
  69. package/dist/persistence/autosave.svelte.js +110 -0
  70. package/dist/persistence/index.d.ts +6 -0
  71. package/dist/persistence/index.d.ts.map +1 -0
  72. package/dist/persistence/index.js +2 -0
  73. package/dist/review/documentCompare.svelte.d.ts +21 -0
  74. package/dist/review/documentCompare.svelte.d.ts.map +1 -0
  75. package/dist/review/documentCompare.svelte.js +23 -0
  76. package/dist/review/index.d.ts +1 -0
  77. package/dist/review/index.d.ts.map +1 -1
  78. package/dist/review/index.js +1 -0
  79. package/dist/spreadsheet/SpreadsheetContextMenu.svelte +6 -3
  80. package/dist/spreadsheet/SpreadsheetContextMenu.svelte.d.ts.map +1 -1
  81. package/dist/spreadsheet/SpreadsheetEditor.svelte +6 -2
  82. package/dist/spreadsheet/SpreadsheetEditor.svelte.d.ts.map +1 -1
  83. package/dist/spreadsheet/spreadsheet.svelte.d.ts +2 -0
  84. package/dist/spreadsheet/spreadsheet.svelte.d.ts.map +1 -1
  85. package/package.json +18 -2
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Histogram.svelte.d.ts","sourceRoot":"","sources":["../../src/image-editor/Histogram.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AAIlE,UAAU,KAAK;IACb,SAAS,EAAE,cAAc,GAAG,IAAI,CAAC;IACjC,gGAAgG;IAChG,OAAO,CAAC,EAAE,KAAK,GAAG,WAAW,GAAG,KAAK,GAAG,OAAO,GAAG,MAAM,CAAC;IACzD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAsCH,QAAA,MAAM,SAAS,2CAAwC,CAAC;AACxD,KAAK,SAAS,GAAG,UAAU,CAAC,OAAO,SAAS,CAAC,CAAC;AAC9C,eAAe,SAAS,CAAC"}
@@ -18,7 +18,12 @@
18
18
  hitTestAnnotation,
19
19
  normalizeProcessVersion,
20
20
  LATEST_PROCESS_VERSION,
21
+ IMAGE_ADJUSTMENT_CONTROLS,
22
+ readAdjustmentControls,
21
23
  } from '@pixerate/editor/image-editor';
24
+ import ControlSlider from './ControlSlider.svelte';
25
+ import Histogram from './Histogram.svelte';
26
+ import AdjustmentLayersPanel from './AdjustmentLayersPanel.svelte';
22
27
  import { onDestroy, untrack } from 'svelte';
23
28
  import { createReactiveImageEditor, type ReactiveImageEditor } from './imageEditorState.svelte.js';
24
29
 
@@ -886,150 +891,37 @@
886
891
  </button>
887
892
  </div>
888
893
 
889
- <!-- Brightness -->
890
- <div>
891
- <div class="flex justify-between text-slate-400 mb-1">
892
- <span>Brightness</span>
893
- <span class="font-mono">{editor.state.adjustments.brightness}</span>
894
- </div>
895
- <input
896
- type="range"
897
- min="-100"
898
- max="100"
899
- value={editor.state.adjustments.brightness}
900
- oninput={(e) => editor.setAdjustments({ brightness: Number((e.target as HTMLInputElement).value) })}
901
- class="w-full accent-indigo-500"
902
- />
903
- </div>
904
-
905
- <!-- Contrast -->
906
- <div>
907
- <div class="flex justify-between text-slate-400 mb-1">
908
- <span>Contrast</span>
909
- <span class="font-mono">{editor.state.adjustments.contrast}</span>
910
- </div>
911
- <input
912
- type="range"
913
- min="-100"
914
- max="100"
915
- value={editor.state.adjustments.contrast}
916
- oninput={(e) => editor.setAdjustments({ contrast: Number((e.target as HTMLInputElement).value) })}
917
- class="w-full accent-indigo-500"
918
- />
919
- </div>
920
-
921
- <!-- Saturation -->
922
- <div>
923
- <div class="flex justify-between text-slate-400 mb-1">
924
- <span>Saturation</span>
925
- <span class="font-mono">{editor.state.adjustments.saturation}</span>
926
- </div>
927
- <input
928
- type="range"
929
- min="-100"
930
- max="100"
931
- value={editor.state.adjustments.saturation}
932
- oninput={(e) => editor.setAdjustments({ saturation: Number((e.target as HTMLInputElement).value) })}
933
- class="w-full accent-indigo-500"
934
- />
935
- </div>
936
-
937
- <!-- Exposure -->
938
- <div>
939
- <div class="flex justify-between text-slate-400 mb-1">
940
- <span>Exposure</span>
941
- <span class="font-mono">{editor.state.adjustments.exposure}</span>
942
- </div>
943
- <input
944
- type="range"
945
- min="-100"
946
- max="100"
947
- value={editor.state.adjustments.exposure}
948
- oninput={(e) => editor.setAdjustments({ exposure: Number((e.target as HTMLInputElement).value) })}
949
- class="w-full accent-indigo-500"
950
- />
951
- </div>
894
+ <Histogram histogram={editor.histogram} />
952
895
 
953
- <!-- Temperature / Warmth -->
954
- <div>
955
- <div class="flex justify-between text-slate-400 mb-1">
956
- <span>Warmth / Temperature</span>
957
- <span class="font-mono">{editor.state.adjustments.temperature}</span>
958
- </div>
959
- <input
960
- type="range"
961
- min="-100"
962
- max="100"
963
- value={editor.state.adjustments.temperature}
964
- oninput={(e) => editor.setAdjustments({ temperature: Number((e.target as HTMLInputElement).value) })}
965
- class="w-full accent-indigo-500"
896
+ {#each IMAGE_ADJUSTMENT_CONTROLS as spec (spec.id)}
897
+ <ControlSlider
898
+ {spec}
899
+ value={readAdjustmentControls(editor.state.adjustments)[spec.id]}
900
+ disabled={spec.requiresProcess !== undefined && legacyProcess}
901
+ onchange={(v) => editor.setAdjustments({ [spec.id]: v })}
966
902
  />
967
- </div>
968
-
969
- <!-- Tint (process 2 white balance) -->
970
- <div>
971
- <div class="flex justify-between text-slate-400 mb-1">
972
- <span>Tint</span>
973
- <span class="font-mono">{editor.state.adjustments.tint ?? 0}</span>
974
- </div>
975
- <input
976
- type="range"
977
- min="-100"
978
- max="100"
979
- aria-label="Tint"
980
- disabled={legacyProcess}
981
- value={editor.state.adjustments.tint ?? 0}
982
- oninput={(e) => editor.setAdjustments({ tint: Number((e.target as HTMLInputElement).value) })}
983
- class="w-full accent-indigo-500 disabled:opacity-40"
984
- />
985
- </div>
986
-
987
- {#if legacyProcess}
988
- <div
989
- class="flex items-center justify-between gap-2 rounded border border-amber-500/30 bg-amber-500/10 px-2 py-1.5 text-[10px] text-amber-200"
990
- data-testid="image-editor-process-notice"
991
- >
992
- <span>Legacy rendering (process 1). Tint needs the current process.</span>
993
- <button
994
- type="button"
995
- onclick={() => editor.upgradeProcess()}
996
- class="shrink-0 rounded bg-amber-500/20 px-2 py-0.5 hover:bg-amber-500/30"
903
+ {#if spec.id === 'tint' && legacyProcess}
904
+ <div
905
+ class="flex items-center justify-between gap-2 rounded border border-amber-500/30 bg-amber-500/10 px-2 py-1.5 text-[10px] text-amber-200"
906
+ data-testid="image-editor-process-notice"
997
907
  >
998
- Update
999
- </button>
1000
- </div>
1001
- {/if}
1002
-
1003
- <!-- Blur -->
1004
- <div>
1005
- <div class="flex justify-between text-slate-400 mb-1">
1006
- <span>Blur</span>
1007
- <span class="font-mono">{editor.state.adjustments.blur}px</span>
1008
- </div>
1009
- <input
1010
- type="range"
1011
- min="0"
1012
- max="30"
1013
- value={editor.state.adjustments.blur}
1014
- oninput={(e) => editor.setAdjustments({ blur: Number((e.target as HTMLInputElement).value) })}
1015
- class="w-full accent-indigo-500"
1016
- />
1017
- </div>
908
+ <span>Legacy rendering (process 1). Tint needs the current process.</span>
909
+ <button
910
+ type="button"
911
+ onclick={() => editor.upgradeProcess()}
912
+ class="shrink-0 rounded bg-amber-500/20 px-2 py-0.5 hover:bg-amber-500/30"
913
+ >
914
+ Update
915
+ </button>
916
+ </div>
917
+ {/if}
918
+ {/each}
1018
919
 
1019
- <!-- Opacity -->
1020
- <div>
1021
- <div class="flex justify-between text-slate-400 mb-1">
1022
- <span>Opacity</span>
1023
- <span class="font-mono">{Math.round(editor.state.adjustments.opacity * 100)}%</span>
1024
- </div>
1025
- <input
1026
- type="range"
1027
- min="0"
1028
- max="1"
1029
- step="0.05"
1030
- value={editor.state.adjustments.opacity}
1031
- oninput={(e) => editor.setAdjustments({ opacity: Number((e.target as HTMLInputElement).value) })}
1032
- class="w-full accent-indigo-500"
920
+ <div class="border-t border-slate-700/60 pt-3">
921
+ <AdjustmentLayersPanel
922
+ controller={editor.controller}
923
+ layers={editor.state.adjustments.layers}
924
+ histogram={editor.histogram}
1033
925
  />
1034
926
  </div>
1035
927
  </div>
@@ -1 +1 @@
1
- {"version":3,"file":"ImageEditor.svelte.d.ts","sourceRoot":"","sources":["../../src/image-editor/ImageEditor.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAIR,aAAa,EAGb,WAAW,EACX,oBAAoB,EACpB,kBAAkB,EACnB,MAAM,+BAA+B,CAAC;AAWzC,OAAO,EAA6B,KAAK,mBAAmB,EAAE,MAAM,8BAA8B,CAAC;AAGjG,UAAU,KAAK;IACb,MAAM,CAAC,EAAE,mBAAmB,CAAC;IAC7B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,SAAS,CAAC,EAAE,WAAW,CAAC;IACxB,kBAAkB,CAAC,EAAE,oBAAoB,CAAC;IAC1C,OAAO,CAAC,EAAE,kBAAkB,CAAC;CAC9B;AAojCH,QAAA,MAAM,WAAW,2CAAwC,CAAC;AAC1D,KAAK,WAAW,GAAG,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC;AAClD,eAAe,WAAW,CAAC"}
1
+ {"version":3,"file":"ImageEditor.svelte.d.ts","sourceRoot":"","sources":["../../src/image-editor/ImageEditor.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAIR,aAAa,EAGb,WAAW,EACX,oBAAoB,EACpB,kBAAkB,EACnB,MAAM,+BAA+B,CAAC;AAgBzC,OAAO,EAA6B,KAAK,mBAAmB,EAAE,MAAM,8BAA8B,CAAC;AAGjG,UAAU,KAAK;IACb,MAAM,CAAC,EAAE,mBAAmB,CAAC;IAC7B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,SAAS,CAAC,EAAE,WAAW,CAAC;IACxB,kBAAkB,CAAC,EAAE,oBAAoB,CAAC;IAC1C,OAAO,CAAC,EAAE,kBAAkB,CAAC;CAC9B;AAw/BH,QAAA,MAAM,WAAW,2CAAwC,CAAC;AAC1D,KAAK,WAAW,GAAG,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC;AAClD,eAAe,WAAW,CAAC"}
@@ -1,8 +1,10 @@
1
- import { ImageEditorController, type ImageEditorOptions, type ImageEditorState, type ImageEditorTool, type CropBox, type AspectRatio, type ImageAdjustments, type DepthMaskOptions, type Annotation, type CreateAnnotationPayload, type ExportOptions, type SerializedImageEditorState, type ImagePoint } from '@pixerate/editor/image-editor';
1
+ import { ImageEditorController, type ImageEditorOptions, type ImageEditorState, type ImageEditorTool, type CropBox, type AspectRatio, type ImageAdjustments, type DepthMaskOptions, type Annotation, type CreateAnnotationPayload, type ExportOptions, type SerializedImageEditorState, type ImagePoint, type AdjustmentKind, type AdjustmentLayerInit, type AdjustmentLayerUpdate, type ImageHistogram } from '@pixerate/editor/image-editor';
2
2
  export declare function createReactiveImageEditor(options?: ImageEditorOptions): {
3
3
  readonly controller: ImageEditorController;
4
4
  readonly state: ImageEditorState;
5
5
  readonly canUndo: boolean;
6
+ /** Histogram of the last rendered preview (after adjustments and layers); null before a render. */
7
+ readonly histogram: ImageHistogram | null;
6
8
  readonly canRedo: boolean;
7
9
  setTool(tool: ImageEditorTool): void;
8
10
  setCrop(crop: CropBox | null, addToHistory?: boolean): void;
@@ -16,6 +18,12 @@ export declare function createReactiveImageEditor(options?: ImageEditorOptions):
16
18
  resetAdjustments(): void;
17
19
  /** Update to Current Process: re-render the adjustments with the latest process (one undo step). */
18
20
  upgradeProcess(): boolean;
21
+ /** Adds an adjustment layer (one undo step) and returns its id. */
22
+ addAdjustmentLayer(init: AdjustmentKind | AdjustmentLayerInit, index?: number): string;
23
+ updateAdjustmentLayer(id: string, update: AdjustmentLayerUpdate, commitToHistory?: boolean): boolean;
24
+ removeAdjustmentLayer(id: string): boolean;
25
+ moveAdjustmentLayer(id: string, toIndex: number): boolean;
26
+ toggleAdjustmentLayer(id: string, enabled?: boolean): boolean;
19
27
  setDepthMask(partial: Partial<DepthMaskOptions>): void;
20
28
  resetDepthMask(): void;
21
29
  addAnnotation(ann: CreateAnnotationPayload): string;
@@ -52,4 +60,9 @@ export declare function createReactiveImageEditor(options?: ImageEditorOptions):
52
60
  destroy(): void;
53
61
  };
54
62
  export type ReactiveImageEditor = ReturnType<typeof createReactiveImageEditor>;
63
+ /** The preview histogram of an image editor controller as a rune (`current`), updated after every render. */
64
+ export declare function createImageHistogram(controller: ImageEditorController): {
65
+ readonly current: ImageHistogram | null;
66
+ destroy(): void;
67
+ };
55
68
  //# sourceMappingURL=imageEditorState.svelte.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"imageEditorState.svelte.d.ts","sourceRoot":"","sources":["../../src/image-editor/imageEditorState.svelte.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,qBAAqB,EACrB,KAAK,kBAAkB,EACvB,KAAK,gBAAgB,EACrB,KAAK,eAAe,EACpB,KAAK,OAAO,EACZ,KAAK,WAAW,EAChB,KAAK,gBAAgB,EACrB,KAAK,gBAAgB,EACrB,KAAK,UAAU,EACf,KAAK,uBAAuB,EAC5B,KAAK,aAAa,EAClB,KAAK,0BAA0B,EAC/B,KAAK,UAAU,EAChB,MAAM,+BAA+B,CAAC;AAEvC,wBAAgB,yBAAyB,CAAC,OAAO,GAAE,kBAAuB;;;;;kBA0BxD,eAAe;kBAGf,OAAO,GAAG,IAAI;2BAGL,WAAW;;oBAMlB,MAAM;yBAGD,MAAM;;;4BASH,OAAO,CAAC,gBAAgB,CAAC;;IAMjD,oGAAoG;;0BAI9E,OAAO,CAAC,gBAAgB,CAAC;;uBAM5B,uBAAuB;yBAGrB,MAAM,WAAW,OAAO,CAAC,UAAU,CAAC;yBAGpC,MAAM;;yBAMN,MAAM,GAAG,IAAI;;;;oBAYlB,MAAM;qBAGL,MAAM;;kBAMT,MAAM;gBAGR,UAAU;4BAGE,OAAO;mBAGhB,MAAM,GAAG,gBAAgB;qBAGvB,aAAa;kBAGhB,aAAa;;mBAMZ,0BAA0B;sBAGvB;QAAE,cAAc,CAAC,EAAE,OAAO,CAAC;QAAC,OAAO,CAAC,EAAE,OAAO,CAAA;KAAE;yBAG5C;QAAE,cAAc,CAAC,EAAE,OAAO,CAAC;QAAC,OAAO,CAAC,EAAE,OAAO,CAAA;KAAE;2BAG7C,IAAI,CAAC,aAAa,EAAE,QAAQ,GAAG,SAAS,CAAC;oCAGhC,MAAM;4CAGE,MAAM;IAG9C,8EAA8E;;EAKjF;AAED,MAAM,MAAM,mBAAmB,GAAG,UAAU,CAAC,OAAO,yBAAyB,CAAC,CAAC"}
1
+ {"version":3,"file":"imageEditorState.svelte.d.ts","sourceRoot":"","sources":["../../src/image-editor/imageEditorState.svelte.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,qBAAqB,EACrB,KAAK,kBAAkB,EACvB,KAAK,gBAAgB,EACrB,KAAK,eAAe,EACpB,KAAK,OAAO,EACZ,KAAK,WAAW,EAChB,KAAK,gBAAgB,EACrB,KAAK,gBAAgB,EACrB,KAAK,UAAU,EACf,KAAK,uBAAuB,EAC5B,KAAK,aAAa,EAClB,KAAK,0BAA0B,EAC/B,KAAK,UAAU,EACf,KAAK,cAAc,EACnB,KAAK,mBAAmB,EACxB,KAAK,qBAAqB,EAC1B,KAAK,cAAc,EACpB,MAAM,+BAA+B,CAAC;AAEvC,wBAAgB,yBAAyB,CAAC,OAAO,GAAE,kBAAuB;;;;IA2BtE,mGAAmG;;;kBAQrF,eAAe;kBAGf,OAAO,GAAG,IAAI;2BAGL,WAAW;;oBAMlB,MAAM;yBAGD,MAAM;;;4BASH,OAAO,CAAC,gBAAgB,CAAC;;IAMjD,oGAAoG;;IAIpG,mEAAmE;6BAC1C,cAAc,GAAG,mBAAmB,UAAU,MAAM;8BAGnD,MAAM,UAAU,qBAAqB;8BAGrC,MAAM;4BAGR,MAAM,WAAW,MAAM;8BAGrB,MAAM,YAAY,OAAO;0BAG7B,OAAO,CAAC,gBAAgB,CAAC;;uBAM5B,uBAAuB;yBAGrB,MAAM,WAAW,OAAO,CAAC,UAAU,CAAC;yBAGpC,MAAM;;yBAMN,MAAM,GAAG,IAAI;;;;oBAYlB,MAAM;qBAGL,MAAM;;kBAMT,MAAM;gBAGR,UAAU;4BAGE,OAAO;mBAGhB,MAAM,GAAG,gBAAgB;qBAGvB,aAAa;kBAGhB,aAAa;;mBAMZ,0BAA0B;sBAGvB;QAAE,cAAc,CAAC,EAAE,OAAO,CAAC;QAAC,OAAO,CAAC,EAAE,OAAO,CAAA;KAAE;yBAG5C;QAAE,cAAc,CAAC,EAAE,OAAO,CAAC;QAAC,OAAO,CAAC,EAAE,OAAO,CAAA;KAAE;2BAG7C,IAAI,CAAC,aAAa,EAAE,QAAQ,GAAG,SAAS,CAAC;oCAGhC,MAAM;4CAGE,MAAM;IAG9C,8EAA8E;;EAMjF;AAED,MAAM,MAAM,mBAAmB,GAAG,UAAU,CAAC,OAAO,yBAAyB,CAAC,CAAC;AAE/E,6GAA6G;AAC7G,wBAAgB,oBAAoB,CAAC,UAAU,EAAE,qBAAqB;;;EAarE"}
@@ -3,10 +3,14 @@ export function createReactiveImageEditor(options = {}) {
3
3
  const controller = new ImageEditorController(options);
4
4
  let state = $state(controller.getState());
5
5
  let historyTicks = $state(0);
6
+ let histogram = $state.raw(controller.getHistogram());
6
7
  const unsubscribe = controller.subscribe((updated) => {
7
8
  state = { ...updated };
8
9
  historyTicks++;
9
10
  });
11
+ const unsubscribeHistogram = controller.subscribeHistogram((h) => {
12
+ histogram = h;
13
+ });
10
14
  return {
11
15
  get controller() {
12
16
  return controller;
@@ -19,6 +23,10 @@ export function createReactiveImageEditor(options = {}) {
19
23
  void historyTicks;
20
24
  return controller.canUndo;
21
25
  },
26
+ /** Histogram of the last rendered preview (after adjustments and layers); null before a render. */
27
+ get histogram() {
28
+ return histogram;
29
+ },
22
30
  get canRedo() {
23
31
  void historyTicks;
24
32
  return controller.canRedo;
@@ -57,6 +65,22 @@ export function createReactiveImageEditor(options = {}) {
57
65
  upgradeProcess() {
58
66
  return controller.upgradeProcess();
59
67
  },
68
+ /** Adds an adjustment layer (one undo step) and returns its id. */
69
+ addAdjustmentLayer(init, index) {
70
+ return controller.addAdjustmentLayer(init, index);
71
+ },
72
+ updateAdjustmentLayer(id, update, commitToHistory = true) {
73
+ return controller.updateAdjustmentLayer(id, update, commitToHistory);
74
+ },
75
+ removeAdjustmentLayer(id) {
76
+ return controller.removeAdjustmentLayer(id);
77
+ },
78
+ moveAdjustmentLayer(id, toIndex) {
79
+ return controller.moveAdjustmentLayer(id, toIndex);
80
+ },
81
+ toggleAdjustmentLayer(id, enabled) {
82
+ return controller.toggleAdjustmentLayer(id, enabled);
83
+ },
60
84
  setDepthMask(partial) {
61
85
  controller.setDepthMask(partial);
62
86
  },
@@ -136,6 +160,22 @@ export function createReactiveImageEditor(options = {}) {
136
160
  return controller.applyBackgroundRemovedImage(newDataUrl);
137
161
  },
138
162
  /** Stops syncing controller state; call when the editor is no longer used. */
163
+ destroy() {
164
+ unsubscribe();
165
+ unsubscribeHistogram();
166
+ },
167
+ };
168
+ }
169
+ /** The preview histogram of an image editor controller as a rune (`current`), updated after every render. */
170
+ export function createImageHistogram(controller) {
171
+ let current = $state.raw(controller.getHistogram());
172
+ const unsubscribe = controller.subscribeHistogram((h) => {
173
+ current = h;
174
+ });
175
+ return {
176
+ get current() {
177
+ return current;
178
+ },
139
179
  destroy() {
140
180
  unsubscribe();
141
181
  },
@@ -1,6 +1,18 @@
1
1
  import type { ComponentProps } from "svelte";
2
2
  import type ImageEditorComponent from "./ImageEditor.svelte";
3
+ import type HistogramComponent from "./Histogram.svelte";
4
+ import type AdjustmentLayersPanelComponent from "./AdjustmentLayersPanel.svelte";
5
+ import type CurvesEditorComponent from "./CurvesEditor.svelte";
6
+ import type ControlSliderComponent from "./ControlSlider.svelte";
3
7
  export { default as ImageEditor } from './ImageEditor.svelte';
8
+ export { default as Histogram } from './Histogram.svelte';
9
+ export { default as AdjustmentLayersPanel } from './AdjustmentLayersPanel.svelte';
10
+ export { default as CurvesEditor } from './CurvesEditor.svelte';
11
+ export { default as ControlSlider } from './ControlSlider.svelte';
4
12
  export * from './imageEditorState.svelte.js';
5
13
  export type ImageEditorProps = ComponentProps<typeof ImageEditorComponent>;
14
+ export type HistogramProps = ComponentProps<typeof HistogramComponent>;
15
+ export type AdjustmentLayersPanelProps = ComponentProps<typeof AdjustmentLayersPanelComponent>;
16
+ export type CurvesEditorProps = ComponentProps<typeof CurvesEditorComponent>;
17
+ export type ControlSliderProps = ComponentProps<typeof ControlSliderComponent>;
6
18
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/image-editor/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,QAAQ,CAAC;AAC7C,OAAO,KAAK,oBAAoB,MAAM,sBAAsB,CAAC;AAE7D,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAC9D,cAAc,8BAA8B,CAAC;AAE7C,MAAM,MAAM,gBAAgB,GAAG,cAAc,CAAC,OAAO,oBAAoB,CAAC,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/image-editor/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,QAAQ,CAAC;AAC7C,OAAO,KAAK,oBAAoB,MAAM,sBAAsB,CAAC;AAC7D,OAAO,KAAK,kBAAkB,MAAM,oBAAoB,CAAC;AACzD,OAAO,KAAK,8BAA8B,MAAM,gCAAgC,CAAC;AACjF,OAAO,KAAK,qBAAqB,MAAM,uBAAuB,CAAC;AAC/D,OAAO,KAAK,sBAAsB,MAAM,wBAAwB,CAAC;AAEjE,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,EAAE,OAAO,IAAI,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,EAAE,OAAO,IAAI,qBAAqB,EAAE,MAAM,gCAAgC,CAAC;AAClF,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAChE,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAClE,cAAc,8BAA8B,CAAC;AAE7C,MAAM,MAAM,gBAAgB,GAAG,cAAc,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAC3E,MAAM,MAAM,cAAc,GAAG,cAAc,CAAC,OAAO,kBAAkB,CAAC,CAAC;AACvE,MAAM,MAAM,0BAA0B,GAAG,cAAc,CAAC,OAAO,8BAA8B,CAAC,CAAC;AAC/F,MAAM,MAAM,iBAAiB,GAAG,cAAc,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAC7E,MAAM,MAAM,kBAAkB,GAAG,cAAc,CAAC,OAAO,sBAAsB,CAAC,CAAC"}
@@ -1,2 +1,6 @@
1
1
  export { default as ImageEditor } from './ImageEditor.svelte';
2
+ export { default as Histogram } from './Histogram.svelte';
3
+ export { default as AdjustmentLayersPanel } from './AdjustmentLayersPanel.svelte';
4
+ export { default as CurvesEditor } from './CurvesEditor.svelte';
5
+ export { default as ControlSlider } from './ControlSlider.svelte';
2
6
  export * from './imageEditorState.svelte.js';
package/dist/index.d.ts CHANGED
@@ -4,6 +4,7 @@ import type BubbleMenuComponent from "./BubbleMenu.svelte";
4
4
  import type TemplateRendererComponent from "./TemplateRenderer.svelte";
5
5
  import type VariableFormComponent from "./VariableForm.svelte";
6
6
  import type OutlinePanelComponent from "./OutlinePanel.svelte";
7
+ import type MergePreviewComponent from "./MergePreview.svelte";
7
8
  export * from "./editor.svelte.js";
8
9
  export * from "./navigationGuard.js";
9
10
  export * from "./createNavigationGuard.svelte.js";
@@ -11,6 +12,7 @@ export * from "./history/index.js";
11
12
  export * from "./fileImport.svelte.js";
12
13
  export * from "./agentTools.svelte.js";
13
14
  export * from "./outline.svelte.js";
15
+ export * from "./merge.svelte.js";
14
16
  export { DirtyTracker, createDirtyTracker, defaultIsEqual, HistoryManager, createHistoryManager, bindDirtyToHistory, withoutDirty, } from "@pixerate/editor";
15
17
  export type { DirtyTrackerOptions, Command, HistoryState, HistoryManagerOptions, HistoryEntry, HistoryMark, MergedExecutionOptions, HistoryDirtyBinding, BindDirtyToHistoryOptions, } from "@pixerate/editor";
16
18
  export type EditableTextNodeEditorProps = ComponentProps<typeof EditableTextNodeEditorComponent>;
@@ -21,12 +23,17 @@ export { default as EditableTextNodeEditor } from "./EditableTextNodeEditor.svel
21
23
  export { default as BubbleMenu } from "./BubbleMenu.svelte";
22
24
  export { default as TemplateRenderer } from "./TemplateRenderer.svelte";
23
25
  export { default as VariableForm } from "./VariableForm.svelte";
26
+ export type MergePreviewProps = ComponentProps<typeof MergePreviewComponent>;
27
+ export { default as MergePreview } from "./MergePreview.svelte";
24
28
  export type OutlinePanelProps = ComponentProps<typeof OutlinePanelComponent>;
25
29
  export { default as OutlinePanel } from "./OutlinePanel.svelte";
26
30
  export type { Template, TemplateVersion, Token, TokenType, ColorGradient, RichTextPresetOptions, ImageOptions, } from "@pixerate/editor";
27
- export type { PromptValues, PromptDiagnostic, VariableFormField, } from "@pixerate/editor";
31
+ export type { PromptValues, PromptDiagnostic, VariableFormField, MergeDataSource, MergeRecord, MergeRecordsResult, MergeOutput, } from "@pixerate/editor";
28
32
  export * from "./spreadsheet/index.js";
29
33
  export * from "./image-editor/index.js";
30
34
  export * from "./review/index.js";
31
35
  export * from "./commands/index.js";
36
+ export * from "./persistence/index.js";
37
+ export * from "./generation/index.js";
38
+ export * from "./find-replace/index.js";
32
39
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,QAAQ,CAAC;AAE7C,OAAO,KAAK,+BAA+B,MAAM,iCAAiC,CAAC;AACnF,OAAO,KAAK,mBAAmB,MAAM,qBAAqB,CAAC;AAC3D,OAAO,KAAK,yBAAyB,MAAM,2BAA2B,CAAC;AACvE,OAAO,KAAK,qBAAqB,MAAM,uBAAuB,CAAC;AAC/D,OAAO,KAAK,qBAAqB,MAAM,uBAAuB,CAAC;AAE/D,cAAc,oBAAoB,CAAC;AACnC,cAAc,sBAAsB,CAAC;AACrC,cAAc,mCAAmC,CAAC;AAClD,cAAc,oBAAoB,CAAC;AACnC,cAAc,wBAAwB,CAAC;AACvC,cAAc,wBAAwB,CAAC;AACvC,cAAc,qBAAqB,CAAC;AAEpC,OAAO,EACL,YAAY,EACZ,kBAAkB,EAClB,cAAc,EACd,cAAc,EACd,oBAAoB,EACpB,kBAAkB,EAClB,YAAY,GACb,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EACV,mBAAmB,EACnB,OAAO,EACP,YAAY,EACZ,qBAAqB,EACrB,YAAY,EACZ,WAAW,EACX,sBAAsB,EACtB,mBAAmB,EACnB,yBAAyB,GAC1B,MAAM,kBAAkB,CAAC;AAG1B,MAAM,MAAM,2BAA2B,GAAG,cAAc,CACtD,OAAO,+BAA+B,CACvC,CAAC;AACF,MAAM,MAAM,eAAe,GAAG,cAAc,CAAC,OAAO,mBAAmB,CAAC,CAAC;AACzE,MAAM,MAAM,qBAAqB,GAAG,cAAc,CAChD,OAAO,yBAAyB,CACjC,CAAC;AACF,MAAM,MAAM,iBAAiB,GAAG,cAAc,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAE7E,OAAO,EAAE,OAAO,IAAI,sBAAsB,EAAE,MAAM,iCAAiC,CAAC;AACpF,OAAO,EAAE,OAAO,IAAI,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,EAAE,OAAO,IAAI,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AACxE,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAChE,MAAM,MAAM,iBAAiB,GAAG,cAAc,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAC7E,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAEhE,YAAY,EACV,QAAQ,EACR,eAAe,EACf,KAAK,EACL,SAAS,EACT,aAAa,EACb,qBAAqB,EACrB,YAAY,GACb,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EACV,YAAY,EACZ,gBAAgB,EAChB,iBAAiB,GAClB,MAAM,kBAAkB,CAAC;AAE1B,cAAc,wBAAwB,CAAC;AACvC,cAAc,yBAAyB,CAAC;AACxC,cAAc,mBAAmB,CAAC;AAClC,cAAc,qBAAqB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,QAAQ,CAAC;AAE7C,OAAO,KAAK,+BAA+B,MAAM,iCAAiC,CAAC;AACnF,OAAO,KAAK,mBAAmB,MAAM,qBAAqB,CAAC;AAC3D,OAAO,KAAK,yBAAyB,MAAM,2BAA2B,CAAC;AACvE,OAAO,KAAK,qBAAqB,MAAM,uBAAuB,CAAC;AAC/D,OAAO,KAAK,qBAAqB,MAAM,uBAAuB,CAAC;AAC/D,OAAO,KAAK,qBAAqB,MAAM,uBAAuB,CAAC;AAE/D,cAAc,oBAAoB,CAAC;AACnC,cAAc,sBAAsB,CAAC;AACrC,cAAc,mCAAmC,CAAC;AAClD,cAAc,oBAAoB,CAAC;AACnC,cAAc,wBAAwB,CAAC;AACvC,cAAc,wBAAwB,CAAC;AACvC,cAAc,qBAAqB,CAAC;AACpC,cAAc,mBAAmB,CAAC;AAElC,OAAO,EACL,YAAY,EACZ,kBAAkB,EAClB,cAAc,EACd,cAAc,EACd,oBAAoB,EACpB,kBAAkB,EAClB,YAAY,GACb,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EACV,mBAAmB,EACnB,OAAO,EACP,YAAY,EACZ,qBAAqB,EACrB,YAAY,EACZ,WAAW,EACX,sBAAsB,EACtB,mBAAmB,EACnB,yBAAyB,GAC1B,MAAM,kBAAkB,CAAC;AAG1B,MAAM,MAAM,2BAA2B,GAAG,cAAc,CACtD,OAAO,+BAA+B,CACvC,CAAC;AACF,MAAM,MAAM,eAAe,GAAG,cAAc,CAAC,OAAO,mBAAmB,CAAC,CAAC;AACzE,MAAM,MAAM,qBAAqB,GAAG,cAAc,CAChD,OAAO,yBAAyB,CACjC,CAAC;AACF,MAAM,MAAM,iBAAiB,GAAG,cAAc,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAE7E,OAAO,EAAE,OAAO,IAAI,sBAAsB,EAAE,MAAM,iCAAiC,CAAC;AACpF,OAAO,EAAE,OAAO,IAAI,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,EAAE,OAAO,IAAI,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AACxE,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAChE,MAAM,MAAM,iBAAiB,GAAG,cAAc,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAC7E,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAChE,MAAM,MAAM,iBAAiB,GAAG,cAAc,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAC7E,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAEhE,YAAY,EACV,QAAQ,EACR,eAAe,EACf,KAAK,EACL,SAAS,EACT,aAAa,EACb,qBAAqB,EACrB,YAAY,GACb,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EACV,YAAY,EACZ,gBAAgB,EAChB,iBAAiB,EACjB,eAAe,EACf,WAAW,EACX,kBAAkB,EAClB,WAAW,GACZ,MAAM,kBAAkB,CAAC;AAE1B,cAAc,wBAAwB,CAAC;AACvC,cAAc,yBAAyB,CAAC;AACxC,cAAc,mBAAmB,CAAC;AAClC,cAAc,qBAAqB,CAAC;AACpC,cAAc,wBAAwB,CAAC;AACvC,cAAc,uBAAuB,CAAC;AACtC,cAAc,yBAAyB,CAAC"}
package/dist/index.js CHANGED
@@ -5,13 +5,18 @@ export * from "./history/index.js";
5
5
  export * from "./fileImport.svelte.js";
6
6
  export * from "./agentTools.svelte.js";
7
7
  export * from "./outline.svelte.js";
8
+ export * from "./merge.svelte.js";
8
9
  export { DirtyTracker, createDirtyTracker, defaultIsEqual, HistoryManager, createHistoryManager, bindDirtyToHistory, withoutDirty, } from "@pixerate/editor";
9
10
  export { default as EditableTextNodeEditor } from "./EditableTextNodeEditor.svelte";
10
11
  export { default as BubbleMenu } from "./BubbleMenu.svelte";
11
12
  export { default as TemplateRenderer } from "./TemplateRenderer.svelte";
12
13
  export { default as VariableForm } from "./VariableForm.svelte";
14
+ export { default as MergePreview } from "./MergePreview.svelte";
13
15
  export { default as OutlinePanel } from "./OutlinePanel.svelte";
14
16
  export * from "./spreadsheet/index.js";
15
17
  export * from "./image-editor/index.js";
16
18
  export * from "./review/index.js";
17
19
  export * from "./commands/index.js";
20
+ export * from "./persistence/index.js";
21
+ export * from "./generation/index.js";
22
+ export * from "./find-replace/index.js";
@@ -0,0 +1,51 @@
1
+ import { type MergeRecordsOptions, type MergeRecordsResult, type ParseDataSourceOptions, type Template } from "@pixerate/editor";
2
+ export interface MergePreviewOptions {
3
+ /** The prompt merged with each record. */
4
+ template: string;
5
+ templates?: Template[];
6
+ bindings?: Record<string, string>;
7
+ /** How to read the data source text. */
8
+ parseOptions?: ParseDataSourceOptions;
9
+ /** Passed to `mergeRecords` by `merge()`. */
10
+ skipIf?: MergeRecordsOptions["skipIf"];
11
+ separator?: string;
12
+ maxOutputs?: number;
13
+ }
14
+ /**
15
+ * Reactive mail-merge preview state for Svelte 5 (the counterpart of React's
16
+ * `useMergePreview`): parses a pasted or loaded data source, matches its
17
+ * fields to the template's variables (editable), steps through records and
18
+ * merges them all with `mergeRecords`.
19
+ *
20
+ * `getOptions` is read reactively, so pass `() => ({ template, templates })`
21
+ * built from props or state.
22
+ */
23
+ export declare function createMergePreview(getOptions: () => MergePreviewOptions, initialSource?: string): {
24
+ sourceText: string;
25
+ setSourceText: (text: string) => void;
26
+ /** Reads a picked file (CSV, TSV or JSON) into `sourceText`. */
27
+ loadFile(file: Blob): Promise<void>;
28
+ readonly source: import("@pixerate/editor").MergeDataSource;
29
+ /** Variable names the template uses, in order. */
30
+ readonly variables: string[];
31
+ /** Variable → field: automatic matches with the user's overrides applied. */
32
+ readonly mapping: Record<string, string | null>;
33
+ setFieldMapping(variable: string, field: string | null): void;
34
+ resetMapping(): void;
35
+ /** 0-based index of the previewed record. */
36
+ readonly index: number;
37
+ readonly count: number;
38
+ setIndex: (next: number) => void;
39
+ next(): void;
40
+ prev(): void;
41
+ readonly record: import("@pixerate/editor").MergeRecord;
42
+ /** Prompt values for the previewed record. */
43
+ readonly values: import("@pixerate/editor").PromptValues;
44
+ /** Merges every record with the current mapping and stores the result. */
45
+ merge(): MergeRecordsResult;
46
+ readonly result: MergeRecordsResult | null;
47
+ /** The last merge's outputs as text or JSON. */
48
+ serialize(format: "text" | "json"): string;
49
+ };
50
+ export type MergePreviewState = ReturnType<typeof createMergePreview>;
51
+ //# sourceMappingURL=merge.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"merge.svelte.d.ts","sourceRoot":"","sources":["../src/merge.svelte.ts"],"names":[],"mappings":"AAAA,OAAO,EAOL,KAAK,mBAAmB,EACxB,KAAK,kBAAkB,EACvB,KAAK,sBAAsB,EAC3B,KAAK,QAAQ,EACd,MAAM,kBAAkB,CAAC;AAE1B,MAAM,WAAW,mBAAmB;IAClC,0CAA0C;IAC1C,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,QAAQ,EAAE,CAAC;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAClC,wCAAwC;IACxC,YAAY,CAAC,EAAE,sBAAsB,CAAC;IACtC,6CAA6C;IAC7C,MAAM,CAAC,EAAE,mBAAmB,CAAC,QAAQ,CAAC,CAAC;IACvC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAChC,UAAU,EAAE,MAAM,mBAAmB,EACrC,aAAa,SAAK;gBA4CK,MAAM;0BAbA,MAAM;IAiBjC,gEAAgE;mBAC3C,IAAI;;IAMzB,kDAAkD;;IAIlD,6EAA6E;;8BAInD,MAAM,SAAS,MAAM,GAAG,IAAI;;IAOtD,6CAA6C;;;qBAlCvB,MAAM;;;;IAmD5B,8CAA8C;;IAI9C,0EAA0E;aACjE,kBAAkB;;IAe3B,gDAAgD;sBAC9B,MAAM,GAAG,MAAM,GAAG,MAAM;EAI7C;AAED,MAAM,MAAM,iBAAiB,GAAG,UAAU,CAAC,OAAO,kBAAkB,CAAC,CAAC"}
@@ -0,0 +1,117 @@
1
+ import { extractVariables, matchFields, mergeRecords, parseDataSource, recordToValues, serializeMergeOutputs, } from "@pixerate/editor";
2
+ /**
3
+ * Reactive mail-merge preview state for Svelte 5 (the counterpart of React's
4
+ * `useMergePreview`): parses a pasted or loaded data source, matches its
5
+ * fields to the template's variables (editable), steps through records and
6
+ * merges them all with `mergeRecords`.
7
+ *
8
+ * `getOptions` is read reactively, so pass `() => ({ template, templates })`
9
+ * built from props or state.
10
+ */
11
+ export function createMergePreview(getOptions, initialSource = "") {
12
+ let sourceText = $state(initialSource);
13
+ let overrides = $state.raw({});
14
+ let index = $state(0);
15
+ let result = $state.raw(null);
16
+ const options = $derived(getOptions());
17
+ const source = $derived(parseDataSource(sourceText, options.parseOptions));
18
+ const variables = $derived(extractVariables(options.template, {
19
+ templates: options.templates,
20
+ bindings: options.bindings,
21
+ }).map((v) => v.name));
22
+ const mapping = $derived.by(() => {
23
+ const merged = {
24
+ ...matchFields(variables, source.fields).mapping,
25
+ };
26
+ for (const [variable, field] of Object.entries(overrides)) {
27
+ if (variables.includes(variable) && (field === null || source.fields.includes(field))) {
28
+ merged[variable] = field;
29
+ }
30
+ }
31
+ return merged;
32
+ });
33
+ const count = $derived(source.records.length);
34
+ const current = $derived(count === 0 ? 0 : Math.min(index, count - 1));
35
+ const record = $derived(source.records[current]);
36
+ const values = $derived(record ? recordToValues(record, mapping) : {});
37
+ function setSourceText(text) {
38
+ sourceText = text;
39
+ index = 0;
40
+ result = null;
41
+ }
42
+ function setIndex(next) {
43
+ index = Math.max(0, Math.min(next, count - 1));
44
+ }
45
+ return {
46
+ get sourceText() {
47
+ return sourceText;
48
+ },
49
+ set sourceText(text) {
50
+ setSourceText(text);
51
+ },
52
+ setSourceText,
53
+ /** Reads a picked file (CSV, TSV or JSON) into `sourceText`. */
54
+ async loadFile(file) {
55
+ setSourceText(await file.text());
56
+ },
57
+ get source() {
58
+ return source;
59
+ },
60
+ /** Variable names the template uses, in order. */
61
+ get variables() {
62
+ return variables;
63
+ },
64
+ /** Variable → field: automatic matches with the user's overrides applied. */
65
+ get mapping() {
66
+ return mapping;
67
+ },
68
+ setFieldMapping(variable, field) {
69
+ overrides = { ...overrides, [variable]: field };
70
+ result = null;
71
+ },
72
+ resetMapping() {
73
+ overrides = {};
74
+ },
75
+ /** 0-based index of the previewed record. */
76
+ get index() {
77
+ return current;
78
+ },
79
+ get count() {
80
+ return count;
81
+ },
82
+ setIndex,
83
+ next() {
84
+ setIndex(current + 1);
85
+ },
86
+ prev() {
87
+ setIndex(current - 1);
88
+ },
89
+ get record() {
90
+ return record;
91
+ },
92
+ /** Prompt values for the previewed record. */
93
+ get values() {
94
+ return values;
95
+ },
96
+ /** Merges every record with the current mapping and stores the result. */
97
+ merge() {
98
+ const merged = mergeRecords(options.template, source, {
99
+ templates: options.templates,
100
+ bindings: options.bindings,
101
+ mapping,
102
+ skipIf: options.skipIf,
103
+ separator: options.separator,
104
+ maxOutputs: options.maxOutputs,
105
+ });
106
+ result = merged;
107
+ return merged;
108
+ },
109
+ get result() {
110
+ return result;
111
+ },
112
+ /** The last merge's outputs as text or JSON. */
113
+ serialize(format) {
114
+ return result ? serializeMergeOutputs(result.outputs, format, options.separator) : "";
115
+ },
116
+ };
117
+ }
@@ -0,0 +1,57 @@
1
+ <script lang="ts">
2
+ import { formatRecoveryMessage, type RecoveryMeta } from '@pixerate/editor/persistence';
3
+
4
+ interface Props {
5
+ /** `createAutosave(...).recovery`; nothing renders while it is `null`. */
6
+ recovery: { meta: RecoveryMeta } | null;
7
+ onRestore: () => void;
8
+ onDiscard: () => void;
9
+ /** Formats the snapshot time (default `toLocaleString`). */
10
+ formatTime?: (time: number) => string;
11
+ restoreLabel?: string;
12
+ discardLabel?: string;
13
+ class?: string;
14
+ }
15
+
16
+ let {
17
+ recovery,
18
+ onRestore,
19
+ onDiscard,
20
+ formatTime,
21
+ restoreLabel = 'Restore',
22
+ discardLabel = 'Discard',
23
+ class: className = ''
24
+ }: Props = $props();
25
+ </script>
26
+
27
+ {#if recovery}
28
+ <div
29
+ role="alertdialog"
30
+ aria-label="Unsaved changes found"
31
+ data-recovery-prompt=""
32
+ class="flex flex-wrap items-center gap-3 rounded-md border border-border bg-background px-3 py-2 text-sm text-foreground shadow-sm {className}"
33
+ >
34
+ <span class="min-w-0 flex-1">
35
+ {formatRecoveryMessage(recovery.meta, formatTime)}
36
+ {#if recovery.meta.name}
37
+ <span class="ml-1 text-xs text-muted-foreground">({recovery.meta.name})</span>
38
+ {/if}
39
+ </span>
40
+ <button
41
+ type="button"
42
+ data-recovery-restore=""
43
+ onclick={onRestore}
44
+ class="rounded-md bg-primary px-3 py-1 text-xs font-medium text-primary-foreground hover:opacity-90"
45
+ >
46
+ {restoreLabel}
47
+ </button>
48
+ <button
49
+ type="button"
50
+ data-recovery-discard=""
51
+ onclick={onDiscard}
52
+ class="rounded-md border border-border px-3 py-1 text-xs font-medium hover:bg-muted"
53
+ >
54
+ {discardLabel}
55
+ </button>
56
+ </div>
57
+ {/if}