@pixerate/editor-svelte 0.21.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.
- package/README.md +6 -1
- package/dist/MergePreview.svelte +201 -0
- package/dist/MergePreview.svelte.d.ts +25 -0
- package/dist/MergePreview.svelte.d.ts.map +1 -0
- package/dist/TemplateRenderer.svelte +4 -10
- package/dist/TemplateRenderer.svelte.d.ts.map +1 -1
- package/dist/canvas/CanvasGuidesLayer.svelte +32 -2
- package/dist/canvas/CanvasGuidesLayer.svelte.d.ts +2 -0
- package/dist/canvas/CanvasGuidesLayer.svelte.d.ts.map +1 -1
- package/dist/canvas/createCanvasArrange.svelte.d.ts +16 -1
- package/dist/canvas/createCanvasArrange.svelte.d.ts.map +1 -1
- package/dist/canvas/createCanvasArrange.svelte.js +19 -2
- package/dist/canvas/createCanvasAutoLayout.svelte.d.ts +68 -0
- package/dist/canvas/createCanvasAutoLayout.svelte.d.ts.map +1 -0
- package/dist/canvas/createCanvasAutoLayout.svelte.js +78 -0
- package/dist/canvas/createCanvasMeasure.svelte.d.ts +53 -0
- package/dist/canvas/createCanvasMeasure.svelte.d.ts.map +1 -0
- package/dist/canvas/createCanvasMeasure.svelte.js +80 -0
- package/dist/canvas/index.d.ts +4 -0
- package/dist/canvas/index.d.ts.map +1 -1
- package/dist/canvas/index.js +6 -0
- package/dist/commands/CommandPalette.svelte +129 -0
- package/dist/commands/CommandPalette.svelte.d.ts +13 -0
- package/dist/commands/CommandPalette.svelte.d.ts.map +1 -0
- package/dist/commands/commandShortcuts.svelte.d.ts +30 -0
- package/dist/commands/commandShortcuts.svelte.d.ts.map +1 -0
- package/dist/commands/commandShortcuts.svelte.js +37 -0
- package/dist/commands/index.d.ts +7 -0
- package/dist/commands/index.d.ts.map +1 -0
- package/dist/commands/index.js +3 -0
- package/dist/commands/palette.svelte.d.ts +34 -0
- package/dist/commands/palette.svelte.d.ts.map +1 -0
- package/dist/commands/palette.svelte.js +61 -0
- package/dist/find-replace/FindReplaceBar.svelte +203 -0
- package/dist/find-replace/FindReplaceBar.svelte.d.ts +15 -0
- package/dist/find-replace/FindReplaceBar.svelte.d.ts.map +1 -0
- package/dist/find-replace/findReplace.svelte.d.ts +27 -0
- package/dist/find-replace/findReplace.svelte.d.ts.map +1 -0
- package/dist/find-replace/findReplace.svelte.js +79 -0
- package/dist/find-replace/index.d.ts +6 -0
- package/dist/find-replace/index.d.ts.map +1 -0
- package/dist/find-replace/index.js +2 -0
- package/dist/generation/GenerationSettings.svelte +174 -0
- package/dist/generation/GenerationSettings.svelte.d.ts +23 -0
- package/dist/generation/GenerationSettings.svelte.d.ts.map +1 -0
- package/dist/generation/JobProgress.svelte +53 -0
- package/dist/generation/JobProgress.svelte.d.ts +17 -0
- package/dist/generation/JobProgress.svelte.d.ts.map +1 -0
- package/dist/generation/index.d.ts +10 -0
- package/dist/generation/index.d.ts.map +1 -0
- package/dist/generation/index.js +4 -0
- package/dist/generation/plan.svelte.d.ts +16 -0
- package/dist/generation/plan.svelte.d.ts.map +1 -0
- package/dist/generation/plan.svelte.js +26 -0
- package/dist/generation/progress.svelte.d.ts +25 -0
- package/dist/generation/progress.svelte.d.ts.map +1 -0
- package/dist/generation/progress.svelte.js +36 -0
- package/dist/history/createHistoryShortcuts.d.ts +1 -1
- package/dist/history/createHistoryShortcuts.d.ts.map +1 -1
- package/dist/history/createHistoryShortcuts.js +10 -32
- package/dist/history/history.svelte.d.ts +13 -4
- package/dist/history/history.svelte.d.ts.map +1 -1
- package/dist/history/history.svelte.js +10 -0
- package/dist/history/historyDirty.svelte.d.ts +31 -0
- package/dist/history/historyDirty.svelte.d.ts.map +1 -0
- package/dist/history/historyDirty.svelte.js +30 -0
- package/dist/history/index.d.ts +1 -0
- package/dist/history/index.d.ts.map +1 -1
- package/dist/history/index.js +1 -0
- package/dist/image-editor/AdjustmentLayersPanel.svelte +309 -0
- package/dist/image-editor/AdjustmentLayersPanel.svelte.d.ts +11 -0
- package/dist/image-editor/AdjustmentLayersPanel.svelte.d.ts.map +1 -0
- package/dist/image-editor/ControlSlider.svelte +39 -0
- package/dist/image-editor/ControlSlider.svelte.d.ts +13 -0
- package/dist/image-editor/ControlSlider.svelte.d.ts.map +1 -0
- package/dist/image-editor/CurvesEditor.svelte +105 -0
- package/dist/image-editor/CurvesEditor.svelte.d.ts +13 -0
- package/dist/image-editor/CurvesEditor.svelte.d.ts.map +1 -0
- package/dist/image-editor/Histogram.svelte +49 -0
- package/dist/image-editor/Histogram.svelte.d.ts +12 -0
- package/dist/image-editor/Histogram.svelte.d.ts.map +1 -0
- package/dist/image-editor/ImageEditor.svelte +38 -107
- package/dist/image-editor/ImageEditor.svelte.d.ts.map +1 -1
- package/dist/image-editor/imageEditorState.svelte.d.ts +16 -1
- package/dist/image-editor/imageEditorState.svelte.d.ts.map +1 -1
- package/dist/image-editor/imageEditorState.svelte.js +44 -0
- package/dist/image-editor/index.d.ts +12 -0
- package/dist/image-editor/index.d.ts.map +1 -1
- package/dist/image-editor/index.js +4 -0
- package/dist/index.d.ts +11 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -1
- package/dist/merge.svelte.d.ts +51 -0
- package/dist/merge.svelte.d.ts.map +1 -0
- package/dist/merge.svelte.js +117 -0
- package/dist/persistence/RecoveryPrompt.svelte +57 -0
- package/dist/persistence/RecoveryPrompt.svelte.d.ts +18 -0
- package/dist/persistence/RecoveryPrompt.svelte.d.ts.map +1 -0
- package/dist/persistence/autosave.svelte.d.ts +63 -0
- package/dist/persistence/autosave.svelte.d.ts.map +1 -0
- package/dist/persistence/autosave.svelte.js +110 -0
- package/dist/persistence/index.d.ts +6 -0
- package/dist/persistence/index.d.ts.map +1 -0
- package/dist/persistence/index.js +2 -0
- package/dist/review/documentCompare.svelte.d.ts +21 -0
- package/dist/review/documentCompare.svelte.d.ts.map +1 -0
- package/dist/review/documentCompare.svelte.js +23 -0
- package/dist/review/index.d.ts +1 -0
- package/dist/review/index.d.ts.map +1 -1
- package/dist/review/index.js +1 -0
- package/dist/spreadsheet/SpreadsheetContextMenu.svelte +6 -3
- package/dist/spreadsheet/SpreadsheetContextMenu.svelte.d.ts.map +1 -1
- package/dist/spreadsheet/SpreadsheetEditor.svelte +7 -2
- package/dist/spreadsheet/SpreadsheetEditor.svelte.d.ts.map +1 -1
- package/dist/spreadsheet/spreadsheet.svelte.d.ts +2 -0
- package/dist/spreadsheet/spreadsheet.svelte.d.ts.map +1 -1
- package/package.json +26 -2
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plan.svelte.d.ts","sourceRoot":"","sources":["../../src/generation/plan.svelte.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,cAAc,EACnB,KAAK,iBAAiB,EACtB,KAAK,iBAAiB,EACtB,KAAK,cAAc,EACnB,KAAK,SAAS,EACd,KAAK,WAAW,EACjB,MAAM,6BAA6B,CAAC;AAErC,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAC;IAC9B,QAAQ,CAAC,EAAE,EAAE,OAAO,CAAC;IACrB,4BAA4B;IAC5B,QAAQ,CAAC,OAAO,EAAE,iBAAiB,CAAC;IACpC,QAAQ,CAAC,WAAW,EAAE,cAAc,EAAE,CAAC;IACvC,QAAQ,CAAC,MAAM,EAAE,SAAS,EAAE,CAAC;CAC9B;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,MAAM,iBAAiB,EAChC,YAAY,EAAE,MAAM,iBAAiB,EACrC,OAAO,GAAE,MAAM,WAAwB,GACtC,oBAAoB,CAmBtB"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { planGenerationRequest, } from "@pixerate/editor/generation";
|
|
2
|
+
/**
|
|
3
|
+
* Reactive `planGenerationRequest` (Svelte 5 runes). Pass getters so the plan
|
|
4
|
+
* follows `$state` request/capabilities/options:
|
|
5
|
+
* `createGenerationPlan(() => request, () => caps, () => ({ strategy }))`.
|
|
6
|
+
*/
|
|
7
|
+
export function createGenerationPlan(request, capabilities, options = () => ({})) {
|
|
8
|
+
const plan = $derived(planGenerationRequest(request(), capabilities(), options()));
|
|
9
|
+
return {
|
|
10
|
+
get plan() {
|
|
11
|
+
return plan;
|
|
12
|
+
},
|
|
13
|
+
get ok() {
|
|
14
|
+
return plan.ok;
|
|
15
|
+
},
|
|
16
|
+
get request() {
|
|
17
|
+
return plan.request;
|
|
18
|
+
},
|
|
19
|
+
get adjustments() {
|
|
20
|
+
return plan.adjustments;
|
|
21
|
+
},
|
|
22
|
+
get errors() {
|
|
23
|
+
return plan.errors;
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { type JobProgressEstimate } from "@pixerate/editor/generation";
|
|
2
|
+
export interface JobProgressOptions {
|
|
3
|
+
startedAt: number | Date | string;
|
|
4
|
+
expectedMs?: number;
|
|
5
|
+
done?: boolean;
|
|
6
|
+
/** Fixed current time (disables ticking; useful for SSR and tests). */
|
|
7
|
+
now?: number | Date | string;
|
|
8
|
+
/** Update interval while running in ms (default 1000). */
|
|
9
|
+
intervalMs?: number;
|
|
10
|
+
}
|
|
11
|
+
export interface SvelteJobProgress {
|
|
12
|
+
readonly progress: JobProgressEstimate;
|
|
13
|
+
readonly percent: number;
|
|
14
|
+
readonly label: string;
|
|
15
|
+
/** Stops ticking. */
|
|
16
|
+
destroy(): void;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Ticking `estimateJobProgress` (Svelte 5 runes). Pass a getter so it follows
|
|
20
|
+
* `$state` options; it ticks every `intervalMs` while not `done` and no fixed
|
|
21
|
+
* `now` is given (browser only; nothing ticks during SSR). Call `destroy()`
|
|
22
|
+
* when done (e.g. in `onDestroy`).
|
|
23
|
+
*/
|
|
24
|
+
export declare function createJobProgress(options: () => JobProgressOptions): SvelteJobProgress;
|
|
25
|
+
//# sourceMappingURL=progress.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"progress.svelte.d.ts","sourceRoot":"","sources":["../../src/generation/progress.svelte.ts"],"names":[],"mappings":"AAAA,OAAO,EAAuB,KAAK,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAE5F,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAAC;IAClC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,uEAAuE;IACvE,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAAC;IAC7B,0DAA0D;IAC1D,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,QAAQ,EAAE,mBAAmB,CAAC;IACvC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,qBAAqB;IACrB,OAAO,IAAI,IAAI,CAAC;CACjB;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,kBAAkB,GAAG,iBAAiB,CA2BtF"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { estimateJobProgress } from "@pixerate/editor/generation";
|
|
2
|
+
/**
|
|
3
|
+
* Ticking `estimateJobProgress` (Svelte 5 runes). Pass a getter so it follows
|
|
4
|
+
* `$state` options; it ticks every `intervalMs` while not `done` and no fixed
|
|
5
|
+
* `now` is given (browser only; nothing ticks during SSR). Call `destroy()`
|
|
6
|
+
* when done (e.g. in `onDestroy`).
|
|
7
|
+
*/
|
|
8
|
+
export function createJobProgress(options) {
|
|
9
|
+
let tick = $state(Date.now());
|
|
10
|
+
const timer = typeof window !== "undefined"
|
|
11
|
+
? setInterval(() => {
|
|
12
|
+
const current = options();
|
|
13
|
+
if (!current.done && current.now === undefined)
|
|
14
|
+
tick = Date.now();
|
|
15
|
+
}, Math.max(16, options().intervalMs ?? 1000))
|
|
16
|
+
: null;
|
|
17
|
+
const progress = $derived.by(() => {
|
|
18
|
+
const { startedAt, expectedMs, done, now } = options();
|
|
19
|
+
return estimateJobProgress({ startedAt, expectedMs, done, now: now ?? tick });
|
|
20
|
+
});
|
|
21
|
+
return {
|
|
22
|
+
get progress() {
|
|
23
|
+
return progress;
|
|
24
|
+
},
|
|
25
|
+
get percent() {
|
|
26
|
+
return progress.percent;
|
|
27
|
+
},
|
|
28
|
+
get label() {
|
|
29
|
+
return progress.label;
|
|
30
|
+
},
|
|
31
|
+
destroy() {
|
|
32
|
+
if (timer !== null)
|
|
33
|
+
clearInterval(timer);
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createHistoryShortcuts.d.ts","sourceRoot":"","sources":["../../src/history/createHistoryShortcuts.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"createHistoryShortcuts.d.ts","sourceRoot":"","sources":["../../src/history/createHistoryShortcuts.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAG/D,MAAM,WAAW,uBAAuB;IACtC;;OAEG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;OAEG;IACH,MAAM,CAAC,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI,CAAC;IACrC;;;OAGG;IACH,sBAAsB,CAAC,EAAE,OAAO,CAAC;CAClC;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,cAAc,EACvB,OAAO,GAAE,uBAA4B,GACpC;IAAE,OAAO,EAAE,MAAM,IAAI,CAAA;CAAE,CAqCzB"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { matchHistoryShortcut } from "@pixerate/editor/commands";
|
|
1
2
|
/**
|
|
2
3
|
* Attaches keyboard listeners for Undo (Mod+Z) and Redo (Mod+Shift+Z / Mod+Y).
|
|
3
4
|
* Returns an object with a `destroy()` function for lifecycle cleanup.
|
|
@@ -13,39 +14,16 @@ export function createHistoryShortcuts(manager, options = {}) {
|
|
|
13
14
|
}
|
|
14
15
|
const handleKeyDown = (event) => {
|
|
15
16
|
const e = event;
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
const
|
|
19
|
-
if (
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
const targetEl = (e.target || document.activeElement);
|
|
23
|
-
if (targetEl && targetEl.tagName) {
|
|
24
|
-
const tagName = targetEl.tagName.toLowerCase();
|
|
25
|
-
const isInput = tagName === "input" || tagName === "textarea";
|
|
26
|
-
const isContentEditable = targetEl.isContentEditable ||
|
|
27
|
-
targetEl.getAttribute?.("contenteditable") === "true";
|
|
28
|
-
if (isInput || isContentEditable) {
|
|
29
|
-
return;
|
|
30
|
-
}
|
|
31
|
-
}
|
|
17
|
+
// Default bindings from the command system: Mod+Z, Mod+Shift+Z and (off macOS) Mod+Y,
|
|
18
|
+
// matched by `event.code` with `Mod` = ⌘ on macOS and Ctrl elsewhere.
|
|
19
|
+
const action = matchHistoryShortcut(e, { allowInEditable: !ignoreEditableElements });
|
|
20
|
+
if (action === "undo" && manager.getState().canUndo) {
|
|
21
|
+
e.preventDefault();
|
|
22
|
+
manager.undo();
|
|
32
23
|
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
if (manager.getState().canUndo) {
|
|
37
|
-
e.preventDefault();
|
|
38
|
-
manager.undo();
|
|
39
|
-
}
|
|
40
|
-
return;
|
|
41
|
-
}
|
|
42
|
-
// Redo: Mod+Shift+Z or (on non-Mac) Mod+Y
|
|
43
|
-
const isRedoKey = (key === "z" && e.shiftKey) || (!isMac && key === "y" && !e.shiftKey);
|
|
44
|
-
if (isRedoKey) {
|
|
45
|
-
if (manager.getState().canRedo) {
|
|
46
|
-
e.preventDefault();
|
|
47
|
-
manager.redo();
|
|
48
|
-
}
|
|
24
|
+
else if (action === "redo" && manager.getState().canRedo) {
|
|
25
|
+
e.preventDefault();
|
|
26
|
+
manager.redo();
|
|
49
27
|
}
|
|
50
28
|
};
|
|
51
29
|
eventTarget.addEventListener("keydown", handleKeyDown);
|
|
@@ -1,14 +1,23 @@
|
|
|
1
|
-
import { HistoryManager, HistoryState, Command, HistoryManagerOptions } from "@pixerate/editor/history";
|
|
1
|
+
import { HistoryManager, HistoryState, HistoryEntry, HistoryMark, MergedExecutionOptions, Command, HistoryManagerOptions } from "@pixerate/editor/history";
|
|
2
2
|
export interface SvelteHistory extends HistoryState {
|
|
3
3
|
readonly manager: HistoryManager;
|
|
4
|
+
/** History panel rows (undo steps, then redo steps). */
|
|
5
|
+
readonly entries: HistoryEntry[];
|
|
4
6
|
undo(): boolean;
|
|
5
7
|
redo(): boolean;
|
|
6
8
|
clear(): void;
|
|
7
9
|
execute(command: Command): void;
|
|
8
|
-
executeMerged(command: Command, options?:
|
|
9
|
-
windowMs?: number;
|
|
10
|
-
}): void;
|
|
10
|
+
executeMerged(command: Command, options?: MergedExecutionOptions): void;
|
|
11
11
|
batch(name: string, fn: () => void): void;
|
|
12
|
+
/** Undoes / redoes until the entry at `index` is current (`-1` = before all). */
|
|
13
|
+
goTo(index: number): number;
|
|
14
|
+
/** Captures the current point, e.g. when a drag starts. */
|
|
15
|
+
mark(): HistoryMark;
|
|
16
|
+
/** Takes back every step since `mark` without leaving redo (Escape-cancel). */
|
|
17
|
+
revertTo(mark: HistoryMark): number;
|
|
18
|
+
/** Joins every merged execute into one step until `endGesture`. */
|
|
19
|
+
beginGesture(key: string): void;
|
|
20
|
+
endGesture(): void;
|
|
12
21
|
subscribe(run: (value: HistoryState) => void): () => void;
|
|
13
22
|
}
|
|
14
23
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"history.svelte.d.ts","sourceRoot":"","sources":["../../src/history/history.svelte.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,cAAc,EACd,YAAY,EACZ,OAAO,EAEP,qBAAqB,EACtB,MAAM,0BAA0B,CAAC;AAElC,MAAM,WAAW,aAAc,SAAQ,YAAY;IACjD,QAAQ,CAAC,OAAO,EAAE,cAAc,CAAC;IACjC,IAAI,IAAI,OAAO,CAAC;IAChB,IAAI,IAAI,OAAO,CAAC;IAChB,KAAK,IAAI,IAAI,CAAC;IACd,OAAO,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAAC;IAChC,aAAa,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE;
|
|
1
|
+
{"version":3,"file":"history.svelte.d.ts","sourceRoot":"","sources":["../../src/history/history.svelte.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,cAAc,EACd,YAAY,EACZ,YAAY,EACZ,WAAW,EACX,sBAAsB,EACtB,OAAO,EAEP,qBAAqB,EACtB,MAAM,0BAA0B,CAAC;AAElC,MAAM,WAAW,aAAc,SAAQ,YAAY;IACjD,QAAQ,CAAC,OAAO,EAAE,cAAc,CAAC;IACjC,wDAAwD;IACxD,QAAQ,CAAC,OAAO,EAAE,YAAY,EAAE,CAAC;IACjC,IAAI,IAAI,OAAO,CAAC;IAChB,IAAI,IAAI,OAAO,CAAC;IAChB,KAAK,IAAI,IAAI,CAAC;IACd,OAAO,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAAC;IAChC,aAAa,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,sBAAsB,GAAG,IAAI,CAAC;IACxE,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,IAAI,GAAG,IAAI,CAAC;IAC1C,iFAAiF;IACjF,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC;IAC5B,2DAA2D;IAC3D,IAAI,IAAI,WAAW,CAAC;IACpB,+EAA+E;IAC/E,QAAQ,CAAC,IAAI,EAAE,WAAW,GAAG,MAAM,CAAC;IACpC,mEAAmE;IACnE,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,UAAU,IAAI,IAAI,CAAC;IACnB,SAAS,CAAC,GAAG,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;CAC3D;AAED;;GAEG;AACH,wBAAgB,aAAa,CAC3B,gBAAgB,CAAC,EAAE,cAAc,GAAG,qBAAqB,GACxD,aAAa,CAqDf"}
|
|
@@ -32,12 +32,22 @@ export function createHistory(managerOrOptions) {
|
|
|
32
32
|
get manager() {
|
|
33
33
|
return manager;
|
|
34
34
|
},
|
|
35
|
+
get entries() {
|
|
36
|
+
// Reading `state` makes the getter reactive; getEntries() is cached per change.
|
|
37
|
+
void state;
|
|
38
|
+
return manager.getEntries();
|
|
39
|
+
},
|
|
35
40
|
undo: () => manager.undo(),
|
|
36
41
|
redo: () => manager.redo(),
|
|
37
42
|
clear: () => manager.clear(),
|
|
38
43
|
execute: (cmd) => manager.execute(cmd),
|
|
39
44
|
executeMerged: (cmd, opts) => manager.executeMerged(cmd, opts),
|
|
40
45
|
batch: (name, fn) => manager.batch(name, fn),
|
|
46
|
+
goTo: (index) => manager.goTo(index),
|
|
47
|
+
mark: () => manager.mark(),
|
|
48
|
+
revertTo: (m) => manager.revertTo(m),
|
|
49
|
+
beginGesture: (key) => manager.beginGesture(key),
|
|
50
|
+
endGesture: () => manager.endGesture(),
|
|
41
51
|
subscribe: (run) => manager.subscribe(run),
|
|
42
52
|
};
|
|
43
53
|
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { DirtyTracker, type DirtyTrackerOptions, type HistoryManager } from "@pixerate/editor/history";
|
|
2
|
+
export interface CreateHistoryDirtyOptions<T> extends DirtyTrackerOptions<T> {
|
|
3
|
+
/** The history whose changes re-check the document (a `createHistory().manager`). */
|
|
4
|
+
history: HistoryManager;
|
|
5
|
+
/** Reads the current document value. */
|
|
6
|
+
getValue: () => T;
|
|
7
|
+
/** Existing tracker to drive. */
|
|
8
|
+
tracker?: DirtyTracker<T>;
|
|
9
|
+
}
|
|
10
|
+
export interface SvelteHistoryDirty<T> {
|
|
11
|
+
/** Whether the document differs from the saved state (reactive). */
|
|
12
|
+
readonly isDirty: boolean;
|
|
13
|
+
/** Marks the current document as saved. */
|
|
14
|
+
markClean(): void;
|
|
15
|
+
/** Runs a load / restore without marking the document dirty (re-entrant). */
|
|
16
|
+
suppress<R>(fn: () => R): R;
|
|
17
|
+
/** Re-checks after a change made outside the history. */
|
|
18
|
+
refresh(): boolean;
|
|
19
|
+
readonly tracker: DirtyTracker<T>;
|
|
20
|
+
/** Stops following the history. */
|
|
21
|
+
destroy(): void;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Svelte 5 rune: dirty-vs-saved tracking bound to a HistoryManager. Undoing
|
|
25
|
+
* back to the saved state makes the document clean again, so a navigation
|
|
26
|
+
* guard fed with `isDirty` stops prompting.
|
|
27
|
+
*
|
|
28
|
+
* Usage: `createNavigationGuard({ isDirty: () => dirty.isDirty })`.
|
|
29
|
+
*/
|
|
30
|
+
export declare function createHistoryDirty<T>(options: CreateHistoryDirtyOptions<T>): SvelteHistoryDirty<T>;
|
|
31
|
+
//# sourceMappingURL=historyDirty.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"historyDirty.svelte.d.ts","sourceRoot":"","sources":["../../src/history/historyDirty.svelte.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,EAEZ,KAAK,mBAAmB,EACxB,KAAK,cAAc,EACpB,MAAM,0BAA0B,CAAC;AAElC,MAAM,WAAW,yBAAyB,CAAC,CAAC,CAAE,SAAQ,mBAAmB,CAAC,CAAC,CAAC;IAC1E,qFAAqF;IACrF,OAAO,EAAE,cAAc,CAAC;IACxB,wCAAwC;IACxC,QAAQ,EAAE,MAAM,CAAC,CAAC;IAClB,iCAAiC;IACjC,OAAO,CAAC,EAAE,YAAY,CAAC,CAAC,CAAC,CAAC;CAC3B;AAED,MAAM,WAAW,kBAAkB,CAAC,CAAC;IACnC,oEAAoE;IACpE,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,2CAA2C;IAC3C,SAAS,IAAI,IAAI,CAAC;IAClB,6EAA6E;IAC7E,QAAQ,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC;IAC5B,yDAAyD;IACzD,OAAO,IAAI,OAAO,CAAC;IACnB,QAAQ,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC,CAAC,CAAC;IAClC,mCAAmC;IACnC,OAAO,IAAI,IAAI,CAAC;CACjB;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,EAClC,OAAO,EAAE,yBAAyB,CAAC,CAAC,CAAC,GACpC,kBAAkB,CAAC,CAAC,CAAC,CAsBvB"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { bindDirtyToHistory, } from "@pixerate/editor/history";
|
|
2
|
+
/**
|
|
3
|
+
* Svelte 5 rune: dirty-vs-saved tracking bound to a HistoryManager. Undoing
|
|
4
|
+
* back to the saved state makes the document clean again, so a navigation
|
|
5
|
+
* guard fed with `isDirty` stops prompting.
|
|
6
|
+
*
|
|
7
|
+
* Usage: `createNavigationGuard({ isDirty: () => dirty.isDirty })`.
|
|
8
|
+
*/
|
|
9
|
+
export function createHistoryDirty(options) {
|
|
10
|
+
const binding = bindDirtyToHistory(options);
|
|
11
|
+
let isDirty = $state(binding.dirty);
|
|
12
|
+
const unsubscribe = binding.subscribe((dirty) => {
|
|
13
|
+
isDirty = dirty;
|
|
14
|
+
});
|
|
15
|
+
return {
|
|
16
|
+
get isDirty() {
|
|
17
|
+
return isDirty;
|
|
18
|
+
},
|
|
19
|
+
markClean: () => binding.markClean(),
|
|
20
|
+
suppress: (fn) => binding.suppress(fn),
|
|
21
|
+
refresh: () => binding.refresh(),
|
|
22
|
+
get tracker() {
|
|
23
|
+
return binding.tracker;
|
|
24
|
+
},
|
|
25
|
+
destroy() {
|
|
26
|
+
unsubscribe();
|
|
27
|
+
binding.destroy();
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
}
|
package/dist/history/index.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/history/index.ts"],"names":[],"mappings":"AAAA,cAAc,qBAAqB,CAAC;AACpC,cAAc,6BAA6B,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/history/index.ts"],"names":[],"mappings":"AAAA,cAAc,qBAAqB,CAAC;AACpC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,0BAA0B,CAAC"}
|
package/dist/history/index.js
CHANGED
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type {
|
|
3
|
+
AdjustmentKind,
|
|
4
|
+
AdjustmentLayer,
|
|
5
|
+
BlendMode,
|
|
6
|
+
ControlSpec,
|
|
7
|
+
CurvePoint,
|
|
8
|
+
ImageEditorController,
|
|
9
|
+
ImageHistogram,
|
|
10
|
+
} from '@pixerate/editor/image-editor';
|
|
11
|
+
import {
|
|
12
|
+
ADJUSTMENT_KINDS,
|
|
13
|
+
ADJUSTMENT_LAYER_CONTROLS,
|
|
14
|
+
ADJUSTMENT_LAYER_OPACITY_CONTROL,
|
|
15
|
+
BLEND_MODES,
|
|
16
|
+
HUE_RANGE_LABELS,
|
|
17
|
+
adjustmentKindLabel,
|
|
18
|
+
getControlPath,
|
|
19
|
+
hueRangeWeight,
|
|
20
|
+
setControlPath,
|
|
21
|
+
} from '@pixerate/editor/image-editor';
|
|
22
|
+
import { untrack } from 'svelte';
|
|
23
|
+
import ControlSlider from './ControlSlider.svelte';
|
|
24
|
+
import CurvesEditor from './CurvesEditor.svelte';
|
|
25
|
+
|
|
26
|
+
interface Props {
|
|
27
|
+
controller: ImageEditorController;
|
|
28
|
+
/** The current layers (`state.adjustments.layers`), bottom to top. Omit to follow the controller. */
|
|
29
|
+
layers?: AdjustmentLayer[];
|
|
30
|
+
histogram?: ImageHistogram | null;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
let { controller, layers: layersProp, histogram = null }: Props = $props();
|
|
34
|
+
|
|
35
|
+
// Follows the controller when no `layers` prop is given.
|
|
36
|
+
let tick = $state(0);
|
|
37
|
+
$effect(() => {
|
|
38
|
+
const c = controller;
|
|
39
|
+
// `subscribe` calls the listener at once: untracked, or the effect would depend on `tick`.
|
|
40
|
+
return untrack(() => c.subscribe(() => tick++));
|
|
41
|
+
});
|
|
42
|
+
const layers = $derived.by(() => {
|
|
43
|
+
void tick;
|
|
44
|
+
return layersProp ?? controller.getAdjustmentLayers();
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
type Channel = 'master' | 'red' | 'green' | 'blue';
|
|
48
|
+
const CHANNELS: { id: Channel; label: string }[] = [
|
|
49
|
+
{ id: 'master', label: 'RGB' },
|
|
50
|
+
{ id: 'red', label: 'Red' },
|
|
51
|
+
{ id: 'green', label: 'Green' },
|
|
52
|
+
{ id: 'blue', label: 'Blue' },
|
|
53
|
+
];
|
|
54
|
+
const TONES = [
|
|
55
|
+
{ id: 'shadows', label: 'Shadows' },
|
|
56
|
+
{ id: 'midtones', label: 'Midtones' },
|
|
57
|
+
{ id: 'highlights', label: 'Highlights' },
|
|
58
|
+
] as const;
|
|
59
|
+
type Tone = (typeof TONES)[number]['id'];
|
|
60
|
+
|
|
61
|
+
let newKind: AdjustmentKind = $state('curves');
|
|
62
|
+
let selectedId: string | null = $state(null);
|
|
63
|
+
let channel: Channel = $state('master');
|
|
64
|
+
let range = $state(-1);
|
|
65
|
+
let tone: Tone = $state('midtones');
|
|
66
|
+
|
|
67
|
+
const selected = $derived(layers.find((l) => l.id === selectedId) ?? null);
|
|
68
|
+
const topFirst = $derived([...layers].reverse());
|
|
69
|
+
|
|
70
|
+
const selectClass = 'bg-slate-800 border border-slate-700 rounded px-1.5 py-1 text-slate-200';
|
|
71
|
+
const chipClass = (on: boolean) =>
|
|
72
|
+
`px-2 py-0.5 rounded text-[10px] ${on ? 'bg-indigo-600 text-white' : 'bg-slate-800 text-slate-400 hover:text-slate-200'}`;
|
|
73
|
+
|
|
74
|
+
function setParam(layer: AdjustmentLayer, path: string, value: unknown, commit = true) {
|
|
75
|
+
controller.updateAdjustmentLayer(layer.id, { params: setControlPath(layer.params, path, value) as never }, commit);
|
|
76
|
+
}
|
|
77
|
+
const paramValue = (layer: AdjustmentLayer, spec: ControlSpec) => Number(getControlPath(layer.params, spec.id) ?? spec.default);
|
|
78
|
+
|
|
79
|
+
function visibleControls(layer: AdjustmentLayer): ControlSpec[] {
|
|
80
|
+
const all = ADJUSTMENT_LAYER_CONTROLS[layer.kind];
|
|
81
|
+
switch (layer.kind) {
|
|
82
|
+
case 'levels':
|
|
83
|
+
return all.filter((s) => s.id.startsWith(`${channel}.`));
|
|
84
|
+
case 'hueSaturation':
|
|
85
|
+
return all.filter((s) =>
|
|
86
|
+
range === -1 || layer.params.colorize ? !s.id.startsWith('ranges.') : s.id.startsWith(`ranges.${range}.`)
|
|
87
|
+
);
|
|
88
|
+
case 'colorBalance':
|
|
89
|
+
return all.filter((s) => s.id.startsWith(`${tone}.`));
|
|
90
|
+
default:
|
|
91
|
+
return [...all];
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function stripPoints(bounds: [number, number, number, number]) {
|
|
96
|
+
return Array.from({ length: 73 }, (_, i) => `${(i / 72) * 360},${20 - hueRangeWeight({ bounds }, i * 5) * 18}`).join(' ');
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function addLayer() {
|
|
100
|
+
selectedId = controller.addAdjustmentLayer(newKind);
|
|
101
|
+
channel = 'master';
|
|
102
|
+
range = -1;
|
|
103
|
+
}
|
|
104
|
+
</script>
|
|
105
|
+
|
|
106
|
+
<div class="flex flex-col gap-2 text-xs" data-testid="adjustment-layers-panel">
|
|
107
|
+
<div class="flex items-center justify-between">
|
|
108
|
+
<span class="font-semibold text-slate-200 uppercase tracking-wider text-[11px]">Adjustment Layers</span>
|
|
109
|
+
</div>
|
|
110
|
+
<div class="flex gap-1.5">
|
|
111
|
+
<select aria-label="New adjustment layer kind" class="{selectClass} flex-1" bind:value={newKind}>
|
|
112
|
+
{#each ADJUSTMENT_KINDS as k (k.kind)}
|
|
113
|
+
<option value={k.kind}>{k.label}</option>
|
|
114
|
+
{/each}
|
|
115
|
+
</select>
|
|
116
|
+
<button type="button" class="px-2 py-1 rounded bg-indigo-600 text-white hover:bg-indigo-500" onclick={addLayer}>
|
|
117
|
+
Add Layer
|
|
118
|
+
</button>
|
|
119
|
+
</div>
|
|
120
|
+
|
|
121
|
+
{#if topFirst.length > 0}
|
|
122
|
+
<ul class="flex flex-col gap-1" aria-label="Adjustment layers">
|
|
123
|
+
{#each topFirst as layer (layer.id)}
|
|
124
|
+
{@const index = layers.indexOf(layer)}
|
|
125
|
+
{@const label = layer.name || adjustmentKindLabel(layer.kind)}
|
|
126
|
+
<li
|
|
127
|
+
class="flex items-center gap-1.5 rounded px-1.5 py-1 {layer.id === selectedId
|
|
128
|
+
? 'bg-indigo-600/20 ring-1 ring-indigo-500/50'
|
|
129
|
+
: 'bg-slate-800/60'}"
|
|
130
|
+
>
|
|
131
|
+
<input
|
|
132
|
+
type="checkbox"
|
|
133
|
+
aria-label="Show {label}"
|
|
134
|
+
checked={layer.enabled}
|
|
135
|
+
onchange={() => controller.toggleAdjustmentLayer(layer.id)}
|
|
136
|
+
class="accent-indigo-500"
|
|
137
|
+
/>
|
|
138
|
+
<button
|
|
139
|
+
type="button"
|
|
140
|
+
class="flex-1 text-left truncate {layer.enabled ? 'text-slate-200' : 'text-slate-500 line-through'}"
|
|
141
|
+
aria-pressed={layer.id === selectedId}
|
|
142
|
+
onclick={() => (selectedId = layer.id === selectedId ? null : layer.id)}
|
|
143
|
+
>
|
|
144
|
+
{label}
|
|
145
|
+
</button>
|
|
146
|
+
<button
|
|
147
|
+
type="button"
|
|
148
|
+
aria-label="Move {label} up"
|
|
149
|
+
disabled={index === layers.length - 1}
|
|
150
|
+
onclick={() => controller.moveAdjustmentLayer(layer.id, index + 1)}
|
|
151
|
+
class="px-1 text-slate-400 hover:text-slate-200 disabled:opacity-30"
|
|
152
|
+
>
|
|
153
|
+
↑
|
|
154
|
+
</button>
|
|
155
|
+
<button
|
|
156
|
+
type="button"
|
|
157
|
+
aria-label="Move {label} down"
|
|
158
|
+
disabled={index === 0}
|
|
159
|
+
onclick={() => controller.moveAdjustmentLayer(layer.id, index - 1)}
|
|
160
|
+
class="px-1 text-slate-400 hover:text-slate-200 disabled:opacity-30"
|
|
161
|
+
>
|
|
162
|
+
↓
|
|
163
|
+
</button>
|
|
164
|
+
<button
|
|
165
|
+
type="button"
|
|
166
|
+
aria-label="Remove {label}"
|
|
167
|
+
onclick={() => {
|
|
168
|
+
controller.removeAdjustmentLayer(layer.id);
|
|
169
|
+
if (selectedId === layer.id) selectedId = null;
|
|
170
|
+
}}
|
|
171
|
+
class="px-1 text-rose-400 hover:text-rose-300"
|
|
172
|
+
>
|
|
173
|
+
✕
|
|
174
|
+
</button>
|
|
175
|
+
</li>
|
|
176
|
+
{/each}
|
|
177
|
+
</ul>
|
|
178
|
+
{/if}
|
|
179
|
+
|
|
180
|
+
{#if selected}
|
|
181
|
+
{@const layer = selected}
|
|
182
|
+
<div class="flex flex-col gap-2 rounded border border-slate-700/60 p-2" data-testid="adjustment-layer-editor">
|
|
183
|
+
<div class="flex items-center gap-2">
|
|
184
|
+
<span class="text-slate-400">Blend</span>
|
|
185
|
+
<select
|
|
186
|
+
aria-label="Blend mode"
|
|
187
|
+
class="{selectClass} flex-1"
|
|
188
|
+
value={layer.blend}
|
|
189
|
+
onchange={(e) => controller.updateAdjustmentLayer(layer.id, { blend: (e.target as HTMLSelectElement).value as BlendMode })}
|
|
190
|
+
>
|
|
191
|
+
{#each BLEND_MODES as m (m.id)}
|
|
192
|
+
<option value={m.id}>{m.label}</option>
|
|
193
|
+
{/each}
|
|
194
|
+
</select>
|
|
195
|
+
</div>
|
|
196
|
+
<ControlSlider
|
|
197
|
+
spec={ADJUSTMENT_LAYER_OPACITY_CONTROL}
|
|
198
|
+
label="Layer opacity"
|
|
199
|
+
value={layer.opacity}
|
|
200
|
+
onchange={(v) => controller.updateAdjustmentLayer(layer.id, { opacity: v })}
|
|
201
|
+
/>
|
|
202
|
+
|
|
203
|
+
{#if layer.kind === 'curves' || layer.kind === 'levels'}
|
|
204
|
+
<div class="flex gap-1" role="group" aria-label={layer.kind === 'curves' ? 'Curve channel' : 'Levels channel'}>
|
|
205
|
+
{#each CHANNELS as c (c.id)}
|
|
206
|
+
<button type="button" class={chipClass(channel === c.id)} onclick={() => (channel = c.id)}>{c.label}</button>
|
|
207
|
+
{/each}
|
|
208
|
+
</div>
|
|
209
|
+
{/if}
|
|
210
|
+
|
|
211
|
+
{#if layer.kind === 'curves'}
|
|
212
|
+
<CurvesEditor
|
|
213
|
+
points={layer.params[channel]}
|
|
214
|
+
{channel}
|
|
215
|
+
{histogram}
|
|
216
|
+
onchange={(points: CurvePoint[], commit: boolean) => setParam(layer, channel, points, commit)}
|
|
217
|
+
/>
|
|
218
|
+
{:else if layer.kind === 'hueSaturation'}
|
|
219
|
+
<div class="flex flex-wrap gap-1" role="group" aria-label="Hue range">
|
|
220
|
+
<button type="button" class={chipClass(range === -1)} onclick={() => (range = -1)}>Master</button>
|
|
221
|
+
{#each HUE_RANGE_LABELS as name, i (name)}
|
|
222
|
+
<button type="button" class={chipClass(range === i)} disabled={layer.params.colorize} onclick={() => (range = i)}>
|
|
223
|
+
{name}
|
|
224
|
+
</button>
|
|
225
|
+
{/each}
|
|
226
|
+
</div>
|
|
227
|
+
{#if range >= 0 && !layer.params.colorize}
|
|
228
|
+
<svg viewBox="0 0 360 20" preserveAspectRatio="none" class="w-full h-4 rounded" data-testid="hue-range-strip">
|
|
229
|
+
<defs>
|
|
230
|
+
<linearGradient id="pixerate-hue-strip-svelte">
|
|
231
|
+
{#each [0, 60, 120, 180, 240, 300, 360] as h (h)}
|
|
232
|
+
<stop offset="{(h / 360) * 100}%" stop-color="hsl({h}, 90%, 50%)" />
|
|
233
|
+
{/each}
|
|
234
|
+
</linearGradient>
|
|
235
|
+
</defs>
|
|
236
|
+
<rect width="360" height="20" fill="url(#pixerate-hue-strip-svelte)" opacity="0.6" />
|
|
237
|
+
<polyline points={stripPoints(layer.params.ranges[range].bounds)} fill="none" stroke="#fff" stroke-width="1.5" />
|
|
238
|
+
</svg>
|
|
239
|
+
{/if}
|
|
240
|
+
{:else if layer.kind === 'colorBalance'}
|
|
241
|
+
<div class="flex gap-1" role="group" aria-label="Tone">
|
|
242
|
+
{#each TONES as t (t.id)}
|
|
243
|
+
<button type="button" class={chipClass(tone === t.id)} onclick={() => (tone = t.id)}>{t.label}</button>
|
|
244
|
+
{/each}
|
|
245
|
+
</div>
|
|
246
|
+
{:else if layer.kind === 'photoFilter'}
|
|
247
|
+
<label class="flex items-center gap-2 text-slate-300">
|
|
248
|
+
Filter colour
|
|
249
|
+
<input
|
|
250
|
+
type="color"
|
|
251
|
+
aria-label="Filter colour"
|
|
252
|
+
value={layer.params.color}
|
|
253
|
+
oninput={(e) => setParam(layer, 'color', (e.target as HTMLInputElement).value)}
|
|
254
|
+
class="h-5 w-8 bg-transparent"
|
|
255
|
+
/>
|
|
256
|
+
</label>
|
|
257
|
+
{:else if layer.kind === 'invert'}
|
|
258
|
+
<p class="text-slate-500 text-[11px]">Invert has no settings.</p>
|
|
259
|
+
{/if}
|
|
260
|
+
|
|
261
|
+
{#each visibleControls(layer) as spec (spec.id)}
|
|
262
|
+
<ControlSlider {spec} value={paramValue(layer, spec)} onchange={(v) => setParam(layer, spec.id, v)} />
|
|
263
|
+
{/each}
|
|
264
|
+
|
|
265
|
+
{#if layer.kind === 'hueSaturation' && range === -1}
|
|
266
|
+
<label class="flex items-center gap-2 text-slate-300">
|
|
267
|
+
<input
|
|
268
|
+
type="checkbox"
|
|
269
|
+
checked={layer.params.colorize}
|
|
270
|
+
onchange={(e) => setParam(layer, 'colorize', (e.target as HTMLInputElement).checked)}
|
|
271
|
+
class="accent-indigo-500"
|
|
272
|
+
/>
|
|
273
|
+
Colorize
|
|
274
|
+
</label>
|
|
275
|
+
{/if}
|
|
276
|
+
{#if layer.kind === 'colorBalance' || layer.kind === 'photoFilter'}
|
|
277
|
+
<label class="flex items-center gap-2 text-slate-300">
|
|
278
|
+
<input
|
|
279
|
+
type="checkbox"
|
|
280
|
+
checked={layer.params.preserveLuminosity}
|
|
281
|
+
onchange={(e) => setParam(layer, 'preserveLuminosity', (e.target as HTMLInputElement).checked)}
|
|
282
|
+
class="accent-indigo-500"
|
|
283
|
+
/>
|
|
284
|
+
Preserve luminosity
|
|
285
|
+
</label>
|
|
286
|
+
{/if}
|
|
287
|
+
{#if layer.kind === 'blackWhite'}
|
|
288
|
+
<label class="flex items-center gap-2 text-slate-300">
|
|
289
|
+
<input
|
|
290
|
+
type="checkbox"
|
|
291
|
+
checked={layer.params.tint !== null}
|
|
292
|
+
onchange={(e) => setParam(layer, 'tint', (e.target as HTMLInputElement).checked ? '#e1d3b3' : null)}
|
|
293
|
+
class="accent-indigo-500"
|
|
294
|
+
/>
|
|
295
|
+
Tint
|
|
296
|
+
{#if layer.params.tint}
|
|
297
|
+
<input
|
|
298
|
+
type="color"
|
|
299
|
+
aria-label="Tint colour"
|
|
300
|
+
value={layer.params.tint}
|
|
301
|
+
oninput={(e) => setParam(layer, 'tint', (e.target as HTMLInputElement).value)}
|
|
302
|
+
class="h-5 w-8 bg-transparent"
|
|
303
|
+
/>
|
|
304
|
+
{/if}
|
|
305
|
+
</label>
|
|
306
|
+
{/if}
|
|
307
|
+
</div>
|
|
308
|
+
{/if}
|
|
309
|
+
</div>
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { AdjustmentLayer, ImageEditorController, ImageHistogram } from '@pixerate/editor/image-editor';
|
|
2
|
+
interface Props {
|
|
3
|
+
controller: ImageEditorController;
|
|
4
|
+
/** The current layers (`state.adjustments.layers`), bottom to top. Omit to follow the controller. */
|
|
5
|
+
layers?: AdjustmentLayer[];
|
|
6
|
+
histogram?: ImageHistogram | null;
|
|
7
|
+
}
|
|
8
|
+
declare const AdjustmentLayersPanel: import("svelte").Component<Props, {}, "">;
|
|
9
|
+
type AdjustmentLayersPanel = ReturnType<typeof AdjustmentLayersPanel>;
|
|
10
|
+
export default AdjustmentLayersPanel;
|
|
11
|
+
//# sourceMappingURL=AdjustmentLayersPanel.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AdjustmentLayersPanel.svelte.d.ts","sourceRoot":"","sources":["../../src/image-editor/AdjustmentLayersPanel.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAER,eAAe,EAIf,qBAAqB,EACrB,cAAc,EACf,MAAM,+BAA+B,CAAC;AAiBvC,UAAU,KAAK;IACb,UAAU,EAAE,qBAAqB,CAAC;IAClC,qGAAqG;IACrG,MAAM,CAAC,EAAE,eAAe,EAAE,CAAC;IAC3B,SAAS,CAAC,EAAE,cAAc,GAAG,IAAI,CAAC;CACnC;AA0NH,QAAA,MAAM,qBAAqB,2CAAwC,CAAC;AACpE,KAAK,qBAAqB,GAAG,UAAU,CAAC,OAAO,qBAAqB,CAAC,CAAC;AACtE,eAAe,qBAAqB,CAAC"}
|