@pixerate/editor-svelte 0.22.0 → 0.24.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 -2
- 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/StylesGallery.svelte +169 -0
- package/dist/StylesGallery.svelte.d.ts +27 -0
- package/dist/StylesGallery.svelte.d.ts.map +1 -0
- package/dist/TemplateRenderer.svelte +4 -10
- package/dist/TemplateRenderer.svelte.d.ts.map +1 -1
- package/dist/canvas/TokenBindingPicker.svelte +93 -0
- package/dist/canvas/TokenBindingPicker.svelte.d.ts +20 -0
- package/dist/canvas/TokenBindingPicker.svelte.d.ts.map +1 -0
- package/dist/canvas/TokenModeSwitcher.svelte +55 -0
- package/dist/canvas/TokenModeSwitcher.svelte.d.ts +18 -0
- package/dist/canvas/TokenModeSwitcher.svelte.d.ts.map +1 -0
- package/dist/canvas/createCanvasArrange.svelte.d.ts +4 -0
- package/dist/canvas/createCanvasArrange.svelte.d.ts.map +1 -1
- 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/createCanvasTokens.svelte.d.ts +31 -0
- package/dist/canvas/createCanvasTokens.svelte.d.ts.map +1 -0
- package/dist/canvas/createCanvasTokens.svelte.js +50 -0
- package/dist/canvas/index.d.ts +7 -0
- package/dist/canvas/index.d.ts.map +1 -1
- package/dist/canvas/index.js +10 -0
- package/dist/fileImport.svelte.d.ts +8 -1
- package/dist/fileImport.svelte.d.ts.map +1 -1
- package/dist/fileImport.svelte.js +11 -1
- 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/AssetMentionChips.svelte +130 -0
- package/dist/generation/AssetMentionChips.svelte.d.ts +21 -0
- package/dist/generation/AssetMentionChips.svelte.d.ts.map +1 -0
- package/dist/generation/GenerationHistory.svelte +161 -0
- package/dist/generation/GenerationHistory.svelte.d.ts +28 -0
- package/dist/generation/GenerationHistory.svelte.d.ts.map +1 -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 +17 -0
- package/dist/generation/index.d.ts.map +1 -0
- package/dist/generation/index.js +7 -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/generation/records.svelte.d.ts +18 -0
- package/dist/generation/records.svelte.d.ts.map +1 -0
- package/dist/generation/records.svelte.js +66 -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 +104 -141
- package/dist/image-editor/ImageEditor.svelte.d.ts.map +1 -1
- package/dist/image-editor/RefineEdgePanel.svelte +180 -0
- package/dist/image-editor/RefineEdgePanel.svelte.d.ts +13 -0
- package/dist/image-editor/RefineEdgePanel.svelte.d.ts.map +1 -0
- package/dist/image-editor/SelectionOptionsBar.svelte +135 -0
- package/dist/image-editor/SelectionOptionsBar.svelte.d.ts +15 -0
- package/dist/image-editor/SelectionOptionsBar.svelte.d.ts.map +1 -0
- package/dist/image-editor/imageEditorState.svelte.d.ts +14 -1
- package/dist/image-editor/imageEditorState.svelte.d.ts.map +1 -1
- package/dist/image-editor/imageEditorState.svelte.js +40 -0
- package/dist/image-editor/index.d.ts +18 -0
- package/dist/image-editor/index.d.ts.map +1 -1
- package/dist/image-editor/index.js +6 -0
- package/dist/index.d.ts +12 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -0
- 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/paragraphStyles.svelte.d.ts +30 -0
- package/dist/paragraphStyles.svelte.d.ts.map +1 -0
- package/dist/paragraphStyles.svelte.js +44 -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/TrackedChangesList.svelte +67 -0
- package/dist/review/TrackedChangesList.svelte.d.ts +13 -0
- package/dist/review/TrackedChangesList.svelte.d.ts.map +1 -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 +4 -0
- package/dist/review/index.d.ts.map +1 -1
- package/dist/review/index.js +2 -0
- package/dist/review/review.svelte.d.ts +6 -0
- package/dist/review/review.svelte.d.ts.map +1 -1
- package/dist/review/review.svelte.js +3 -0
- package/dist/spreadsheet/FormulaBar.svelte +13 -4
- package/dist/spreadsheet/FormulaBar.svelte.d.ts.map +1 -1
- package/dist/spreadsheet/FormulaHints.svelte +7 -5
- package/dist/spreadsheet/FormulaHints.svelte.d.ts +3 -0
- package/dist/spreadsheet/FormulaHints.svelte.d.ts.map +1 -1
- package/dist/spreadsheet/NameManager.svelte +165 -0
- package/dist/spreadsheet/NameManager.svelte.d.ts +11 -0
- package/dist/spreadsheet/NameManager.svelte.d.ts.map +1 -0
- package/dist/spreadsheet/SheetTabs.svelte +161 -0
- package/dist/spreadsheet/SheetTabs.svelte.d.ts +10 -0
- package/dist/spreadsheet/SheetTabs.svelte.d.ts.map +1 -0
- package/dist/spreadsheet/SpreadsheetConditionalFormatPanel.svelte +184 -0
- package/dist/spreadsheet/SpreadsheetConditionalFormatPanel.svelte.d.ts +9 -0
- package/dist/spreadsheet/SpreadsheetConditionalFormatPanel.svelte.d.ts.map +1 -0
- package/dist/spreadsheet/SpreadsheetContextMenu.svelte +13 -5
- package/dist/spreadsheet/SpreadsheetContextMenu.svelte.d.ts +5 -1
- package/dist/spreadsheet/SpreadsheetContextMenu.svelte.d.ts.map +1 -1
- package/dist/spreadsheet/SpreadsheetDataValidationPanel.svelte +141 -0
- package/dist/spreadsheet/SpreadsheetDataValidationPanel.svelte.d.ts +9 -0
- package/dist/spreadsheet/SpreadsheetDataValidationPanel.svelte.d.ts.map +1 -0
- package/dist/spreadsheet/SpreadsheetEditor.svelte +199 -11
- package/dist/spreadsheet/SpreadsheetEditor.svelte.d.ts +2 -0
- package/dist/spreadsheet/SpreadsheetEditor.svelte.d.ts.map +1 -1
- package/dist/spreadsheet/SpreadsheetValidationDialog.svelte +92 -0
- package/dist/spreadsheet/SpreadsheetValidationDialog.svelte.d.ts +9 -0
- package/dist/spreadsheet/SpreadsheetValidationDialog.svelte.d.ts.map +1 -0
- package/dist/spreadsheet/index.d.ts +9 -0
- package/dist/spreadsheet/index.d.ts.map +1 -1
- package/dist/spreadsheet/index.js +5 -0
- package/dist/spreadsheet/spreadsheet.svelte.d.ts +57 -2
- package/dist/spreadsheet/spreadsheet.svelte.d.ts.map +1 -1
- package/dist/spreadsheet/spreadsheet.svelte.js +105 -6
- package/dist/tokens/TokenSwatch.svelte +14 -0
- package/dist/tokens/TokenSwatch.svelte.d.ts +8 -0
- package/dist/tokens/TokenSwatch.svelte.d.ts.map +1 -0
- package/dist/tokens/TokensPanel.svelte +323 -0
- package/dist/tokens/TokensPanel.svelte.d.ts +14 -0
- package/dist/tokens/TokensPanel.svelte.d.ts.map +1 -0
- package/dist/tokens/createTokens.svelte.d.ts +31 -0
- package/dist/tokens/createTokens.svelte.d.ts.map +1 -0
- package/dist/tokens/createTokens.svelte.js +43 -0
- package/dist/tokens/index.d.ts +7 -0
- package/dist/tokens/index.d.ts.map +1 -0
- package/dist/tokens/index.js +3 -0
- package/package.json +26 -2
|
@@ -18,7 +18,17 @@
|
|
|
18
18
|
hitTestAnnotation,
|
|
19
19
|
normalizeProcessVersion,
|
|
20
20
|
LATEST_PROCESS_VERSION,
|
|
21
|
+
IMAGE_ADJUSTMENT_CONTROLS,
|
|
22
|
+
readAdjustmentControls,
|
|
23
|
+
DEFAULT_SELECTION_TOOL_OPTIONS,
|
|
24
|
+
SelectionGesture,
|
|
25
|
+
type SelectionToolOptions,
|
|
21
26
|
} from '@pixerate/editor/image-editor';
|
|
27
|
+
import ControlSlider from './ControlSlider.svelte';
|
|
28
|
+
import Histogram from './Histogram.svelte';
|
|
29
|
+
import AdjustmentLayersPanel from './AdjustmentLayersPanel.svelte';
|
|
30
|
+
import SelectionOptionsBar from './SelectionOptionsBar.svelte';
|
|
31
|
+
import RefineEdgePanel from './RefineEdgePanel.svelte';
|
|
22
32
|
import { onDestroy, untrack } from 'svelte';
|
|
23
33
|
import { createReactiveImageEditor, type ReactiveImageEditor } from './imageEditorState.svelte.js';
|
|
24
34
|
|
|
@@ -55,7 +65,13 @@
|
|
|
55
65
|
|
|
56
66
|
let canvasEl = $state<HTMLCanvasElement | null>(null);
|
|
57
67
|
let containerEl = $state<HTMLDivElement | null>(null);
|
|
58
|
-
let activeTab = $state<'crop' | 'adjust' | 'annotate' | 'depth' | 'ai'>('crop');
|
|
68
|
+
let activeTab = $state<'crop' | 'adjust' | 'select' | 'annotate' | 'depth' | 'ai'>('crop');
|
|
69
|
+
|
|
70
|
+
// Selection tools (magic wand, marquees, lasso): options bar state and the pointer gesture.
|
|
71
|
+
let selectionOptions = $state<SelectionToolOptions>({ ...DEFAULT_SELECTION_TOOL_OPTIONS });
|
|
72
|
+
const selectionGesture = $derived(new SelectionGesture(editor.controller, () => selectionOptions));
|
|
73
|
+
let selectionDraft = $state.raw<ImagePoint[] | null>(null);
|
|
74
|
+
let selectionError = $state<string | null>(null);
|
|
59
75
|
let activeCropRatio = $state<number | null>(null);
|
|
60
76
|
|
|
61
77
|
// AI state
|
|
@@ -182,6 +198,7 @@
|
|
|
182
198
|
interactive: true,
|
|
183
199
|
draftAnnotation: overrideAnnotation !== undefined ? overrideAnnotation : draftAnnotation,
|
|
184
200
|
draftCrop: overrideCrop !== undefined ? overrideCrop : draftCrop,
|
|
201
|
+
draftSelectionPath: selectionDraft,
|
|
185
202
|
});
|
|
186
203
|
}
|
|
187
204
|
|
|
@@ -239,6 +256,13 @@
|
|
|
239
256
|
if (!canvasEl) return;
|
|
240
257
|
const canvasPt = getCanvasEventPoint(e);
|
|
241
258
|
|
|
259
|
+
if (activeTab === 'select') {
|
|
260
|
+
const gesture = selectionGesture;
|
|
261
|
+
const imgPt = canvasToImagePoint(canvasPt, editor.state, canvasEl.width, canvasEl.height, false);
|
|
262
|
+
void gesture.begin(imgPt, e).then(() => (selectionError = gesture.error));
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
|
|
242
266
|
if (activeTab === 'crop') {
|
|
243
267
|
const activeCrop = draftCrop || editor.state.crop || {
|
|
244
268
|
x: 0,
|
|
@@ -311,6 +335,15 @@
|
|
|
311
335
|
if (!canvasEl) return;
|
|
312
336
|
const canvasPt = getCanvasEventPoint(e);
|
|
313
337
|
|
|
338
|
+
if (activeTab === 'select') {
|
|
339
|
+
const gesture = selectionGesture;
|
|
340
|
+
if (gesture.active && gesture.move(canvasToImagePoint(canvasPt, editor.state, canvasEl.width, canvasEl.height, false))) {
|
|
341
|
+
selectionDraft = gesture.draftPath;
|
|
342
|
+
renderCanvas();
|
|
343
|
+
}
|
|
344
|
+
return;
|
|
345
|
+
}
|
|
346
|
+
|
|
314
347
|
if (activeTab === 'crop') {
|
|
315
348
|
if (cropDragInfo) {
|
|
316
349
|
const currentImgPt = canvasToImagePoint(canvasPt, editor.state, canvasEl.width, canvasEl.height, true);
|
|
@@ -463,6 +496,16 @@
|
|
|
463
496
|
if (!canvasEl) return;
|
|
464
497
|
const canvasPt = getCanvasEventPoint(e);
|
|
465
498
|
|
|
499
|
+
if (activeTab === 'select') {
|
|
500
|
+
const gesture = selectionGesture;
|
|
501
|
+
if (gesture.active) {
|
|
502
|
+
gesture.end(canvasToImagePoint(canvasPt, editor.state, canvasEl.width, canvasEl.height, false), e);
|
|
503
|
+
selectionDraft = null;
|
|
504
|
+
renderCanvas();
|
|
505
|
+
}
|
|
506
|
+
return;
|
|
507
|
+
}
|
|
508
|
+
|
|
466
509
|
if (activeTab === 'crop') {
|
|
467
510
|
if (cropDragInfo) {
|
|
468
511
|
if (draftCrop && draftCrop.width >= 20 && draftCrop.height >= 20) {
|
|
@@ -616,6 +659,18 @@
|
|
|
616
659
|
>
|
|
617
660
|
Adjustments
|
|
618
661
|
</button>
|
|
662
|
+
<button
|
|
663
|
+
type="button"
|
|
664
|
+
onclick={() => {
|
|
665
|
+
activeTab = 'select';
|
|
666
|
+
editor.setTool('select');
|
|
667
|
+
}}
|
|
668
|
+
class="px-3 py-1.5 rounded-md font-medium transition-colors {activeTab === 'select'
|
|
669
|
+
? 'bg-indigo-600 text-white shadow'
|
|
670
|
+
: 'text-slate-400 hover:text-slate-200'}"
|
|
671
|
+
>
|
|
672
|
+
Select & Mask
|
|
673
|
+
</button>
|
|
619
674
|
<button
|
|
620
675
|
type="button"
|
|
621
676
|
onclick={() => {
|
|
@@ -886,150 +941,37 @@
|
|
|
886
941
|
</button>
|
|
887
942
|
</div>
|
|
888
943
|
|
|
889
|
-
|
|
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>
|
|
952
|
-
|
|
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"
|
|
966
|
-
/>
|
|
967
|
-
</div>
|
|
944
|
+
<Histogram histogram={editor.histogram} />
|
|
968
945
|
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
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"
|
|
946
|
+
{#each IMAGE_ADJUSTMENT_CONTROLS as spec (spec.id)}
|
|
947
|
+
<ControlSlider
|
|
948
|
+
{spec}
|
|
949
|
+
value={readAdjustmentControls(editor.state.adjustments)[spec.id]}
|
|
950
|
+
disabled={spec.requiresProcess !== undefined && legacyProcess}
|
|
951
|
+
onchange={(v) => editor.setAdjustments({ [spec.id]: v })}
|
|
984
952
|
/>
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
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"
|
|
953
|
+
{#if spec.id === 'tint' && legacyProcess}
|
|
954
|
+
<div
|
|
955
|
+
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"
|
|
956
|
+
data-testid="image-editor-process-notice"
|
|
997
957
|
>
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
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>
|
|
958
|
+
<span>Legacy rendering (process 1). Tint needs the current process.</span>
|
|
959
|
+
<button
|
|
960
|
+
type="button"
|
|
961
|
+
onclick={() => editor.upgradeProcess()}
|
|
962
|
+
class="shrink-0 rounded bg-amber-500/20 px-2 py-0.5 hover:bg-amber-500/30"
|
|
963
|
+
>
|
|
964
|
+
Update
|
|
965
|
+
</button>
|
|
966
|
+
</div>
|
|
967
|
+
{/if}
|
|
968
|
+
{/each}
|
|
1018
969
|
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
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"
|
|
970
|
+
<div class="border-t border-slate-700/60 pt-3">
|
|
971
|
+
<AdjustmentLayersPanel
|
|
972
|
+
controller={editor.controller}
|
|
973
|
+
layers={editor.state.adjustments.layers}
|
|
974
|
+
histogram={editor.histogram}
|
|
1033
975
|
/>
|
|
1034
976
|
</div>
|
|
1035
977
|
</div>
|
|
@@ -1261,6 +1203,27 @@
|
|
|
1261
1203
|
</div>
|
|
1262
1204
|
{/if}
|
|
1263
1205
|
|
|
1206
|
+
<!-- SELECT & MASK: selection tools, refine edge, layer masks -->
|
|
1207
|
+
{#if activeTab === 'select'}
|
|
1208
|
+
<div class="flex flex-col gap-4 text-xs">
|
|
1209
|
+
<SelectionOptionsBar
|
|
1210
|
+
controller={editor.controller}
|
|
1211
|
+
options={selectionOptions}
|
|
1212
|
+
onOptionsChange={(next) => (selectionOptions = next)}
|
|
1213
|
+
selection={editor.state.selection ?? null}
|
|
1214
|
+
quickMask={Boolean(editor.state.quickMask)}
|
|
1215
|
+
/>
|
|
1216
|
+
{#if selectionError}
|
|
1217
|
+
<p role="alert" class="text-rose-400">{selectionError}</p>
|
|
1218
|
+
{/if}
|
|
1219
|
+
<RefineEdgePanel
|
|
1220
|
+
controller={editor.controller}
|
|
1221
|
+
selection={editor.state.selection ?? null}
|
|
1222
|
+
layers={editor.state.adjustments.layers}
|
|
1223
|
+
/>
|
|
1224
|
+
</div>
|
|
1225
|
+
{/if}
|
|
1226
|
+
|
|
1264
1227
|
<!-- TAB 5: AI TOOLS / INPAINTING & BACKGROUND REMOVAL -->
|
|
1265
1228
|
{#if activeTab === 'ai'}
|
|
1266
1229
|
<div class="flex flex-col gap-4 text-xs">
|
|
@@ -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;
|
|
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;AAqBzC,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;AA8iCH,QAAA,MAAM,WAAW,2CAAwC,CAAC;AAC1D,KAAK,WAAW,GAAG,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC;AAClD,eAAe,WAAW,CAAC"}
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type {
|
|
3
|
+
AdjustmentKind,
|
|
4
|
+
AdjustmentLayer,
|
|
5
|
+
ImageEditorController,
|
|
6
|
+
RefineEdgeParams,
|
|
7
|
+
SelectionMask,
|
|
8
|
+
} from '@pixerate/editor/image-editor';
|
|
9
|
+
import {
|
|
10
|
+
ADJUSTMENT_KINDS,
|
|
11
|
+
REFINE_EDGE_CONTROLS,
|
|
12
|
+
adjustmentKindLabel,
|
|
13
|
+
layerMaskFromSelection,
|
|
14
|
+
} from '@pixerate/editor/image-editor';
|
|
15
|
+
import { untrack } from 'svelte';
|
|
16
|
+
|
|
17
|
+
interface Props {
|
|
18
|
+
controller: ImageEditorController;
|
|
19
|
+
/** The current selection (`state.selection`). Omit to follow the controller. */
|
|
20
|
+
selection?: SelectionMask | null;
|
|
21
|
+
/** The adjustment layers (`state.adjustments.layers`). Omit to follow the controller. */
|
|
22
|
+
layers?: AdjustmentLayer[];
|
|
23
|
+
class?: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
let { controller, selection, layers: layersProp, class: className = '' }: Props = $props();
|
|
27
|
+
|
|
28
|
+
let tick = $state(0);
|
|
29
|
+
$effect(() => {
|
|
30
|
+
const c = controller;
|
|
31
|
+
return untrack(() => c.subscribe(() => tick++));
|
|
32
|
+
});
|
|
33
|
+
const sel = $derived.by(() => {
|
|
34
|
+
void tick;
|
|
35
|
+
return selection === undefined ? controller.getSelection() : selection;
|
|
36
|
+
});
|
|
37
|
+
const list = $derived.by(() => {
|
|
38
|
+
void tick;
|
|
39
|
+
return layersProp ?? controller.getAdjustmentLayers();
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
const DEFAULT_PARAMS: Required<RefineEdgeParams> = { radius: 8, smartRadius: false, smooth: 0, feather: 0, contrast: 0, shiftEdge: 0 };
|
|
43
|
+
let params = $state<Required<RefineEdgeParams>>({ ...DEFAULT_PARAMS });
|
|
44
|
+
let busy = $state(false);
|
|
45
|
+
let error = $state<string | null>(null);
|
|
46
|
+
let layerId = $state('');
|
|
47
|
+
let newKind = $state<AdjustmentKind>('hueSaturation');
|
|
48
|
+
const target = $derived(list.find((l) => l.id === layerId) ?? list[list.length - 1]);
|
|
49
|
+
|
|
50
|
+
async function refine() {
|
|
51
|
+
busy = true;
|
|
52
|
+
error = null;
|
|
53
|
+
try {
|
|
54
|
+
await controller.refineSelection({ ...params });
|
|
55
|
+
} catch (err) {
|
|
56
|
+
error = err instanceof Error ? err.message : String(err);
|
|
57
|
+
} finally {
|
|
58
|
+
busy = false;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function newMaskedLayer() {
|
|
63
|
+
const s = controller.getSelection();
|
|
64
|
+
if (s) layerId = controller.addAdjustmentLayer({ kind: newKind, mask: layerMaskFromSelection(s) });
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const button =
|
|
68
|
+
'px-2 py-1 rounded-md border border-slate-800 bg-slate-950 text-[11px] text-slate-200 hover:text-white disabled:opacity-40';
|
|
69
|
+
</script>
|
|
70
|
+
|
|
71
|
+
<div data-testid="refine-edge-panel" class="flex flex-col gap-3 text-xs {className}">
|
|
72
|
+
<span class="font-semibold text-slate-200 uppercase tracking-wider text-[11px]">Refine Edge</span>
|
|
73
|
+
{#each REFINE_EDGE_CONTROLS as c (c.id)}
|
|
74
|
+
<label class="flex flex-col gap-1">
|
|
75
|
+
<span class="flex justify-between text-slate-400">
|
|
76
|
+
<span>{c.label}</span>
|
|
77
|
+
<span class="font-mono">{params[c.id]}{c.unit}</span>
|
|
78
|
+
</span>
|
|
79
|
+
<input
|
|
80
|
+
type="range"
|
|
81
|
+
min={c.min}
|
|
82
|
+
max={c.max}
|
|
83
|
+
step={c.step}
|
|
84
|
+
aria-label={c.label}
|
|
85
|
+
value={params[c.id]}
|
|
86
|
+
oninput={(e) => (params = { ...params, [c.id]: Number((e.currentTarget as HTMLInputElement).value) })}
|
|
87
|
+
class="w-full accent-indigo-500"
|
|
88
|
+
/>
|
|
89
|
+
</label>
|
|
90
|
+
{/each}
|
|
91
|
+
<label class="flex items-center gap-2 text-slate-300">
|
|
92
|
+
<input
|
|
93
|
+
type="checkbox"
|
|
94
|
+
checked={params.smartRadius}
|
|
95
|
+
onchange={(e) => (params = { ...params, smartRadius: (e.currentTarget as HTMLInputElement).checked })}
|
|
96
|
+
/>
|
|
97
|
+
Smart Radius
|
|
98
|
+
</label>
|
|
99
|
+
<div class="flex gap-1">
|
|
100
|
+
<button type="button" class={button} disabled={!sel || busy} onclick={refine}>Refine Edge</button>
|
|
101
|
+
<button type="button" class={button} onclick={() => (params = { ...DEFAULT_PARAMS })}>Reset</button>
|
|
102
|
+
</div>
|
|
103
|
+
{#if error}
|
|
104
|
+
<p role="alert" class="text-rose-400">{error}</p>
|
|
105
|
+
{/if}
|
|
106
|
+
|
|
107
|
+
<span class="font-semibold text-slate-200 uppercase tracking-wider text-[11px] mt-2">Layer Mask</span>
|
|
108
|
+
<div class="flex gap-1 items-center">
|
|
109
|
+
<select
|
|
110
|
+
aria-label="New masked layer kind"
|
|
111
|
+
value={newKind}
|
|
112
|
+
onchange={(e) => (newKind = (e.currentTarget as HTMLSelectElement).value as AdjustmentKind)}
|
|
113
|
+
class="flex-1 bg-slate-950 border border-slate-800 rounded px-1 py-1 text-slate-200"
|
|
114
|
+
>
|
|
115
|
+
{#each ADJUSTMENT_KINDS as k (k.kind)}
|
|
116
|
+
<option value={k.kind}>{k.label}</option>
|
|
117
|
+
{/each}
|
|
118
|
+
</select>
|
|
119
|
+
<button type="button" class={button} disabled={!sel} onclick={newMaskedLayer}>New Masked Layer</button>
|
|
120
|
+
</div>
|
|
121
|
+
{#if list.length > 0 && target}
|
|
122
|
+
<div class="flex flex-col gap-2">
|
|
123
|
+
<select
|
|
124
|
+
aria-label="Mask layer"
|
|
125
|
+
value={target.id}
|
|
126
|
+
onchange={(e) => (layerId = (e.currentTarget as HTMLSelectElement).value)}
|
|
127
|
+
class="bg-slate-950 border border-slate-800 rounded px-1 py-1 text-slate-200"
|
|
128
|
+
>
|
|
129
|
+
{#each list as l (l.id)}
|
|
130
|
+
<option value={l.id}>{l.name || adjustmentKindLabel(l.kind)}{l.mask ? ' (masked)' : ''}</option>
|
|
131
|
+
{/each}
|
|
132
|
+
</select>
|
|
133
|
+
<div class="flex flex-wrap gap-1">
|
|
134
|
+
<button type="button" class={button} disabled={!sel} onclick={() => controller.applySelectionAsLayerMask(target.id)}>
|
|
135
|
+
Mask with Selection
|
|
136
|
+
</button>
|
|
137
|
+
<button type="button" class={button} disabled={!target.mask} onclick={() => controller.selectionFromLayerMask(target.id)}>
|
|
138
|
+
Load as Selection
|
|
139
|
+
</button>
|
|
140
|
+
<button type="button" class={button} disabled={!target.mask} onclick={() => controller.setAdjustmentLayerMask(target.id, null)}>
|
|
141
|
+
Remove Mask
|
|
142
|
+
</button>
|
|
143
|
+
</div>
|
|
144
|
+
{#if target.mask}
|
|
145
|
+
<label class="flex flex-col gap-1">
|
|
146
|
+
<span class="flex justify-between text-slate-400">
|
|
147
|
+
<span>Density</span>
|
|
148
|
+
<span class="font-mono">{Math.round((target.mask.density ?? 1) * 100)}%</span>
|
|
149
|
+
</span>
|
|
150
|
+
<input
|
|
151
|
+
type="range"
|
|
152
|
+
min="0"
|
|
153
|
+
max="100"
|
|
154
|
+
step="1"
|
|
155
|
+
aria-label="Mask density"
|
|
156
|
+
value={Math.round((target.mask.density ?? 1) * 100)}
|
|
157
|
+
oninput={(e) => controller.setAdjustmentLayerMaskProps(target.id, { density: Number((e.currentTarget as HTMLInputElement).value) / 100 })}
|
|
158
|
+
class="w-full accent-indigo-500"
|
|
159
|
+
/>
|
|
160
|
+
</label>
|
|
161
|
+
<label class="flex flex-col gap-1">
|
|
162
|
+
<span class="flex justify-between text-slate-400">
|
|
163
|
+
<span>Mask Feather</span>
|
|
164
|
+
<span class="font-mono">{target.mask.feather ?? 0}px</span>
|
|
165
|
+
</span>
|
|
166
|
+
<input
|
|
167
|
+
type="range"
|
|
168
|
+
min="0"
|
|
169
|
+
max="50"
|
|
170
|
+
step="0.5"
|
|
171
|
+
aria-label="Mask feather"
|
|
172
|
+
value={target.mask.feather ?? 0}
|
|
173
|
+
oninput={(e) => controller.setAdjustmentLayerMaskProps(target.id, { feather: Number((e.currentTarget as HTMLInputElement).value) })}
|
|
174
|
+
class="w-full accent-indigo-500"
|
|
175
|
+
/>
|
|
176
|
+
</label>
|
|
177
|
+
{/if}
|
|
178
|
+
</div>
|
|
179
|
+
{/if}
|
|
180
|
+
</div>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { AdjustmentLayer, ImageEditorController, SelectionMask } from '@pixerate/editor/image-editor';
|
|
2
|
+
interface Props {
|
|
3
|
+
controller: ImageEditorController;
|
|
4
|
+
/** The current selection (`state.selection`). Omit to follow the controller. */
|
|
5
|
+
selection?: SelectionMask | null;
|
|
6
|
+
/** The adjustment layers (`state.adjustments.layers`). Omit to follow the controller. */
|
|
7
|
+
layers?: AdjustmentLayer[];
|
|
8
|
+
class?: string;
|
|
9
|
+
}
|
|
10
|
+
declare const RefineEdgePanel: import("svelte").Component<Props, {}, "">;
|
|
11
|
+
type RefineEdgePanel = ReturnType<typeof RefineEdgePanel>;
|
|
12
|
+
export default RefineEdgePanel;
|
|
13
|
+
//# sourceMappingURL=RefineEdgePanel.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RefineEdgePanel.svelte.d.ts","sourceRoot":"","sources":["../../src/image-editor/RefineEdgePanel.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAER,eAAe,EACf,qBAAqB,EAErB,aAAa,EACd,MAAM,+BAA+B,CAAC;AAUvC,UAAU,KAAK;IACb,UAAU,EAAE,qBAAqB,CAAC;IAClC,gFAAgF;IAChF,SAAS,CAAC,EAAE,aAAa,GAAG,IAAI,CAAC;IACjC,yFAAyF;IACzF,MAAM,CAAC,EAAE,eAAe,EAAE,CAAC;IAC3B,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AA4HH,QAAA,MAAM,eAAe,2CAAwC,CAAC;AAC9D,KAAK,eAAe,GAAG,UAAU,CAAC,OAAO,eAAe,CAAC,CAAC;AAC1D,eAAe,eAAe,CAAC"}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type {
|
|
3
|
+
ImageEditorController,
|
|
4
|
+
SelectionMask,
|
|
5
|
+
SelectionModifyOp,
|
|
6
|
+
SelectionToolOptions,
|
|
7
|
+
} from '@pixerate/editor/image-editor';
|
|
8
|
+
import {
|
|
9
|
+
SELECTION_MODES,
|
|
10
|
+
SELECTION_MODE_LABELS,
|
|
11
|
+
SELECTION_TOOLS,
|
|
12
|
+
describeSelection,
|
|
13
|
+
} from '@pixerate/editor/image-editor';
|
|
14
|
+
import { untrack } from 'svelte';
|
|
15
|
+
|
|
16
|
+
interface Props {
|
|
17
|
+
controller: ImageEditorController;
|
|
18
|
+
options: SelectionToolOptions;
|
|
19
|
+
onOptionsChange: (next: SelectionToolOptions) => void;
|
|
20
|
+
/** The current selection (`state.selection`). Omit to follow the controller. */
|
|
21
|
+
selection?: SelectionMask | null;
|
|
22
|
+
/** The Quick Mask view flag (`state.quickMask`). Omit to follow the controller. */
|
|
23
|
+
quickMask?: boolean;
|
|
24
|
+
class?: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
let { controller, options, onOptionsChange, selection, quickMask, class: className = '' }: Props = $props();
|
|
28
|
+
|
|
29
|
+
let tick = $state(0);
|
|
30
|
+
$effect(() => {
|
|
31
|
+
const c = controller;
|
|
32
|
+
return untrack(() => c.subscribe(() => tick++));
|
|
33
|
+
});
|
|
34
|
+
const sel = $derived.by(() => {
|
|
35
|
+
void tick;
|
|
36
|
+
return selection === undefined ? controller.getSelection() : selection;
|
|
37
|
+
});
|
|
38
|
+
const qm = $derived.by(() => {
|
|
39
|
+
void tick;
|
|
40
|
+
return quickMask ?? Boolean(controller.getState().quickMask);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
let radius = $state(2);
|
|
44
|
+
|
|
45
|
+
const MODIFY_OPS: { op: SelectionModifyOp; label: string }[] = [
|
|
46
|
+
{ op: 'grow', label: 'Grow' },
|
|
47
|
+
{ op: 'shrink', label: 'Shrink' },
|
|
48
|
+
{ op: 'feather', label: 'Feather' },
|
|
49
|
+
{ op: 'border', label: 'Border' },
|
|
50
|
+
{ op: 'smooth', label: 'Smooth' },
|
|
51
|
+
];
|
|
52
|
+
|
|
53
|
+
function set(patch: Partial<SelectionToolOptions>) {
|
|
54
|
+
onOptionsChange({ ...options, ...patch });
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const btn = (on: boolean) =>
|
|
58
|
+
`px-2 py-1 rounded-md border text-[11px] transition-colors ${
|
|
59
|
+
on ? 'bg-indigo-600 border-indigo-500 text-white' : 'bg-slate-950 border-slate-800 text-slate-300 hover:text-white'
|
|
60
|
+
}`;
|
|
61
|
+
</script>
|
|
62
|
+
|
|
63
|
+
<div data-testid="selection-options-bar" class="flex flex-col gap-3 text-xs {className}">
|
|
64
|
+
<div role="group" aria-label="Selection tool" class="flex flex-wrap gap-1">
|
|
65
|
+
{#each SELECTION_TOOLS as t (t.id)}
|
|
66
|
+
<button type="button" aria-pressed={options.tool === t.id} class={btn(options.tool === t.id)} onclick={() => set({ tool: t.id })}>
|
|
67
|
+
{t.label}
|
|
68
|
+
</button>
|
|
69
|
+
{/each}
|
|
70
|
+
</div>
|
|
71
|
+
<div role="group" aria-label="Selection mode" class="flex flex-wrap gap-1">
|
|
72
|
+
{#each SELECTION_MODES as m (m)}
|
|
73
|
+
<button type="button" aria-pressed={options.mode === m} class={btn(options.mode === m)} onclick={() => set({ mode: m })}>
|
|
74
|
+
{SELECTION_MODE_LABELS[m]}
|
|
75
|
+
</button>
|
|
76
|
+
{/each}
|
|
77
|
+
</div>
|
|
78
|
+
{#if options.tool === 'wand'}
|
|
79
|
+
<div class="flex flex-col gap-2">
|
|
80
|
+
<label class="flex flex-col gap-1">
|
|
81
|
+
<span class="flex justify-between text-slate-400">
|
|
82
|
+
<span>Tolerance</span>
|
|
83
|
+
<span class="font-mono">{options.tolerance}</span>
|
|
84
|
+
</span>
|
|
85
|
+
<input
|
|
86
|
+
type="range"
|
|
87
|
+
min="0"
|
|
88
|
+
max="255"
|
|
89
|
+
step="1"
|
|
90
|
+
aria-label="Tolerance"
|
|
91
|
+
value={options.tolerance}
|
|
92
|
+
oninput={(e) => set({ tolerance: Number((e.currentTarget as HTMLInputElement).value) })}
|
|
93
|
+
class="w-full accent-indigo-500"
|
|
94
|
+
/>
|
|
95
|
+
</label>
|
|
96
|
+
<label class="flex items-center gap-2 text-slate-300">
|
|
97
|
+
<input type="checkbox" checked={options.contiguous} onchange={(e) => set({ contiguous: (e.currentTarget as HTMLInputElement).checked })} />
|
|
98
|
+
Contiguous
|
|
99
|
+
</label>
|
|
100
|
+
<label class="flex items-center gap-2 text-slate-300">
|
|
101
|
+
<input type="checkbox" checked={options.sampleMerged} onchange={(e) => set({ sampleMerged: (e.currentTarget as HTMLInputElement).checked })} />
|
|
102
|
+
Sample adjusted image
|
|
103
|
+
</label>
|
|
104
|
+
</div>
|
|
105
|
+
{/if}
|
|
106
|
+
<label class="flex items-center gap-2 text-slate-300">
|
|
107
|
+
<input type="checkbox" checked={options.antiAlias} onchange={(e) => set({ antiAlias: (e.currentTarget as HTMLInputElement).checked })} />
|
|
108
|
+
Anti-alias
|
|
109
|
+
</label>
|
|
110
|
+
<div class="flex flex-wrap gap-1">
|
|
111
|
+
<button type="button" class={btn(false)} onclick={() => controller.selectAll()}>Select All</button>
|
|
112
|
+
<button type="button" class={btn(false)} disabled={!sel} onclick={() => controller.deselect()}>Deselect</button>
|
|
113
|
+
<button type="button" class={btn(false)} onclick={() => controller.invertSelection()}>Inverse</button>
|
|
114
|
+
<button type="button" aria-pressed={qm} class={btn(qm)} onclick={() => controller.setQuickMask(!qm)}>Quick Mask</button>
|
|
115
|
+
</div>
|
|
116
|
+
<div class="flex flex-wrap items-center gap-1">
|
|
117
|
+
<label class="flex items-center gap-1 text-slate-400">
|
|
118
|
+
Radius
|
|
119
|
+
<input
|
|
120
|
+
type="number"
|
|
121
|
+
min="0"
|
|
122
|
+
max="500"
|
|
123
|
+
step="1"
|
|
124
|
+
aria-label="Modify radius"
|
|
125
|
+
value={radius}
|
|
126
|
+
oninput={(e) => (radius = Math.max(0, Number((e.currentTarget as HTMLInputElement).value) || 0))}
|
|
127
|
+
class="w-14 bg-slate-950 border border-slate-800 rounded px-1 py-0.5 text-slate-200"
|
|
128
|
+
/>
|
|
129
|
+
</label>
|
|
130
|
+
{#each MODIFY_OPS as m (m.op)}
|
|
131
|
+
<button type="button" class={btn(false)} disabled={!sel} onclick={() => controller.modifySelection(m.op, radius)}>{m.label}</button>
|
|
132
|
+
{/each}
|
|
133
|
+
</div>
|
|
134
|
+
<p data-testid="selection-status" class="text-slate-400">{describeSelection(sel)}</p>
|
|
135
|
+
</div>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { ImageEditorController, SelectionMask, SelectionToolOptions } from '@pixerate/editor/image-editor';
|
|
2
|
+
interface Props {
|
|
3
|
+
controller: ImageEditorController;
|
|
4
|
+
options: SelectionToolOptions;
|
|
5
|
+
onOptionsChange: (next: SelectionToolOptions) => void;
|
|
6
|
+
/** The current selection (`state.selection`). Omit to follow the controller. */
|
|
7
|
+
selection?: SelectionMask | null;
|
|
8
|
+
/** The Quick Mask view flag (`state.quickMask`). Omit to follow the controller. */
|
|
9
|
+
quickMask?: boolean;
|
|
10
|
+
class?: string;
|
|
11
|
+
}
|
|
12
|
+
declare const SelectionOptionsBar: import("svelte").Component<Props, {}, "">;
|
|
13
|
+
type SelectionOptionsBar = ReturnType<typeof SelectionOptionsBar>;
|
|
14
|
+
export default SelectionOptionsBar;
|
|
15
|
+
//# sourceMappingURL=SelectionOptionsBar.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SelectionOptionsBar.svelte.d.ts","sourceRoot":"","sources":["../../src/image-editor/SelectionOptionsBar.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACR,qBAAqB,EACrB,aAAa,EAEb,oBAAoB,EACrB,MAAM,+BAA+B,CAAC;AAUvC,UAAU,KAAK;IACb,UAAU,EAAE,qBAAqB,CAAC;IAClC,OAAO,EAAE,oBAAoB,CAAC;IAC9B,eAAe,EAAE,CAAC,IAAI,EAAE,oBAAoB,KAAK,IAAI,CAAC;IACtD,gFAAgF;IAChF,SAAS,CAAC,EAAE,aAAa,GAAG,IAAI,CAAC;IACjC,mFAAmF;IACnF,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAqGH,QAAA,MAAM,mBAAmB,2CAAwC,CAAC;AAClE,KAAK,mBAAmB,GAAG,UAAU,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAClE,eAAe,mBAAmB,CAAC"}
|