@pixerate/editor-react 0.58.0 → 0.60.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-ZU54BMDC.js → chunk-OSOGA2OJ.js} +888 -260
- package/dist/chunk-OSOGA2OJ.js.map +1 -0
- package/dist/image-editor/index.cjs +897 -281
- package/dist/image-editor/index.cjs.map +1 -1
- package/dist/image-editor/index.d.cts +35 -4
- package/dist/image-editor/index.d.ts +35 -4
- package/dist/image-editor/index.js +5 -1
- package/dist/index.cjs +1326 -710
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +5 -1
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
- package/dist/chunk-ZU54BMDC.js.map +0 -1
|
@@ -96,11 +96,11 @@ function useImageEditor(options = {}) {
|
|
|
96
96
|
[controller]
|
|
97
97
|
);
|
|
98
98
|
const applyInpaintedImage = useCallback(
|
|
99
|
-
(newDataUrl) => controller.applyInpaintedImage(newDataUrl),
|
|
99
|
+
(newDataUrl, options2) => controller.applyInpaintedImage(newDataUrl, options2),
|
|
100
100
|
[controller]
|
|
101
101
|
);
|
|
102
102
|
const applyBackgroundRemovedImage = useCallback(
|
|
103
|
-
(newDataUrl) => controller.applyBackgroundRemovedImage(newDataUrl),
|
|
103
|
+
(newDataUrl, options2) => controller.applyBackgroundRemovedImage(newDataUrl, options2),
|
|
104
104
|
[controller]
|
|
105
105
|
);
|
|
106
106
|
return useMemo(
|
|
@@ -159,7 +159,7 @@ function useImageEditor(options = {}) {
|
|
|
159
159
|
}
|
|
160
160
|
|
|
161
161
|
// src/image-editor/ImageEditor.tsx
|
|
162
|
-
import
|
|
162
|
+
import React8, { useRef as useRef4, useEffect as useEffect6, useState as useState9, useCallback as useCallback2 } from "react";
|
|
163
163
|
import {
|
|
164
164
|
canvasToImagePoint,
|
|
165
165
|
hitTestCrop,
|
|
@@ -171,7 +171,10 @@ import {
|
|
|
171
171
|
IMAGE_ADJUSTMENT_CONTROLS,
|
|
172
172
|
readAdjustmentControls,
|
|
173
173
|
DEFAULT_SELECTION_TOOL_OPTIONS,
|
|
174
|
-
SelectionGesture
|
|
174
|
+
SelectionGesture,
|
|
175
|
+
PaintGesture,
|
|
176
|
+
handlePaintShortcut,
|
|
177
|
+
pointerPressure
|
|
175
178
|
} from "@pixerate/editor/image-editor";
|
|
176
179
|
|
|
177
180
|
// src/image-editor/ControlSlider.tsx
|
|
@@ -622,15 +625,508 @@ var AdjustmentLayersPanel = ({ controller, layers: layersProp, histogram }) => {
|
|
|
622
625
|
] });
|
|
623
626
|
};
|
|
624
627
|
|
|
628
|
+
// src/image-editor/LayersPanel.tsx
|
|
629
|
+
import { useEffect as useEffect4, useRef as useRef3, useState as useState5 } from "react";
|
|
630
|
+
import {
|
|
631
|
+
BLEND_MODES as BLEND_MODES2,
|
|
632
|
+
PSD_FILE_ACCEPT,
|
|
633
|
+
downloadPsd,
|
|
634
|
+
dropPlacement,
|
|
635
|
+
dropZone,
|
|
636
|
+
openPsdFile,
|
|
637
|
+
layerBadge,
|
|
638
|
+
layerMenu,
|
|
639
|
+
layerPanelRows,
|
|
640
|
+
runLayerMenuAction
|
|
641
|
+
} from "@pixerate/editor/image-editor";
|
|
642
|
+
import { Fragment, jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
643
|
+
var iconButton = "px-1.5 py-0.5 rounded text-[10px] bg-slate-800 text-slate-300 hover:text-white disabled:opacity-40";
|
|
644
|
+
var rowClass = (active) => `flex items-center gap-1.5 px-1.5 py-1 rounded cursor-pointer select-none ${active ? "bg-indigo-600/30 ring-1 ring-indigo-500" : "hover:bg-slate-800"}`;
|
|
645
|
+
var Thumbnail = ({
|
|
646
|
+
pixels,
|
|
647
|
+
size,
|
|
648
|
+
label,
|
|
649
|
+
onClick,
|
|
650
|
+
selected,
|
|
651
|
+
testId
|
|
652
|
+
}) => {
|
|
653
|
+
const ref = useRef3(null);
|
|
654
|
+
useEffect4(() => {
|
|
655
|
+
const canvas = ref.current;
|
|
656
|
+
const ctx = canvas?.getContext("2d");
|
|
657
|
+
if (!canvas || !ctx || !pixels || typeof ctx.createImageData !== "function") return;
|
|
658
|
+
canvas.width = pixels.width;
|
|
659
|
+
canvas.height = pixels.height;
|
|
660
|
+
const img = ctx.createImageData(pixels.width, pixels.height);
|
|
661
|
+
img.data.set(pixels.data);
|
|
662
|
+
ctx.putImageData(img, 0, 0);
|
|
663
|
+
}, [pixels]);
|
|
664
|
+
return /* @__PURE__ */ jsx5(
|
|
665
|
+
"canvas",
|
|
666
|
+
{
|
|
667
|
+
ref,
|
|
668
|
+
role: onClick ? "button" : "img",
|
|
669
|
+
"aria-label": label,
|
|
670
|
+
"data-testid": testId,
|
|
671
|
+
onClick,
|
|
672
|
+
style: { width: size, height: size },
|
|
673
|
+
className: `shrink-0 rounded bg-[repeating-conic-gradient(#334155_0%_25%,#1e293b_0%_50%)] [background-size:8px_8px] object-contain ${selected ? "ring-2 ring-white" : "ring-1 ring-slate-700"}`
|
|
674
|
+
}
|
|
675
|
+
);
|
|
676
|
+
};
|
|
677
|
+
var LayersPanel = ({ controller, thumbnailSize = 32, psd = true, psdFileName = "image", className }) => {
|
|
678
|
+
const [, setTick] = useState5(0);
|
|
679
|
+
useEffect4(() => controller.subscribe(() => setTick((n) => n + 1)), [controller]);
|
|
680
|
+
const [error, setError] = useState5(null);
|
|
681
|
+
const [renaming, setRenaming] = useState5(null);
|
|
682
|
+
const [menu, setMenu] = useState5(null);
|
|
683
|
+
const [drag, setDrag] = useState5(null);
|
|
684
|
+
const [psdNotes, setPsdNotes] = useState5([]);
|
|
685
|
+
const fileInput = useRef3(null);
|
|
686
|
+
const psdTask = async (fn) => {
|
|
687
|
+
try {
|
|
688
|
+
setError(null);
|
|
689
|
+
setPsdNotes(await fn());
|
|
690
|
+
} catch (err) {
|
|
691
|
+
setError(err instanceof Error ? err.message : String(err));
|
|
692
|
+
}
|
|
693
|
+
};
|
|
694
|
+
const commitNext = useRef3(true);
|
|
695
|
+
const state = controller.getState();
|
|
696
|
+
const rows = layerPanelRows(state.layers, state.activeLayerId);
|
|
697
|
+
const active = controller.getActiveLayer();
|
|
698
|
+
const attempt = (fn) => {
|
|
699
|
+
try {
|
|
700
|
+
setError(null);
|
|
701
|
+
fn();
|
|
702
|
+
} catch (err) {
|
|
703
|
+
setError(err instanceof Error ? err.message : String(err));
|
|
704
|
+
}
|
|
705
|
+
};
|
|
706
|
+
const run = async (action) => {
|
|
707
|
+
setMenu(null);
|
|
708
|
+
setError(await runLayerMenuAction(controller, action));
|
|
709
|
+
};
|
|
710
|
+
const prop = (key, value) => attempt(() => {
|
|
711
|
+
controller.setLayerProperties(active.id, { [key]: value }, commitNext.current);
|
|
712
|
+
commitNext.current = false;
|
|
713
|
+
});
|
|
714
|
+
const sliderEvents = {
|
|
715
|
+
onPointerDown: () => commitNext.current = true,
|
|
716
|
+
onPointerUp: () => commitNext.current = true,
|
|
717
|
+
onKeyDown: () => commitNext.current = true
|
|
718
|
+
};
|
|
719
|
+
const onDrop = (e, row) => {
|
|
720
|
+
e.preventDefault();
|
|
721
|
+
const id = e.dataTransfer.getData("text/x-pixerate-layer") || drag;
|
|
722
|
+
setDrag(null);
|
|
723
|
+
if (!id) return;
|
|
724
|
+
const rect = e.currentTarget.getBoundingClientRect();
|
|
725
|
+
const fraction = rect.height ? (e.clientY - rect.top) / rect.height : 0.5;
|
|
726
|
+
const placement = dropPlacement(state.layers, id, row.layer.id, dropZone(fraction, row.layer.type === "group"));
|
|
727
|
+
if (placement) attempt(() => controller.moveLayer(id, placement));
|
|
728
|
+
};
|
|
729
|
+
const thumb = (layer) => layer.type === "pixel" ? controller.getLayerThumbnail(layer.id, { maxSide: 64 }) : null;
|
|
730
|
+
const maskThumb = (layer) => layer.mask && !layer.mask.source ? controller.getLayerThumbnail(layer.id, { maxSide: 64, mask: true }) : null;
|
|
731
|
+
const locked = (layer) => Boolean(layer.locks?.all || layer.locks?.pixels || layer.locks?.position || layer.locks?.transparency);
|
|
732
|
+
return /* @__PURE__ */ jsxs5("div", { className: `flex flex-col gap-2 text-xs text-slate-200 ${className ?? ""}`, "data-testid": "layers-panel", onClick: () => menu && setMenu(null), children: [
|
|
733
|
+
/* @__PURE__ */ jsxs5("div", { className: "flex flex-wrap items-center gap-1", role: "toolbar", "aria-label": "Layer actions", children: [
|
|
734
|
+
/* @__PURE__ */ jsx5("button", { type: "button", className: iconButton, onClick: () => run("new-layer"), children: "New Layer" }),
|
|
735
|
+
/* @__PURE__ */ jsx5("button", { type: "button", className: iconButton, onClick: () => run("new-group"), children: "New Group" }),
|
|
736
|
+
/* @__PURE__ */ jsx5("button", { type: "button", className: iconButton, disabled: !active, onClick: () => run("duplicate"), children: "Duplicate" }),
|
|
737
|
+
/* @__PURE__ */ jsx5("button", { type: "button", className: iconButton, disabled: !active, onClick: () => run("add-mask"), children: "Add Mask" }),
|
|
738
|
+
/* @__PURE__ */ jsx5("button", { type: "button", className: iconButton, disabled: !active, onClick: () => run("delete"), children: "Delete" }),
|
|
739
|
+
/* @__PURE__ */ jsx5(
|
|
740
|
+
"button",
|
|
741
|
+
{
|
|
742
|
+
type: "button",
|
|
743
|
+
className: iconButton,
|
|
744
|
+
"aria-label": "Layer menu",
|
|
745
|
+
"aria-haspopup": "menu",
|
|
746
|
+
onClick: (e) => {
|
|
747
|
+
e.stopPropagation();
|
|
748
|
+
const r = e.currentTarget.getBoundingClientRect();
|
|
749
|
+
setMenu(menu ? null : { x: r.left, y: r.bottom });
|
|
750
|
+
},
|
|
751
|
+
children: "\u22EF"
|
|
752
|
+
}
|
|
753
|
+
),
|
|
754
|
+
psd && /* @__PURE__ */ jsxs5(Fragment, { children: [
|
|
755
|
+
/* @__PURE__ */ jsx5("button", { type: "button", className: iconButton, onClick: () => fileInput.current?.click(), children: "Open PSD\u2026" }),
|
|
756
|
+
/* @__PURE__ */ jsx5("button", { type: "button", className: iconButton, onClick: () => psdTask(async () => (await downloadPsd(controller, psdFileName)).warnings), children: "Export PSD" }),
|
|
757
|
+
/* @__PURE__ */ jsx5(
|
|
758
|
+
"input",
|
|
759
|
+
{
|
|
760
|
+
ref: fileInput,
|
|
761
|
+
type: "file",
|
|
762
|
+
accept: PSD_FILE_ACCEPT,
|
|
763
|
+
className: "hidden",
|
|
764
|
+
"data-testid": "psd-file-input",
|
|
765
|
+
"aria-label": "Open a PSD file",
|
|
766
|
+
onChange: (e) => {
|
|
767
|
+
const file = e.currentTarget.files?.[0];
|
|
768
|
+
e.currentTarget.value = "";
|
|
769
|
+
if (file) psdTask(() => openPsdFile(controller, file));
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
)
|
|
773
|
+
] })
|
|
774
|
+
] }),
|
|
775
|
+
psdNotes.length > 0 && /* @__PURE__ */ jsxs5("ul", { className: "rounded bg-amber-900/30 text-amber-200 px-2 py-1 space-y-0.5", "data-testid": "psd-warnings", "aria-label": "PSD warnings", children: [
|
|
776
|
+
psdNotes.map((n) => /* @__PURE__ */ jsx5("li", { children: n }, n)),
|
|
777
|
+
/* @__PURE__ */ jsx5("li", { children: /* @__PURE__ */ jsx5("button", { type: "button", className: "underline", onClick: () => setPsdNotes([]), children: "Dismiss" }) })
|
|
778
|
+
] }),
|
|
779
|
+
active && /* @__PURE__ */ jsxs5("div", { className: "grid grid-cols-2 gap-x-2 gap-y-1 items-center", "data-testid": "layer-properties", children: [
|
|
780
|
+
/* @__PURE__ */ jsxs5("label", { className: "flex flex-col gap-0.5", children: [
|
|
781
|
+
/* @__PURE__ */ jsxs5("span", { className: "text-slate-400", children: [
|
|
782
|
+
"Opacity ",
|
|
783
|
+
Math.round(active.opacity * 100),
|
|
784
|
+
"%"
|
|
785
|
+
] }),
|
|
786
|
+
/* @__PURE__ */ jsx5(
|
|
787
|
+
"input",
|
|
788
|
+
{
|
|
789
|
+
type: "range",
|
|
790
|
+
min: 0,
|
|
791
|
+
max: 100,
|
|
792
|
+
"aria-label": "Layer opacity",
|
|
793
|
+
value: Math.round(active.opacity * 100),
|
|
794
|
+
...sliderEvents,
|
|
795
|
+
onChange: (e) => prop("opacity", Number(e.target.value) / 100),
|
|
796
|
+
className: "accent-indigo-500"
|
|
797
|
+
}
|
|
798
|
+
)
|
|
799
|
+
] }),
|
|
800
|
+
/* @__PURE__ */ jsxs5("label", { className: "flex flex-col gap-0.5", children: [
|
|
801
|
+
/* @__PURE__ */ jsxs5("span", { className: "text-slate-400", children: [
|
|
802
|
+
"Fill ",
|
|
803
|
+
Math.round(((active.type === "pixel" ? active.fillOpacity : void 0) ?? 1) * 100),
|
|
804
|
+
"%"
|
|
805
|
+
] }),
|
|
806
|
+
/* @__PURE__ */ jsx5(
|
|
807
|
+
"input",
|
|
808
|
+
{
|
|
809
|
+
type: "range",
|
|
810
|
+
min: 0,
|
|
811
|
+
max: 100,
|
|
812
|
+
"aria-label": "Layer fill",
|
|
813
|
+
disabled: active.type !== "pixel",
|
|
814
|
+
value: Math.round(((active.type === "pixel" ? active.fillOpacity : void 0) ?? 1) * 100),
|
|
815
|
+
...sliderEvents,
|
|
816
|
+
onChange: (e) => prop("fillOpacity", Number(e.target.value) / 100),
|
|
817
|
+
className: "accent-indigo-500 disabled:opacity-40"
|
|
818
|
+
}
|
|
819
|
+
)
|
|
820
|
+
] }),
|
|
821
|
+
/* @__PURE__ */ jsxs5(
|
|
822
|
+
"select",
|
|
823
|
+
{
|
|
824
|
+
"aria-label": "Layer blend mode",
|
|
825
|
+
className: "bg-slate-800 border border-slate-700 rounded px-1.5 py-1",
|
|
826
|
+
value: active.blend,
|
|
827
|
+
onChange: (e) => attempt(() => controller.setLayerProperties(active.id, { blend: e.target.value })),
|
|
828
|
+
children: [
|
|
829
|
+
active.type === "group" && /* @__PURE__ */ jsx5("option", { value: "pass-through", children: "Pass Through" }),
|
|
830
|
+
BLEND_MODES2.map((m) => /* @__PURE__ */ jsx5("option", { value: m.id, children: m.label }, m.id))
|
|
831
|
+
]
|
|
832
|
+
}
|
|
833
|
+
),
|
|
834
|
+
/* @__PURE__ */ jsxs5("label", { className: "flex items-center gap-1.5 text-slate-300", children: [
|
|
835
|
+
/* @__PURE__ */ jsx5(
|
|
836
|
+
"input",
|
|
837
|
+
{
|
|
838
|
+
type: "checkbox",
|
|
839
|
+
"aria-label": "Clip to layer below",
|
|
840
|
+
checked: Boolean(active.clipped),
|
|
841
|
+
onChange: (e) => attempt(() => controller.setLayerProperties(active.id, { clipped: e.target.checked })),
|
|
842
|
+
className: "accent-indigo-500"
|
|
843
|
+
}
|
|
844
|
+
),
|
|
845
|
+
"Clip to below"
|
|
846
|
+
] }),
|
|
847
|
+
/* @__PURE__ */ jsx5("div", { className: "col-span-2 flex flex-wrap gap-2 text-slate-400", role: "group", "aria-label": "Layer locks", children: ["pixels", "transparency", "position", "all"].map((k) => /* @__PURE__ */ jsxs5("label", { className: "flex items-center gap-1", children: [
|
|
848
|
+
/* @__PURE__ */ jsx5(
|
|
849
|
+
"input",
|
|
850
|
+
{
|
|
851
|
+
type: "checkbox",
|
|
852
|
+
"aria-label": `Lock ${k}`,
|
|
853
|
+
checked: Boolean(active.locks?.[k]),
|
|
854
|
+
onChange: (e) => attempt(() => controller.setLayerProperties(active.id, { locks: { ...active.locks, [k]: e.target.checked } })),
|
|
855
|
+
className: "accent-indigo-500"
|
|
856
|
+
}
|
|
857
|
+
),
|
|
858
|
+
k
|
|
859
|
+
] }, k)) })
|
|
860
|
+
] }),
|
|
861
|
+
/* @__PURE__ */ jsx5("ul", { className: "flex flex-col gap-0.5", role: "tree", "aria-label": "Layers", children: rows.map((row) => {
|
|
862
|
+
const { layer } = row;
|
|
863
|
+
return /* @__PURE__ */ jsxs5(
|
|
864
|
+
"li",
|
|
865
|
+
{
|
|
866
|
+
role: "treeitem",
|
|
867
|
+
"aria-selected": row.active,
|
|
868
|
+
"aria-level": row.depth + 1,
|
|
869
|
+
"data-layer-id": layer.id,
|
|
870
|
+
draggable: !layer.locks?.all,
|
|
871
|
+
onDragStart: (e) => {
|
|
872
|
+
e.dataTransfer.setData("text/x-pixerate-layer", layer.id);
|
|
873
|
+
setDrag(layer.id);
|
|
874
|
+
},
|
|
875
|
+
onDragOver: (e) => e.preventDefault(),
|
|
876
|
+
onDrop: (e) => onDrop(e, row),
|
|
877
|
+
tabIndex: 0,
|
|
878
|
+
onClick: () => attempt(() => controller.setActiveLayer(layer.id, "pixels")),
|
|
879
|
+
onKeyDown: (e) => {
|
|
880
|
+
if (e.target === e.currentTarget && (e.key === "Enter" || e.key === " ")) {
|
|
881
|
+
e.preventDefault();
|
|
882
|
+
attempt(() => controller.setActiveLayer(layer.id, "pixels"));
|
|
883
|
+
}
|
|
884
|
+
},
|
|
885
|
+
onContextMenu: (e) => {
|
|
886
|
+
e.preventDefault();
|
|
887
|
+
controller.setActiveLayer(layer.id, state.activeLayerId === layer.id ? state.activeTarget : "pixels");
|
|
888
|
+
setMenu({ x: e.clientX, y: e.clientY });
|
|
889
|
+
},
|
|
890
|
+
className: rowClass(row.active),
|
|
891
|
+
style: { paddingLeft: 6 + row.depth * 14 },
|
|
892
|
+
children: [
|
|
893
|
+
/* @__PURE__ */ jsx5(
|
|
894
|
+
"button",
|
|
895
|
+
{
|
|
896
|
+
type: "button",
|
|
897
|
+
"aria-label": layer.visible ? `Hide ${row.label}` : `Show ${row.label}`,
|
|
898
|
+
"aria-pressed": layer.visible,
|
|
899
|
+
className: "w-4 text-center text-slate-300",
|
|
900
|
+
onClick: (e) => {
|
|
901
|
+
e.stopPropagation();
|
|
902
|
+
attempt(() => controller.setLayerProperties(layer.id, { visible: !layer.visible }));
|
|
903
|
+
},
|
|
904
|
+
children: layer.visible ? "\u25CF" : "\u25CB"
|
|
905
|
+
}
|
|
906
|
+
),
|
|
907
|
+
layer.type === "group" && /* @__PURE__ */ jsx5(
|
|
908
|
+
"button",
|
|
909
|
+
{
|
|
910
|
+
type: "button",
|
|
911
|
+
"aria-label": layer.collapsed ? `Expand ${row.label}` : `Collapse ${row.label}`,
|
|
912
|
+
className: "w-3 text-slate-400",
|
|
913
|
+
onClick: (e) => {
|
|
914
|
+
e.stopPropagation();
|
|
915
|
+
attempt(() => controller.setLayerProperties(layer.id, { collapsed: !layer.collapsed }));
|
|
916
|
+
},
|
|
917
|
+
children: layer.collapsed ? "\u25B8" : "\u25BE"
|
|
918
|
+
}
|
|
919
|
+
),
|
|
920
|
+
row.clipped && /* @__PURE__ */ jsx5("span", { className: "text-slate-500", "aria-label": "Clipped", children: "\u21B3" }),
|
|
921
|
+
layer.type === "pixel" ? /* @__PURE__ */ jsx5(
|
|
922
|
+
Thumbnail,
|
|
923
|
+
{
|
|
924
|
+
pixels: thumb(layer),
|
|
925
|
+
size: thumbnailSize,
|
|
926
|
+
label: `${row.label} pixels`,
|
|
927
|
+
selected: row.active && state.activeTarget === "pixels" && Boolean(layer.mask)
|
|
928
|
+
}
|
|
929
|
+
) : /* @__PURE__ */ jsx5(
|
|
930
|
+
"span",
|
|
931
|
+
{
|
|
932
|
+
className: "shrink-0 grid place-items-center rounded bg-slate-800 text-[9px] text-slate-400 ring-1 ring-slate-700",
|
|
933
|
+
style: { width: thumbnailSize, height: thumbnailSize },
|
|
934
|
+
children: layerBadge(layer)
|
|
935
|
+
}
|
|
936
|
+
),
|
|
937
|
+
layer.mask && /* @__PURE__ */ jsx5(
|
|
938
|
+
Thumbnail,
|
|
939
|
+
{
|
|
940
|
+
pixels: maskThumb(layer),
|
|
941
|
+
size: thumbnailSize,
|
|
942
|
+
label: `${row.label} mask${layer.mask.enabled === false ? " (disabled)" : ""}`,
|
|
943
|
+
testId: "layer-mask-thumbnail",
|
|
944
|
+
selected: row.active && state.activeTarget === "mask",
|
|
945
|
+
onClick: (e) => {
|
|
946
|
+
e.stopPropagation();
|
|
947
|
+
if (e.shiftKey) attempt(() => controller.setLayerMaskProps(layer.id, { enabled: layer.mask.enabled === false }));
|
|
948
|
+
else attempt(() => controller.setActiveLayer(layer.id, "mask"));
|
|
949
|
+
}
|
|
950
|
+
}
|
|
951
|
+
),
|
|
952
|
+
renaming === layer.id ? /* @__PURE__ */ jsx5(
|
|
953
|
+
"input",
|
|
954
|
+
{
|
|
955
|
+
autoFocus: true,
|
|
956
|
+
"aria-label": "Layer name",
|
|
957
|
+
defaultValue: layer.name || row.label,
|
|
958
|
+
className: "flex-1 min-w-0 bg-slate-900 border border-slate-600 rounded px-1",
|
|
959
|
+
onClick: (e) => e.stopPropagation(),
|
|
960
|
+
onBlur: (e) => {
|
|
961
|
+
setRenaming(null);
|
|
962
|
+
if (e.target.value.trim() && e.target.value !== layer.name) attempt(() => controller.setLayerProperties(layer.id, { name: e.target.value }));
|
|
963
|
+
},
|
|
964
|
+
onKeyDown: (e) => {
|
|
965
|
+
if (e.key === "Enter") e.target.blur();
|
|
966
|
+
if (e.key === "Escape") setRenaming(null);
|
|
967
|
+
}
|
|
968
|
+
}
|
|
969
|
+
) : /* @__PURE__ */ jsx5(
|
|
970
|
+
"span",
|
|
971
|
+
{
|
|
972
|
+
className: `flex-1 min-w-0 truncate ${layer.visible ? "" : "text-slate-500"}`,
|
|
973
|
+
onDoubleClick: (e) => {
|
|
974
|
+
e.stopPropagation();
|
|
975
|
+
setRenaming(layer.id);
|
|
976
|
+
},
|
|
977
|
+
children: row.label
|
|
978
|
+
}
|
|
979
|
+
),
|
|
980
|
+
locked(layer) && /* @__PURE__ */ jsx5("span", { "aria-label": "Locked", className: "text-slate-400", children: "\u{1F512}" }),
|
|
981
|
+
layer.blend !== "normal" && layer.blend !== "pass-through" && /* @__PURE__ */ jsx5("span", { className: "text-[9px] text-slate-500", children: layer.blend })
|
|
982
|
+
]
|
|
983
|
+
},
|
|
984
|
+
layer.id
|
|
985
|
+
);
|
|
986
|
+
}) }),
|
|
987
|
+
error && /* @__PURE__ */ jsx5("p", { role: "alert", className: "text-rose-400", children: error }),
|
|
988
|
+
menu && /* @__PURE__ */ jsx5(
|
|
989
|
+
"div",
|
|
990
|
+
{
|
|
991
|
+
role: "menu",
|
|
992
|
+
"aria-label": "Layer menu",
|
|
993
|
+
className: "fixed z-50 min-w-44 rounded border border-slate-700 bg-slate-900 py-1 shadow-xl",
|
|
994
|
+
style: { left: menu.x, top: menu.y },
|
|
995
|
+
onClick: (e) => e.stopPropagation(),
|
|
996
|
+
onKeyDown: (e) => e.key === "Escape" && setMenu(null),
|
|
997
|
+
children: layerMenu(controller).map((item) => /* @__PURE__ */ jsx5(
|
|
998
|
+
"button",
|
|
999
|
+
{
|
|
1000
|
+
type: "button",
|
|
1001
|
+
role: "menuitem",
|
|
1002
|
+
disabled: !item.enabled,
|
|
1003
|
+
className: "block w-full px-3 py-1 text-left hover:bg-slate-800 disabled:opacity-40",
|
|
1004
|
+
onClick: () => run(item.action),
|
|
1005
|
+
children: item.label
|
|
1006
|
+
},
|
|
1007
|
+
item.action
|
|
1008
|
+
))
|
|
1009
|
+
}
|
|
1010
|
+
)
|
|
1011
|
+
] });
|
|
1012
|
+
};
|
|
1013
|
+
|
|
1014
|
+
// src/image-editor/BrushOptionsBar.tsx
|
|
1015
|
+
import { useEffect as useEffect5, useState as useState6 } from "react";
|
|
1016
|
+
import { BRUSH_OPTION_CONTROLS, PAINT_TOOLS } from "@pixerate/editor/image-editor";
|
|
1017
|
+
import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
1018
|
+
var chip = (on) => `px-2 py-1 rounded text-[11px] ${on ? "bg-indigo-600 text-white" : "bg-slate-800 text-slate-300 hover:text-white"}`;
|
|
1019
|
+
var BrushOptionsBar = ({ controller, className }) => {
|
|
1020
|
+
const [, setTick] = useState6(0);
|
|
1021
|
+
useEffect5(() => controller.subscribe(() => setTick((n) => n + 1)), [controller]);
|
|
1022
|
+
const [error, setError] = useState6(null);
|
|
1023
|
+
const state = controller.getState();
|
|
1024
|
+
const { brush, foreground, background } = state.paint;
|
|
1025
|
+
const attempt = (fn) => {
|
|
1026
|
+
try {
|
|
1027
|
+
setError(null);
|
|
1028
|
+
fn();
|
|
1029
|
+
} catch (err) {
|
|
1030
|
+
setError(err instanceof Error ? err.message : String(err));
|
|
1031
|
+
}
|
|
1032
|
+
};
|
|
1033
|
+
return /* @__PURE__ */ jsxs6("div", { className: `flex flex-col gap-2 text-xs text-slate-200 ${className ?? ""}`, "data-testid": "brush-options", children: [
|
|
1034
|
+
/* @__PURE__ */ jsx6("div", { className: "flex gap-1", role: "toolbar", "aria-label": "Paint tools", children: PAINT_TOOLS.map((t) => /* @__PURE__ */ jsx6(
|
|
1035
|
+
"button",
|
|
1036
|
+
{
|
|
1037
|
+
type: "button",
|
|
1038
|
+
"aria-pressed": state.activeTool === t.id,
|
|
1039
|
+
title: `${t.label} (${t.shortcut})`,
|
|
1040
|
+
className: chip(state.activeTool === t.id),
|
|
1041
|
+
onClick: () => controller.setTool(t.id),
|
|
1042
|
+
children: t.label
|
|
1043
|
+
},
|
|
1044
|
+
t.id
|
|
1045
|
+
)) }),
|
|
1046
|
+
/* @__PURE__ */ jsxs6("div", { className: "flex items-center gap-2", role: "group", "aria-label": "Colours", children: [
|
|
1047
|
+
/* @__PURE__ */ jsxs6("label", { className: "flex items-center gap-1", children: [
|
|
1048
|
+
/* @__PURE__ */ jsx6(
|
|
1049
|
+
"input",
|
|
1050
|
+
{
|
|
1051
|
+
type: "color",
|
|
1052
|
+
"aria-label": "Foreground colour",
|
|
1053
|
+
value: foreground,
|
|
1054
|
+
onChange: (e) => controller.setPaintOptions({ foreground: e.target.value }),
|
|
1055
|
+
className: "h-6 w-8 rounded border border-slate-700 bg-transparent"
|
|
1056
|
+
}
|
|
1057
|
+
),
|
|
1058
|
+
/* @__PURE__ */ jsx6("span", { className: "text-slate-400", children: "FG" })
|
|
1059
|
+
] }),
|
|
1060
|
+
/* @__PURE__ */ jsxs6("label", { className: "flex items-center gap-1", children: [
|
|
1061
|
+
/* @__PURE__ */ jsx6(
|
|
1062
|
+
"input",
|
|
1063
|
+
{
|
|
1064
|
+
type: "color",
|
|
1065
|
+
"aria-label": "Background colour",
|
|
1066
|
+
value: background,
|
|
1067
|
+
onChange: (e) => controller.setPaintOptions({ background: e.target.value }),
|
|
1068
|
+
className: "h-6 w-8 rounded border border-slate-700 bg-transparent"
|
|
1069
|
+
}
|
|
1070
|
+
),
|
|
1071
|
+
/* @__PURE__ */ jsx6("span", { className: "text-slate-400", children: "BG" })
|
|
1072
|
+
] }),
|
|
1073
|
+
/* @__PURE__ */ jsx6("button", { type: "button", title: "Swap colours (X)", className: chip(false), onClick: () => controller.swapPaintColors(), children: "Swap" }),
|
|
1074
|
+
/* @__PURE__ */ jsx6("button", { type: "button", title: "Default colours (D)", className: chip(false), onClick: () => controller.resetPaintColors(), children: "Default" })
|
|
1075
|
+
] }),
|
|
1076
|
+
BRUSH_OPTION_CONTROLS.map((spec) => /* @__PURE__ */ jsx6(
|
|
1077
|
+
ControlSlider,
|
|
1078
|
+
{
|
|
1079
|
+
spec,
|
|
1080
|
+
value: brush[spec.id],
|
|
1081
|
+
onChange: (v) => controller.setPaintOptions({ brush: { [spec.id]: v } })
|
|
1082
|
+
},
|
|
1083
|
+
spec.id
|
|
1084
|
+
)),
|
|
1085
|
+
/* @__PURE__ */ jsxs6("div", { className: "flex flex-wrap gap-3 text-slate-300", role: "group", "aria-label": "Pressure", children: [
|
|
1086
|
+
/* @__PURE__ */ jsxs6("label", { className: "flex items-center gap-1.5", children: [
|
|
1087
|
+
/* @__PURE__ */ jsx6(
|
|
1088
|
+
"input",
|
|
1089
|
+
{
|
|
1090
|
+
type: "checkbox",
|
|
1091
|
+
"aria-label": "Pressure controls size",
|
|
1092
|
+
checked: brush.pressureSize,
|
|
1093
|
+
onChange: (e) => controller.setPaintOptions({ brush: { pressureSize: e.target.checked } }),
|
|
1094
|
+
className: "accent-indigo-500"
|
|
1095
|
+
}
|
|
1096
|
+
),
|
|
1097
|
+
"Pressure size"
|
|
1098
|
+
] }),
|
|
1099
|
+
/* @__PURE__ */ jsxs6("label", { className: "flex items-center gap-1.5", children: [
|
|
1100
|
+
/* @__PURE__ */ jsx6(
|
|
1101
|
+
"input",
|
|
1102
|
+
{
|
|
1103
|
+
type: "checkbox",
|
|
1104
|
+
"aria-label": "Pressure controls opacity",
|
|
1105
|
+
checked: brush.pressureOpacity,
|
|
1106
|
+
onChange: (e) => controller.setPaintOptions({ brush: { pressureOpacity: e.target.checked } }),
|
|
1107
|
+
className: "accent-indigo-500"
|
|
1108
|
+
}
|
|
1109
|
+
),
|
|
1110
|
+
"Pressure opacity"
|
|
1111
|
+
] })
|
|
1112
|
+
] }),
|
|
1113
|
+
/* @__PURE__ */ jsxs6("div", { className: "flex gap-1", children: [
|
|
1114
|
+
/* @__PURE__ */ jsx6("button", { type: "button", className: chip(false), onClick: () => attempt(() => controller.fillPixels()), children: "Fill" }),
|
|
1115
|
+
/* @__PURE__ */ jsx6("button", { type: "button", className: chip(false), onClick: () => attempt(() => controller.clearPixels()), children: "Clear" })
|
|
1116
|
+
] }),
|
|
1117
|
+
error && /* @__PURE__ */ jsx6("p", { role: "alert", className: "text-rose-400", children: error })
|
|
1118
|
+
] });
|
|
1119
|
+
};
|
|
1120
|
+
|
|
625
1121
|
// src/image-editor/SelectionOptionsBar.tsx
|
|
626
|
-
import { useState as
|
|
1122
|
+
import { useState as useState7 } from "react";
|
|
627
1123
|
import {
|
|
628
1124
|
SELECTION_MODES,
|
|
629
1125
|
SELECTION_MODE_LABELS,
|
|
630
1126
|
SELECTION_TOOLS,
|
|
631
1127
|
describeSelection
|
|
632
1128
|
} from "@pixerate/editor/image-editor";
|
|
633
|
-
import { jsx as
|
|
1129
|
+
import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
634
1130
|
var MODIFY_OPS = [
|
|
635
1131
|
{ op: "grow", label: "Grow" },
|
|
636
1132
|
{ op: "shrink", label: "Shrink" },
|
|
@@ -647,20 +1143,20 @@ var SelectionOptionsBar = ({
|
|
|
647
1143
|
quickMask,
|
|
648
1144
|
className = ""
|
|
649
1145
|
}) => {
|
|
650
|
-
const [radius, setRadius] =
|
|
1146
|
+
const [radius, setRadius] = useState7(2);
|
|
651
1147
|
const set = (patch) => onOptionsChange({ ...options, ...patch });
|
|
652
1148
|
const sel = selection === void 0 ? controller.getSelection() : selection;
|
|
653
1149
|
const qm = quickMask ?? Boolean(controller.getState().quickMask);
|
|
654
|
-
return /* @__PURE__ */
|
|
655
|
-
/* @__PURE__ */
|
|
656
|
-
/* @__PURE__ */
|
|
657
|
-
options.tool === "wand" && /* @__PURE__ */
|
|
658
|
-
/* @__PURE__ */
|
|
659
|
-
/* @__PURE__ */
|
|
660
|
-
/* @__PURE__ */
|
|
661
|
-
/* @__PURE__ */
|
|
1150
|
+
return /* @__PURE__ */ jsxs7("div", { "data-testid": "selection-options-bar", className: `flex flex-col gap-3 text-xs ${className}`, children: [
|
|
1151
|
+
/* @__PURE__ */ jsx7("div", { role: "group", "aria-label": "Selection tool", className: "flex flex-wrap gap-1", children: SELECTION_TOOLS.map((t) => /* @__PURE__ */ jsx7("button", { type: "button", "aria-pressed": options.tool === t.id, className: btn(options.tool === t.id), onClick: () => set({ tool: t.id }), children: t.label }, t.id)) }),
|
|
1152
|
+
/* @__PURE__ */ jsx7("div", { role: "group", "aria-label": "Selection mode", className: "flex flex-wrap gap-1", children: SELECTION_MODES.map((m) => /* @__PURE__ */ jsx7("button", { type: "button", "aria-pressed": options.mode === m, className: btn(options.mode === m), onClick: () => set({ mode: m }), children: SELECTION_MODE_LABELS[m] }, m)) }),
|
|
1153
|
+
options.tool === "wand" && /* @__PURE__ */ jsxs7("div", { className: "flex flex-col gap-2", children: [
|
|
1154
|
+
/* @__PURE__ */ jsxs7("label", { className: "flex flex-col gap-1", children: [
|
|
1155
|
+
/* @__PURE__ */ jsxs7("span", { className: "flex justify-between text-slate-400", children: [
|
|
1156
|
+
/* @__PURE__ */ jsx7("span", { children: "Tolerance" }),
|
|
1157
|
+
/* @__PURE__ */ jsx7("span", { className: "font-mono", children: options.tolerance })
|
|
662
1158
|
] }),
|
|
663
|
-
/* @__PURE__ */
|
|
1159
|
+
/* @__PURE__ */ jsx7(
|
|
664
1160
|
"input",
|
|
665
1161
|
{
|
|
666
1162
|
type: "range",
|
|
@@ -674,29 +1170,45 @@ var SelectionOptionsBar = ({
|
|
|
674
1170
|
}
|
|
675
1171
|
)
|
|
676
1172
|
] }),
|
|
677
|
-
/* @__PURE__ */
|
|
678
|
-
/* @__PURE__ */
|
|
1173
|
+
/* @__PURE__ */ jsxs7("label", { className: "flex items-center gap-2 text-slate-300", children: [
|
|
1174
|
+
/* @__PURE__ */ jsx7("input", { type: "checkbox", checked: options.contiguous, onChange: (e) => set({ contiguous: e.target.checked }) }),
|
|
679
1175
|
"Contiguous"
|
|
680
1176
|
] }),
|
|
681
|
-
/* @__PURE__ */
|
|
682
|
-
/* @__PURE__ */
|
|
1177
|
+
/* @__PURE__ */ jsxs7("label", { className: "flex items-center gap-2 text-slate-300", children: [
|
|
1178
|
+
/* @__PURE__ */ jsx7("input", { type: "checkbox", checked: options.sampleMerged, onChange: (e) => set({ sampleMerged: e.target.checked }) }),
|
|
683
1179
|
"Sample adjusted image"
|
|
1180
|
+
] }),
|
|
1181
|
+
/* @__PURE__ */ jsxs7("label", { className: "flex items-center gap-2 text-slate-300", children: [
|
|
1182
|
+
"Sample",
|
|
1183
|
+
/* @__PURE__ */ jsxs7(
|
|
1184
|
+
"select",
|
|
1185
|
+
{
|
|
1186
|
+
"aria-label": "Sample from",
|
|
1187
|
+
value: options.sample ?? "composite",
|
|
1188
|
+
onChange: (e) => set({ sample: e.target.value }),
|
|
1189
|
+
className: "bg-slate-800 border border-slate-700 rounded px-1 py-0.5",
|
|
1190
|
+
children: [
|
|
1191
|
+
/* @__PURE__ */ jsx7("option", { value: "composite", children: "All layers" }),
|
|
1192
|
+
/* @__PURE__ */ jsx7("option", { value: "active-layer", children: "Active layer" })
|
|
1193
|
+
]
|
|
1194
|
+
}
|
|
1195
|
+
)
|
|
684
1196
|
] })
|
|
685
1197
|
] }),
|
|
686
|
-
/* @__PURE__ */
|
|
687
|
-
/* @__PURE__ */
|
|
1198
|
+
/* @__PURE__ */ jsxs7("label", { className: "flex items-center gap-2 text-slate-300", children: [
|
|
1199
|
+
/* @__PURE__ */ jsx7("input", { type: "checkbox", checked: options.antiAlias, onChange: (e) => set({ antiAlias: e.target.checked }) }),
|
|
688
1200
|
"Anti-alias"
|
|
689
1201
|
] }),
|
|
690
|
-
/* @__PURE__ */
|
|
691
|
-
/* @__PURE__ */
|
|
692
|
-
/* @__PURE__ */
|
|
693
|
-
/* @__PURE__ */
|
|
694
|
-
/* @__PURE__ */
|
|
1202
|
+
/* @__PURE__ */ jsxs7("div", { className: "flex flex-wrap gap-1", children: [
|
|
1203
|
+
/* @__PURE__ */ jsx7("button", { type: "button", className: btn(false), onClick: () => controller.selectAll(), children: "Select All" }),
|
|
1204
|
+
/* @__PURE__ */ jsx7("button", { type: "button", className: btn(false), disabled: !sel, onClick: () => controller.deselect(), children: "Deselect" }),
|
|
1205
|
+
/* @__PURE__ */ jsx7("button", { type: "button", className: btn(false), onClick: () => controller.invertSelection(), children: "Inverse" }),
|
|
1206
|
+
/* @__PURE__ */ jsx7("button", { type: "button", "aria-pressed": qm, className: btn(qm), onClick: () => controller.setQuickMask(!qm), children: "Quick Mask" })
|
|
695
1207
|
] }),
|
|
696
|
-
/* @__PURE__ */
|
|
697
|
-
/* @__PURE__ */
|
|
1208
|
+
/* @__PURE__ */ jsxs7("div", { className: "flex flex-wrap items-center gap-1", children: [
|
|
1209
|
+
/* @__PURE__ */ jsxs7("label", { className: "flex items-center gap-1 text-slate-400", children: [
|
|
698
1210
|
"Radius",
|
|
699
|
-
/* @__PURE__ */
|
|
1211
|
+
/* @__PURE__ */ jsx7(
|
|
700
1212
|
"input",
|
|
701
1213
|
{
|
|
702
1214
|
type: "number",
|
|
@@ -710,29 +1222,29 @@ var SelectionOptionsBar = ({
|
|
|
710
1222
|
}
|
|
711
1223
|
)
|
|
712
1224
|
] }),
|
|
713
|
-
MODIFY_OPS.map(({ op, label }) => /* @__PURE__ */
|
|
1225
|
+
MODIFY_OPS.map(({ op, label }) => /* @__PURE__ */ jsx7("button", { type: "button", className: btn(false), disabled: !sel, onClick: () => controller.modifySelection(op, radius), children: label }, op))
|
|
714
1226
|
] }),
|
|
715
|
-
/* @__PURE__ */
|
|
1227
|
+
/* @__PURE__ */ jsx7("p", { "data-testid": "selection-status", className: "text-slate-400", children: describeSelection(sel) })
|
|
716
1228
|
] });
|
|
717
1229
|
};
|
|
718
1230
|
|
|
719
1231
|
// src/image-editor/RefineEdgePanel.tsx
|
|
720
|
-
import { useState as
|
|
1232
|
+
import { useState as useState8 } from "react";
|
|
721
1233
|
import {
|
|
722
1234
|
ADJUSTMENT_KINDS as ADJUSTMENT_KINDS2,
|
|
723
1235
|
REFINE_EDGE_CONTROLS,
|
|
724
1236
|
adjustmentKindLabel as adjustmentKindLabel2,
|
|
725
1237
|
layerMaskFromSelection
|
|
726
1238
|
} from "@pixerate/editor/image-editor";
|
|
727
|
-
import { Fragment, jsx as
|
|
1239
|
+
import { Fragment as Fragment2, jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
728
1240
|
var DEFAULT_PARAMS = { radius: 8, smartRadius: false, smooth: 0, feather: 0, contrast: 0, shiftEdge: 0 };
|
|
729
1241
|
var button = "px-2 py-1 rounded-md border border-slate-800 bg-slate-950 text-[11px] text-slate-200 hover:text-white disabled:opacity-40";
|
|
730
1242
|
var RefineEdgePanel = ({ controller, selection, layers, className = "" }) => {
|
|
731
|
-
const [params, setParams] =
|
|
732
|
-
const [busy, setBusy] =
|
|
733
|
-
const [error, setError] =
|
|
734
|
-
const [layerId, setLayerId] =
|
|
735
|
-
const [newKind, setNewKind] =
|
|
1243
|
+
const [params, setParams] = useState8(DEFAULT_PARAMS);
|
|
1244
|
+
const [busy, setBusy] = useState8(false);
|
|
1245
|
+
const [error, setError] = useState8(null);
|
|
1246
|
+
const [layerId, setLayerId] = useState8("");
|
|
1247
|
+
const [newKind, setNewKind] = useState8("hueSaturation");
|
|
736
1248
|
const sel = selection === void 0 ? controller.getSelection() : selection;
|
|
737
1249
|
const list = layers ?? controller.getAdjustmentLayers();
|
|
738
1250
|
const target = list.find((l) => l.id === layerId) ?? list[list.length - 1];
|
|
@@ -747,17 +1259,17 @@ var RefineEdgePanel = ({ controller, selection, layers, className = "" }) => {
|
|
|
747
1259
|
setBusy(false);
|
|
748
1260
|
}
|
|
749
1261
|
};
|
|
750
|
-
return /* @__PURE__ */
|
|
751
|
-
/* @__PURE__ */
|
|
752
|
-
REFINE_EDGE_CONTROLS.map((c) => /* @__PURE__ */
|
|
753
|
-
/* @__PURE__ */
|
|
754
|
-
/* @__PURE__ */
|
|
755
|
-
/* @__PURE__ */
|
|
1262
|
+
return /* @__PURE__ */ jsxs8("div", { "data-testid": "refine-edge-panel", className: `flex flex-col gap-3 text-xs ${className}`, children: [
|
|
1263
|
+
/* @__PURE__ */ jsx8("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Refine Edge" }),
|
|
1264
|
+
REFINE_EDGE_CONTROLS.map((c) => /* @__PURE__ */ jsxs8("label", { className: "flex flex-col gap-1", children: [
|
|
1265
|
+
/* @__PURE__ */ jsxs8("span", { className: "flex justify-between text-slate-400", children: [
|
|
1266
|
+
/* @__PURE__ */ jsx8("span", { children: c.label }),
|
|
1267
|
+
/* @__PURE__ */ jsxs8("span", { className: "font-mono", children: [
|
|
756
1268
|
params[c.id],
|
|
757
1269
|
c.unit
|
|
758
1270
|
] })
|
|
759
1271
|
] }),
|
|
760
|
-
/* @__PURE__ */
|
|
1272
|
+
/* @__PURE__ */ jsx8(
|
|
761
1273
|
"input",
|
|
762
1274
|
{
|
|
763
1275
|
type: "range",
|
|
@@ -771,28 +1283,28 @@ var RefineEdgePanel = ({ controller, selection, layers, className = "" }) => {
|
|
|
771
1283
|
}
|
|
772
1284
|
)
|
|
773
1285
|
] }, c.id)),
|
|
774
|
-
/* @__PURE__ */
|
|
775
|
-
/* @__PURE__ */
|
|
1286
|
+
/* @__PURE__ */ jsxs8("label", { className: "flex items-center gap-2 text-slate-300", children: [
|
|
1287
|
+
/* @__PURE__ */ jsx8("input", { type: "checkbox", checked: params.smartRadius, onChange: (e) => setParams({ ...params, smartRadius: e.target.checked }) }),
|
|
776
1288
|
"Smart Radius"
|
|
777
1289
|
] }),
|
|
778
|
-
/* @__PURE__ */
|
|
779
|
-
/* @__PURE__ */
|
|
780
|
-
/* @__PURE__ */
|
|
1290
|
+
/* @__PURE__ */ jsxs8("div", { className: "flex gap-1", children: [
|
|
1291
|
+
/* @__PURE__ */ jsx8("button", { type: "button", className: button, disabled: !sel || busy, onClick: refine, children: "Refine Edge" }),
|
|
1292
|
+
/* @__PURE__ */ jsx8("button", { type: "button", className: button, onClick: () => setParams(DEFAULT_PARAMS), children: "Reset" })
|
|
781
1293
|
] }),
|
|
782
|
-
error && /* @__PURE__ */
|
|
783
|
-
/* @__PURE__ */
|
|
784
|
-
/* @__PURE__ */
|
|
785
|
-
/* @__PURE__ */
|
|
1294
|
+
error && /* @__PURE__ */ jsx8("p", { role: "alert", className: "text-rose-400", children: error }),
|
|
1295
|
+
/* @__PURE__ */ jsx8("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px] mt-2", children: "Layer Mask" }),
|
|
1296
|
+
/* @__PURE__ */ jsxs8("div", { className: "flex gap-1 items-center", children: [
|
|
1297
|
+
/* @__PURE__ */ jsx8(
|
|
786
1298
|
"select",
|
|
787
1299
|
{
|
|
788
1300
|
"aria-label": "New masked layer kind",
|
|
789
1301
|
value: newKind,
|
|
790
1302
|
onChange: (e) => setNewKind(e.target.value),
|
|
791
1303
|
className: "flex-1 bg-slate-950 border border-slate-800 rounded px-1 py-1 text-slate-200",
|
|
792
|
-
children: ADJUSTMENT_KINDS2.map((k) => /* @__PURE__ */
|
|
1304
|
+
children: ADJUSTMENT_KINDS2.map((k) => /* @__PURE__ */ jsx8("option", { value: k.kind, children: k.label }, k.kind))
|
|
793
1305
|
}
|
|
794
1306
|
),
|
|
795
|
-
/* @__PURE__ */
|
|
1307
|
+
/* @__PURE__ */ jsx8(
|
|
796
1308
|
"button",
|
|
797
1309
|
{
|
|
798
1310
|
type: "button",
|
|
@@ -803,35 +1315,35 @@ var RefineEdgePanel = ({ controller, selection, layers, className = "" }) => {
|
|
|
803
1315
|
}
|
|
804
1316
|
)
|
|
805
1317
|
] }),
|
|
806
|
-
list.length > 0 && target && /* @__PURE__ */
|
|
807
|
-
/* @__PURE__ */
|
|
1318
|
+
list.length > 0 && target && /* @__PURE__ */ jsxs8("div", { className: "flex flex-col gap-2", children: [
|
|
1319
|
+
/* @__PURE__ */ jsx8(
|
|
808
1320
|
"select",
|
|
809
1321
|
{
|
|
810
1322
|
"aria-label": "Mask layer",
|
|
811
1323
|
value: target.id,
|
|
812
1324
|
onChange: (e) => setLayerId(e.target.value),
|
|
813
1325
|
className: "bg-slate-950 border border-slate-800 rounded px-1 py-1 text-slate-200",
|
|
814
|
-
children: list.map((l) => /* @__PURE__ */
|
|
1326
|
+
children: list.map((l) => /* @__PURE__ */ jsxs8("option", { value: l.id, children: [
|
|
815
1327
|
l.name || adjustmentKindLabel2(l.kind),
|
|
816
1328
|
l.mask ? " (masked)" : ""
|
|
817
1329
|
] }, l.id))
|
|
818
1330
|
}
|
|
819
1331
|
),
|
|
820
|
-
/* @__PURE__ */
|
|
821
|
-
/* @__PURE__ */
|
|
822
|
-
/* @__PURE__ */
|
|
823
|
-
/* @__PURE__ */
|
|
1332
|
+
/* @__PURE__ */ jsxs8("div", { className: "flex flex-wrap gap-1", children: [
|
|
1333
|
+
/* @__PURE__ */ jsx8("button", { type: "button", className: button, disabled: !sel, onClick: () => controller.applySelectionAsLayerMask(target.id), children: "Mask with Selection" }),
|
|
1334
|
+
/* @__PURE__ */ jsx8("button", { type: "button", className: button, disabled: !target.mask, onClick: () => controller.selectionFromLayerMask(target.id), children: "Load as Selection" }),
|
|
1335
|
+
/* @__PURE__ */ jsx8("button", { type: "button", className: button, disabled: !target.mask, onClick: () => controller.setAdjustmentLayerMask(target.id, null), children: "Remove Mask" })
|
|
824
1336
|
] }),
|
|
825
|
-
target.mask && /* @__PURE__ */
|
|
826
|
-
/* @__PURE__ */
|
|
827
|
-
/* @__PURE__ */
|
|
828
|
-
/* @__PURE__ */
|
|
829
|
-
/* @__PURE__ */
|
|
1337
|
+
target.mask && /* @__PURE__ */ jsxs8(Fragment2, { children: [
|
|
1338
|
+
/* @__PURE__ */ jsxs8("label", { className: "flex flex-col gap-1", children: [
|
|
1339
|
+
/* @__PURE__ */ jsxs8("span", { className: "flex justify-between text-slate-400", children: [
|
|
1340
|
+
/* @__PURE__ */ jsx8("span", { children: "Density" }),
|
|
1341
|
+
/* @__PURE__ */ jsxs8("span", { className: "font-mono", children: [
|
|
830
1342
|
Math.round((target.mask.density ?? 1) * 100),
|
|
831
1343
|
"%"
|
|
832
1344
|
] })
|
|
833
1345
|
] }),
|
|
834
|
-
/* @__PURE__ */
|
|
1346
|
+
/* @__PURE__ */ jsx8(
|
|
835
1347
|
"input",
|
|
836
1348
|
{
|
|
837
1349
|
type: "range",
|
|
@@ -845,15 +1357,15 @@ var RefineEdgePanel = ({ controller, selection, layers, className = "" }) => {
|
|
|
845
1357
|
}
|
|
846
1358
|
)
|
|
847
1359
|
] }),
|
|
848
|
-
/* @__PURE__ */
|
|
849
|
-
/* @__PURE__ */
|
|
850
|
-
/* @__PURE__ */
|
|
851
|
-
/* @__PURE__ */
|
|
1360
|
+
/* @__PURE__ */ jsxs8("label", { className: "flex flex-col gap-1", children: [
|
|
1361
|
+
/* @__PURE__ */ jsxs8("span", { className: "flex justify-between text-slate-400", children: [
|
|
1362
|
+
/* @__PURE__ */ jsx8("span", { children: "Mask Feather" }),
|
|
1363
|
+
/* @__PURE__ */ jsxs8("span", { className: "font-mono", children: [
|
|
852
1364
|
target.mask.feather ?? 0,
|
|
853
1365
|
"px"
|
|
854
1366
|
] })
|
|
855
1367
|
] }),
|
|
856
|
-
/* @__PURE__ */
|
|
1368
|
+
/* @__PURE__ */ jsx8(
|
|
857
1369
|
"input",
|
|
858
1370
|
{
|
|
859
1371
|
type: "range",
|
|
@@ -873,7 +1385,7 @@ var RefineEdgePanel = ({ controller, selection, layers, className = "" }) => {
|
|
|
873
1385
|
};
|
|
874
1386
|
|
|
875
1387
|
// src/image-editor/ImageEditor.tsx
|
|
876
|
-
import { Fragment as
|
|
1388
|
+
import { Fragment as Fragment3, jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
877
1389
|
var ImageEditor = ({
|
|
878
1390
|
editor: externalEditor,
|
|
879
1391
|
src,
|
|
@@ -889,25 +1401,35 @@ var ImageEditor = ({
|
|
|
889
1401
|
const editor = externalEditor || internalEditor;
|
|
890
1402
|
const { state, controller } = editor;
|
|
891
1403
|
const legacyProcess = normalizeProcessVersion(state.adjustments.process) < LATEST_PROCESS_VERSION;
|
|
892
|
-
const canvasRef =
|
|
893
|
-
const containerRef =
|
|
894
|
-
const [activeTab, setActiveTab] =
|
|
895
|
-
const [activeCropRatio, setActiveCropRatio] =
|
|
896
|
-
const [selectionOptions, setSelectionOptions] =
|
|
897
|
-
const selectionOptionsRef =
|
|
1404
|
+
const canvasRef = useRef4(null);
|
|
1405
|
+
const containerRef = useRef4(null);
|
|
1406
|
+
const [activeTab, setActiveTab] = useState9("crop");
|
|
1407
|
+
const [activeCropRatio, setActiveCropRatio] = useState9(null);
|
|
1408
|
+
const [selectionOptions, setSelectionOptions] = useState9(DEFAULT_SELECTION_TOOL_OPTIONS);
|
|
1409
|
+
const selectionOptionsRef = useRef4(selectionOptions);
|
|
898
1410
|
selectionOptionsRef.current = selectionOptions;
|
|
899
|
-
const selectionGestureRef =
|
|
1411
|
+
const selectionGestureRef = useRef4(null);
|
|
900
1412
|
if (!selectionGestureRef.current || selectionGestureRef.current.controller !== controller) {
|
|
901
1413
|
selectionGestureRef.current = new SelectionGesture(controller, () => selectionOptionsRef.current);
|
|
902
1414
|
}
|
|
903
|
-
const selectionDraftRef =
|
|
904
|
-
const [selectionError, setSelectionError] =
|
|
905
|
-
const
|
|
906
|
-
const
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
1415
|
+
const selectionDraftRef = useRef4(null);
|
|
1416
|
+
const [selectionError, setSelectionError] = useState9(null);
|
|
1417
|
+
const paintGestureRef = useRef4(null);
|
|
1418
|
+
const paintControllerRef = useRef4(controller);
|
|
1419
|
+
if (!paintGestureRef.current || paintControllerRef.current !== controller) {
|
|
1420
|
+
paintGestureRef.current = new PaintGesture(controller);
|
|
1421
|
+
paintControllerRef.current = controller;
|
|
1422
|
+
}
|
|
1423
|
+
const brushCursorRef = useRef4(null);
|
|
1424
|
+
const [paintError, setPaintError] = useState9(null);
|
|
1425
|
+
const [inpaintPrompt, setInpaintPrompt] = useState9("");
|
|
1426
|
+
const [inpaintMaskMode, setInpaintMaskMode] = useState9("annotations");
|
|
1427
|
+
const [isRemovingBg, setIsRemovingBg] = useState9(false);
|
|
1428
|
+
const [isInpainting, setIsInpainting] = useState9(false);
|
|
1429
|
+
const [aiError, setAiError] = useState9(null);
|
|
1430
|
+
const [aiSuccess, setAiSuccess] = useState9(null);
|
|
1431
|
+
const [aiTarget, setAiTarget] = useState9("auto");
|
|
1432
|
+
const aiOptions = aiTarget === "auto" ? {} : { target: aiTarget };
|
|
911
1433
|
const effectiveOnInpaint = onInpaint || aiHooks?.onInpaint;
|
|
912
1434
|
const effectiveOnRemoveBg = onRemoveBackground || aiHooks?.onRemoveBackground;
|
|
913
1435
|
const handleRemoveBackground = async () => {
|
|
@@ -919,10 +1441,10 @@ var ImageEditor = ({
|
|
|
919
1441
|
setAiSuccess(null);
|
|
920
1442
|
setIsRemovingBg(true);
|
|
921
1443
|
try {
|
|
922
|
-
const imageDataUrl = await
|
|
1444
|
+
const imageDataUrl = await controller.toAIInputDataURL(aiOptions);
|
|
923
1445
|
const result = await effectiveOnRemoveBg({ imageDataUrl });
|
|
924
1446
|
if (typeof result === "string") {
|
|
925
|
-
await editor.applyBackgroundRemovedImage(result);
|
|
1447
|
+
await editor.applyBackgroundRemovedImage(result, aiOptions);
|
|
926
1448
|
setAiSuccess("Background removed successfully!");
|
|
927
1449
|
} else {
|
|
928
1450
|
setAiSuccess("Background removal complete.");
|
|
@@ -946,7 +1468,7 @@ var ImageEditor = ({
|
|
|
946
1468
|
setAiSuccess(null);
|
|
947
1469
|
setIsInpainting(true);
|
|
948
1470
|
try {
|
|
949
|
-
const imageDataUrl = await
|
|
1471
|
+
const imageDataUrl = await controller.toAIInputDataURL(aiOptions);
|
|
950
1472
|
const maskDataUrl = await editor.toMaskDataURL({
|
|
951
1473
|
useAnnotations: inpaintMaskMode === "annotations",
|
|
952
1474
|
useCrop: inpaintMaskMode === "crop"
|
|
@@ -958,7 +1480,7 @@ var ImageEditor = ({
|
|
|
958
1480
|
cropBox: inpaintMaskMode === "crop" ? editor.state.crop : null
|
|
959
1481
|
});
|
|
960
1482
|
if (typeof result === "string") {
|
|
961
|
-
await editor.applyInpaintedImage(result);
|
|
1483
|
+
await editor.applyInpaintedImage(result, aiOptions);
|
|
962
1484
|
setAiSuccess("Inpainting applied successfully!");
|
|
963
1485
|
} else {
|
|
964
1486
|
setAiSuccess("Inpainting complete.");
|
|
@@ -969,17 +1491,17 @@ var ImageEditor = ({
|
|
|
969
1491
|
setIsInpainting(false);
|
|
970
1492
|
}
|
|
971
1493
|
};
|
|
972
|
-
const [activeColor, setActiveColor] =
|
|
973
|
-
const [strokeWidth, setStrokeWidth] =
|
|
974
|
-
const [fontSize, setFontSize] =
|
|
975
|
-
const [textInput, setTextInput] =
|
|
976
|
-
const draftAnnotationRef =
|
|
977
|
-
const draftCropRef =
|
|
978
|
-
const cropDragInfoRef =
|
|
979
|
-
const annotationDragInfoRef =
|
|
980
|
-
const isDrawingRef =
|
|
981
|
-
const currentPathRef =
|
|
982
|
-
const dragStartPointRef =
|
|
1494
|
+
const [activeColor, setActiveColor] = useState9("#ef4444");
|
|
1495
|
+
const [strokeWidth, setStrokeWidth] = useState9(3);
|
|
1496
|
+
const [fontSize, setFontSize] = useState9(24);
|
|
1497
|
+
const [textInput, setTextInput] = useState9("");
|
|
1498
|
+
const draftAnnotationRef = useRef4(null);
|
|
1499
|
+
const draftCropRef = useRef4(null);
|
|
1500
|
+
const cropDragInfoRef = useRef4(null);
|
|
1501
|
+
const annotationDragInfoRef = useRef4(null);
|
|
1502
|
+
const isDrawingRef = useRef4(false);
|
|
1503
|
+
const currentPathRef = useRef4([]);
|
|
1504
|
+
const dragStartPointRef = useRef4(null);
|
|
983
1505
|
const renderCanvas = useCallback2(
|
|
984
1506
|
async (draftAnnotation, draftCrop) => {
|
|
985
1507
|
const canvas = canvasRef.current;
|
|
@@ -995,12 +1517,13 @@ var ImageEditor = ({
|
|
|
995
1517
|
interactive: true,
|
|
996
1518
|
draftAnnotation: draftAnnotation !== void 0 ? draftAnnotation : draftAnnotationRef.current,
|
|
997
1519
|
draftCrop: draftCrop !== void 0 ? draftCrop : draftCropRef.current,
|
|
998
|
-
draftSelectionPath: selectionDraftRef.current
|
|
1520
|
+
draftSelectionPath: selectionDraftRef.current,
|
|
1521
|
+
brushCursor: activeTab === "paint" && brushCursorRef.current && controller.getState().activeTool !== "move" ? { ...brushCursorRef.current, diameter: controller.getState().paint.brush.size } : null
|
|
999
1522
|
});
|
|
1000
1523
|
},
|
|
1001
1524
|
[controller, activeTab]
|
|
1002
1525
|
);
|
|
1003
|
-
|
|
1526
|
+
useEffect6(() => {
|
|
1004
1527
|
const raf = typeof requestAnimationFrame !== "undefined" ? requestAnimationFrame : (cb) => setTimeout(cb, 0);
|
|
1005
1528
|
const caf = typeof cancelAnimationFrame !== "undefined" ? cancelAnimationFrame : (id) => clearTimeout(id);
|
|
1006
1529
|
const animId = raf(() => {
|
|
@@ -1008,12 +1531,12 @@ var ImageEditor = ({
|
|
|
1008
1531
|
});
|
|
1009
1532
|
return () => caf(animId);
|
|
1010
1533
|
}, [renderCanvas, state]);
|
|
1011
|
-
|
|
1534
|
+
useEffect6(() => {
|
|
1012
1535
|
if (activeTab === "crop" && editor.state.activeTool !== "crop") {
|
|
1013
1536
|
editor.setTool("crop");
|
|
1014
1537
|
}
|
|
1015
1538
|
}, [activeTab, editor]);
|
|
1016
|
-
|
|
1539
|
+
useEffect6(() => {
|
|
1017
1540
|
const container = containerRef.current;
|
|
1018
1541
|
if (!container) return;
|
|
1019
1542
|
if (typeof ResizeObserver !== "undefined") {
|
|
@@ -1039,6 +1562,53 @@ var ImageEditor = ({
|
|
|
1039
1562
|
y: (e.clientY - rect.top) * scaleY
|
|
1040
1563
|
};
|
|
1041
1564
|
};
|
|
1565
|
+
const paintSamples = (e) => {
|
|
1566
|
+
const canvas = canvasRef.current;
|
|
1567
|
+
const native = e.nativeEvent;
|
|
1568
|
+
const events = typeof native.getCoalescedEvents === "function" ? native.getCoalescedEvents() : [];
|
|
1569
|
+
const list = events.length ? events : [native];
|
|
1570
|
+
const rect = canvas.getBoundingClientRect();
|
|
1571
|
+
const sx = rect.width > 0 ? canvas.width / rect.width : 1;
|
|
1572
|
+
const sy = rect.height > 0 ? canvas.height / rect.height : 1;
|
|
1573
|
+
const s = controller.getState();
|
|
1574
|
+
return list.map((ev) => ({
|
|
1575
|
+
...canvasToImagePoint({ x: (ev.clientX - rect.left) * sx, y: (ev.clientY - rect.top) * sy }, s, canvas.width, canvas.height, false),
|
|
1576
|
+
pressure: pointerPressure(ev)
|
|
1577
|
+
}));
|
|
1578
|
+
};
|
|
1579
|
+
const handlePaintPointerDown = (e) => {
|
|
1580
|
+
if (activeTab !== "paint" || e.button !== 0) return;
|
|
1581
|
+
e.preventDefault();
|
|
1582
|
+
try {
|
|
1583
|
+
e.currentTarget.setPointerCapture?.(e.pointerId);
|
|
1584
|
+
} catch {
|
|
1585
|
+
}
|
|
1586
|
+
const [first] = paintSamples(e);
|
|
1587
|
+
setPaintError(paintGestureRef.current.down(first));
|
|
1588
|
+
};
|
|
1589
|
+
const handlePaintPointerMove = (e) => {
|
|
1590
|
+
if (activeTab !== "paint") return;
|
|
1591
|
+
const samples = paintSamples(e);
|
|
1592
|
+
brushCursorRef.current = samples[samples.length - 1] ?? null;
|
|
1593
|
+
if (paintGestureRef.current.active) paintGestureRef.current.moveTo(samples);
|
|
1594
|
+
else void renderCanvas();
|
|
1595
|
+
};
|
|
1596
|
+
const handlePaintPointerUp = () => {
|
|
1597
|
+
if (activeTab !== "paint") return;
|
|
1598
|
+
const g = paintGestureRef.current;
|
|
1599
|
+
if (g.active) {
|
|
1600
|
+
g.up();
|
|
1601
|
+
if (g.error) setPaintError(g.error);
|
|
1602
|
+
}
|
|
1603
|
+
};
|
|
1604
|
+
useEffect6(() => {
|
|
1605
|
+
if (activeTab !== "paint" || typeof window === "undefined") return;
|
|
1606
|
+
const onKey = (e) => {
|
|
1607
|
+
if (handlePaintShortcut(controller, e)) e.preventDefault();
|
|
1608
|
+
};
|
|
1609
|
+
window.addEventListener("keydown", onKey);
|
|
1610
|
+
return () => window.removeEventListener("keydown", onKey);
|
|
1611
|
+
}, [activeTab, controller]);
|
|
1042
1612
|
const handleMouseDown = (e) => {
|
|
1043
1613
|
const canvas = canvasRef.current;
|
|
1044
1614
|
if (!canvas) return;
|
|
@@ -1394,15 +1964,15 @@ var ImageEditor = ({
|
|
|
1394
1964
|
link.click();
|
|
1395
1965
|
}
|
|
1396
1966
|
};
|
|
1397
|
-
return /* @__PURE__ */
|
|
1967
|
+
return /* @__PURE__ */ jsxs9(
|
|
1398
1968
|
"div",
|
|
1399
1969
|
{
|
|
1400
1970
|
className: `flex flex-col bg-slate-900 border border-slate-800 rounded-xl overflow-hidden shadow-2xl text-slate-100 ${className}`,
|
|
1401
1971
|
style,
|
|
1402
1972
|
children: [
|
|
1403
|
-
/* @__PURE__ */
|
|
1404
|
-
/* @__PURE__ */
|
|
1405
|
-
/* @__PURE__ */
|
|
1973
|
+
/* @__PURE__ */ jsxs9("div", { className: "flex flex-wrap items-center justify-between px-4 py-3 bg-slate-950/80 border-b border-slate-800 gap-3", children: [
|
|
1974
|
+
/* @__PURE__ */ jsxs9("div", { className: "flex items-center gap-1 bg-slate-900 p-1 rounded-lg border border-slate-800 text-xs", children: [
|
|
1975
|
+
/* @__PURE__ */ jsx9(
|
|
1406
1976
|
"button",
|
|
1407
1977
|
{
|
|
1408
1978
|
type: "button",
|
|
@@ -1414,7 +1984,7 @@ var ImageEditor = ({
|
|
|
1414
1984
|
children: "Transform & Crop"
|
|
1415
1985
|
}
|
|
1416
1986
|
),
|
|
1417
|
-
/* @__PURE__ */
|
|
1987
|
+
/* @__PURE__ */ jsx9(
|
|
1418
1988
|
"button",
|
|
1419
1989
|
{
|
|
1420
1990
|
type: "button",
|
|
@@ -1426,7 +1996,31 @@ var ImageEditor = ({
|
|
|
1426
1996
|
children: "Adjustments"
|
|
1427
1997
|
}
|
|
1428
1998
|
),
|
|
1429
|
-
/* @__PURE__ */
|
|
1999
|
+
/* @__PURE__ */ jsx9(
|
|
2000
|
+
"button",
|
|
2001
|
+
{
|
|
2002
|
+
type: "button",
|
|
2003
|
+
onClick: () => {
|
|
2004
|
+
setActiveTab("layers");
|
|
2005
|
+
editor.setTool("select");
|
|
2006
|
+
},
|
|
2007
|
+
className: `px-3 py-1.5 rounded-md font-medium transition-colors ${activeTab === "layers" ? "bg-indigo-600 text-white shadow" : "text-slate-400 hover:text-slate-200"}`,
|
|
2008
|
+
children: "Layers"
|
|
2009
|
+
}
|
|
2010
|
+
),
|
|
2011
|
+
/* @__PURE__ */ jsx9(
|
|
2012
|
+
"button",
|
|
2013
|
+
{
|
|
2014
|
+
type: "button",
|
|
2015
|
+
onClick: () => {
|
|
2016
|
+
setActiveTab("paint");
|
|
2017
|
+
editor.setTool("brush");
|
|
2018
|
+
},
|
|
2019
|
+
className: `px-3 py-1.5 rounded-md font-medium transition-colors ${activeTab === "paint" ? "bg-indigo-600 text-white shadow" : "text-slate-400 hover:text-slate-200"}`,
|
|
2020
|
+
children: "Paint"
|
|
2021
|
+
}
|
|
2022
|
+
),
|
|
2023
|
+
/* @__PURE__ */ jsx9(
|
|
1430
2024
|
"button",
|
|
1431
2025
|
{
|
|
1432
2026
|
type: "button",
|
|
@@ -1438,7 +2032,7 @@ var ImageEditor = ({
|
|
|
1438
2032
|
children: "Select & Mask"
|
|
1439
2033
|
}
|
|
1440
2034
|
),
|
|
1441
|
-
/* @__PURE__ */
|
|
2035
|
+
/* @__PURE__ */ jsx9(
|
|
1442
2036
|
"button",
|
|
1443
2037
|
{
|
|
1444
2038
|
type: "button",
|
|
@@ -1450,7 +2044,7 @@ var ImageEditor = ({
|
|
|
1450
2044
|
children: "Annotations"
|
|
1451
2045
|
}
|
|
1452
2046
|
),
|
|
1453
|
-
/* @__PURE__ */
|
|
2047
|
+
/* @__PURE__ */ jsx9(
|
|
1454
2048
|
"button",
|
|
1455
2049
|
{
|
|
1456
2050
|
type: "button",
|
|
@@ -1462,7 +2056,7 @@ var ImageEditor = ({
|
|
|
1462
2056
|
children: "Depth Mask"
|
|
1463
2057
|
}
|
|
1464
2058
|
),
|
|
1465
|
-
/* @__PURE__ */
|
|
2059
|
+
/* @__PURE__ */ jsxs9(
|
|
1466
2060
|
"button",
|
|
1467
2061
|
{
|
|
1468
2062
|
type: "button",
|
|
@@ -1472,14 +2066,14 @@ var ImageEditor = ({
|
|
|
1472
2066
|
},
|
|
1473
2067
|
className: `px-3 py-1.5 rounded-md font-medium transition-colors flex items-center gap-1.5 ${activeTab === "ai" ? "bg-indigo-600 text-white shadow" : "text-slate-400 hover:text-slate-200"}`,
|
|
1474
2068
|
children: [
|
|
1475
|
-
/* @__PURE__ */
|
|
2069
|
+
/* @__PURE__ */ jsx9("svg", { className: "w-3.5 h-3.5", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: /* @__PURE__ */ jsx9("path", { d: "M12 2v4M12 18v4M4.93 4.93l2.83 2.83M16.24 16.24l2.83 2.83M2 12h4M18 12h4M4.93 19.07l2.83-2.83M16.24 7.76l2.83-2.83" }) }),
|
|
1476
2070
|
"AI Tools"
|
|
1477
2071
|
]
|
|
1478
2072
|
}
|
|
1479
2073
|
)
|
|
1480
2074
|
] }),
|
|
1481
|
-
/* @__PURE__ */
|
|
1482
|
-
/* @__PURE__ */
|
|
2075
|
+
/* @__PURE__ */ jsxs9("div", { className: "flex items-center gap-2 text-xs", children: [
|
|
2076
|
+
/* @__PURE__ */ jsx9(
|
|
1483
2077
|
"button",
|
|
1484
2078
|
{
|
|
1485
2079
|
type: "button",
|
|
@@ -1490,7 +2084,7 @@ var ImageEditor = ({
|
|
|
1490
2084
|
children: "\u21A9"
|
|
1491
2085
|
}
|
|
1492
2086
|
),
|
|
1493
|
-
/* @__PURE__ */
|
|
2087
|
+
/* @__PURE__ */ jsx9(
|
|
1494
2088
|
"button",
|
|
1495
2089
|
{
|
|
1496
2090
|
type: "button",
|
|
@@ -1501,7 +2095,7 @@ var ImageEditor = ({
|
|
|
1501
2095
|
children: "\u21AA"
|
|
1502
2096
|
}
|
|
1503
2097
|
),
|
|
1504
|
-
/* @__PURE__ */
|
|
2098
|
+
/* @__PURE__ */ jsx9(
|
|
1505
2099
|
"button",
|
|
1506
2100
|
{
|
|
1507
2101
|
type: "button",
|
|
@@ -1510,8 +2104,8 @@ var ImageEditor = ({
|
|
|
1510
2104
|
children: "Reset"
|
|
1511
2105
|
}
|
|
1512
2106
|
),
|
|
1513
|
-
/* @__PURE__ */
|
|
1514
|
-
/* @__PURE__ */
|
|
2107
|
+
/* @__PURE__ */ jsx9("div", { className: "h-4 w-px bg-slate-800 mx-1" }),
|
|
2108
|
+
/* @__PURE__ */ jsx9(
|
|
1515
2109
|
"button",
|
|
1516
2110
|
{
|
|
1517
2111
|
type: "button",
|
|
@@ -1521,11 +2115,11 @@ var ImageEditor = ({
|
|
|
1521
2115
|
children: "-"
|
|
1522
2116
|
}
|
|
1523
2117
|
),
|
|
1524
|
-
/* @__PURE__ */
|
|
2118
|
+
/* @__PURE__ */ jsxs9("span", { className: "font-mono text-slate-400 w-10 text-center", children: [
|
|
1525
2119
|
Math.round(state.zoom * 100),
|
|
1526
2120
|
"%"
|
|
1527
2121
|
] }),
|
|
1528
|
-
/* @__PURE__ */
|
|
2122
|
+
/* @__PURE__ */ jsx9(
|
|
1529
2123
|
"button",
|
|
1530
2124
|
{
|
|
1531
2125
|
type: "button",
|
|
@@ -1535,8 +2129,8 @@ var ImageEditor = ({
|
|
|
1535
2129
|
children: "+"
|
|
1536
2130
|
}
|
|
1537
2131
|
),
|
|
1538
|
-
/* @__PURE__ */
|
|
1539
|
-
/* @__PURE__ */
|
|
2132
|
+
/* @__PURE__ */ jsx9("div", { className: "h-4 w-px bg-slate-800 mx-1" }),
|
|
2133
|
+
/* @__PURE__ */ jsx9(
|
|
1540
2134
|
"button",
|
|
1541
2135
|
{
|
|
1542
2136
|
type: "button",
|
|
@@ -1547,7 +2141,7 @@ var ImageEditor = ({
|
|
|
1547
2141
|
children: "Hold Compare"
|
|
1548
2142
|
}
|
|
1549
2143
|
),
|
|
1550
|
-
/* @__PURE__ */
|
|
2144
|
+
/* @__PURE__ */ jsx9(
|
|
1551
2145
|
"button",
|
|
1552
2146
|
{
|
|
1553
2147
|
type: "button",
|
|
@@ -1558,13 +2152,13 @@ var ImageEditor = ({
|
|
|
1558
2152
|
)
|
|
1559
2153
|
] })
|
|
1560
2154
|
] }),
|
|
1561
|
-
/* @__PURE__ */
|
|
1562
|
-
/* @__PURE__ */
|
|
2155
|
+
/* @__PURE__ */ jsxs9("div", { className: "flex flex-col md:flex-row flex-1 min-h-[520px] md:h-[560px]", children: [
|
|
2156
|
+
/* @__PURE__ */ jsx9(
|
|
1563
2157
|
"div",
|
|
1564
2158
|
{
|
|
1565
2159
|
ref: containerRef,
|
|
1566
2160
|
className: "relative flex-1 h-[360px] md:h-full min-h-[320px] bg-slate-950 overflow-hidden select-none",
|
|
1567
|
-
children: /* @__PURE__ */
|
|
2161
|
+
children: /* @__PURE__ */ jsx9(
|
|
1568
2162
|
"canvas",
|
|
1569
2163
|
{
|
|
1570
2164
|
ref: canvasRef,
|
|
@@ -1572,16 +2166,21 @@ var ImageEditor = ({
|
|
|
1572
2166
|
onMouseMove: handleMouseMove,
|
|
1573
2167
|
onMouseUp: handleMouseUp,
|
|
1574
2168
|
onMouseLeave: handleMouseUp,
|
|
1575
|
-
|
|
2169
|
+
onPointerDown: handlePaintPointerDown,
|
|
2170
|
+
onPointerMove: handlePaintPointerMove,
|
|
2171
|
+
onPointerUp: handlePaintPointerUp,
|
|
2172
|
+
onPointerCancel: () => paintGestureRef.current?.cancel(),
|
|
2173
|
+
"data-testid": "image-editor-canvas",
|
|
2174
|
+
className: `absolute inset-0 w-full h-full block ${activeTab === "paint" ? "touch-none cursor-crosshair" : ""}`
|
|
1576
2175
|
}
|
|
1577
2176
|
)
|
|
1578
2177
|
}
|
|
1579
2178
|
),
|
|
1580
|
-
/* @__PURE__ */
|
|
1581
|
-
activeTab === "crop" && /* @__PURE__ */
|
|
1582
|
-
/* @__PURE__ */
|
|
1583
|
-
/* @__PURE__ */
|
|
1584
|
-
/* @__PURE__ */
|
|
2179
|
+
/* @__PURE__ */ jsxs9("div", { className: "w-full md:w-80 bg-slate-900/90 border-t md:border-t-0 md:border-l border-slate-800 p-4 overflow-y-auto h-auto md:h-full md:max-h-[560px]", children: [
|
|
2180
|
+
activeTab === "crop" && /* @__PURE__ */ jsxs9("div", { className: "flex flex-col gap-4 text-xs", children: [
|
|
2181
|
+
/* @__PURE__ */ jsx9("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Aspect Ratio Presets" }),
|
|
2182
|
+
/* @__PURE__ */ jsxs9("div", { className: "grid grid-cols-3 gap-2", children: [
|
|
2183
|
+
/* @__PURE__ */ jsx9(
|
|
1585
2184
|
"button",
|
|
1586
2185
|
{
|
|
1587
2186
|
type: "button",
|
|
@@ -1593,7 +2192,7 @@ var ImageEditor = ({
|
|
|
1593
2192
|
children: "Freeform"
|
|
1594
2193
|
}
|
|
1595
2194
|
),
|
|
1596
|
-
/* @__PURE__ */
|
|
2195
|
+
/* @__PURE__ */ jsx9(
|
|
1597
2196
|
"button",
|
|
1598
2197
|
{
|
|
1599
2198
|
type: "button",
|
|
@@ -1605,7 +2204,7 @@ var ImageEditor = ({
|
|
|
1605
2204
|
children: "1:1 Square"
|
|
1606
2205
|
}
|
|
1607
2206
|
),
|
|
1608
|
-
/* @__PURE__ */
|
|
2207
|
+
/* @__PURE__ */ jsx9(
|
|
1609
2208
|
"button",
|
|
1610
2209
|
{
|
|
1611
2210
|
type: "button",
|
|
@@ -1617,7 +2216,7 @@ var ImageEditor = ({
|
|
|
1617
2216
|
children: "4:3 Standard"
|
|
1618
2217
|
}
|
|
1619
2218
|
),
|
|
1620
|
-
/* @__PURE__ */
|
|
2219
|
+
/* @__PURE__ */ jsx9(
|
|
1621
2220
|
"button",
|
|
1622
2221
|
{
|
|
1623
2222
|
type: "button",
|
|
@@ -1629,7 +2228,7 @@ var ImageEditor = ({
|
|
|
1629
2228
|
children: "16:9 Wide"
|
|
1630
2229
|
}
|
|
1631
2230
|
),
|
|
1632
|
-
/* @__PURE__ */
|
|
2231
|
+
/* @__PURE__ */ jsx9(
|
|
1633
2232
|
"button",
|
|
1634
2233
|
{
|
|
1635
2234
|
type: "button",
|
|
@@ -1641,7 +2240,7 @@ var ImageEditor = ({
|
|
|
1641
2240
|
children: "3:2 Photo"
|
|
1642
2241
|
}
|
|
1643
2242
|
),
|
|
1644
|
-
/* @__PURE__ */
|
|
2243
|
+
/* @__PURE__ */ jsx9(
|
|
1645
2244
|
"button",
|
|
1646
2245
|
{
|
|
1647
2246
|
type: "button",
|
|
@@ -1654,10 +2253,10 @@ var ImageEditor = ({
|
|
|
1654
2253
|
}
|
|
1655
2254
|
)
|
|
1656
2255
|
] }),
|
|
1657
|
-
/* @__PURE__ */
|
|
1658
|
-
/* @__PURE__ */
|
|
1659
|
-
/* @__PURE__ */
|
|
1660
|
-
/* @__PURE__ */
|
|
2256
|
+
/* @__PURE__ */ jsx9("div", { className: "h-px bg-slate-800 my-1" }),
|
|
2257
|
+
/* @__PURE__ */ jsx9("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Orientation & Flips" }),
|
|
2258
|
+
/* @__PURE__ */ jsxs9("div", { className: "grid grid-cols-2 gap-2", children: [
|
|
2259
|
+
/* @__PURE__ */ jsx9(
|
|
1661
2260
|
"button",
|
|
1662
2261
|
{
|
|
1663
2262
|
type: "button",
|
|
@@ -1666,7 +2265,7 @@ var ImageEditor = ({
|
|
|
1666
2265
|
children: "Rotate -90\xB0"
|
|
1667
2266
|
}
|
|
1668
2267
|
),
|
|
1669
|
-
/* @__PURE__ */
|
|
2268
|
+
/* @__PURE__ */ jsx9(
|
|
1670
2269
|
"button",
|
|
1671
2270
|
{
|
|
1672
2271
|
type: "button",
|
|
@@ -1675,7 +2274,7 @@ var ImageEditor = ({
|
|
|
1675
2274
|
children: "Rotate +90\xB0"
|
|
1676
2275
|
}
|
|
1677
2276
|
),
|
|
1678
|
-
/* @__PURE__ */
|
|
2277
|
+
/* @__PURE__ */ jsx9(
|
|
1679
2278
|
"button",
|
|
1680
2279
|
{
|
|
1681
2280
|
type: "button",
|
|
@@ -1684,7 +2283,7 @@ var ImageEditor = ({
|
|
|
1684
2283
|
children: "Flip Horizontal"
|
|
1685
2284
|
}
|
|
1686
2285
|
),
|
|
1687
|
-
/* @__PURE__ */
|
|
2286
|
+
/* @__PURE__ */ jsx9(
|
|
1688
2287
|
"button",
|
|
1689
2288
|
{
|
|
1690
2289
|
type: "button",
|
|
@@ -1695,10 +2294,10 @@ var ImageEditor = ({
|
|
|
1695
2294
|
)
|
|
1696
2295
|
] })
|
|
1697
2296
|
] }),
|
|
1698
|
-
activeTab === "adjust" && /* @__PURE__ */
|
|
1699
|
-
/* @__PURE__ */
|
|
1700
|
-
/* @__PURE__ */
|
|
1701
|
-
/* @__PURE__ */
|
|
2297
|
+
activeTab === "adjust" && /* @__PURE__ */ jsxs9("div", { className: "flex flex-col gap-3 text-xs", children: [
|
|
2298
|
+
/* @__PURE__ */ jsxs9("div", { className: "flex items-center justify-between", children: [
|
|
2299
|
+
/* @__PURE__ */ jsx9("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Color Adjustments" }),
|
|
2300
|
+
/* @__PURE__ */ jsx9(
|
|
1702
2301
|
"button",
|
|
1703
2302
|
{
|
|
1704
2303
|
type: "button",
|
|
@@ -1708,9 +2307,9 @@ var ImageEditor = ({
|
|
|
1708
2307
|
}
|
|
1709
2308
|
)
|
|
1710
2309
|
] }),
|
|
1711
|
-
/* @__PURE__ */
|
|
1712
|
-
IMAGE_ADJUSTMENT_CONTROLS.map((spec) => /* @__PURE__ */
|
|
1713
|
-
/* @__PURE__ */
|
|
2310
|
+
/* @__PURE__ */ jsx9(Histogram, { histogram: editor.histogram }),
|
|
2311
|
+
IMAGE_ADJUSTMENT_CONTROLS.map((spec) => /* @__PURE__ */ jsxs9(React8.Fragment, { children: [
|
|
2312
|
+
/* @__PURE__ */ jsx9(
|
|
1714
2313
|
ControlSlider,
|
|
1715
2314
|
{
|
|
1716
2315
|
spec,
|
|
@@ -1719,14 +2318,14 @@ var ImageEditor = ({
|
|
|
1719
2318
|
onChange: (v) => editor.setAdjustments({ [spec.id]: v })
|
|
1720
2319
|
}
|
|
1721
2320
|
),
|
|
1722
|
-
spec.id === "tint" && legacyProcess && /* @__PURE__ */
|
|
2321
|
+
spec.id === "tint" && legacyProcess && /* @__PURE__ */ jsxs9(
|
|
1723
2322
|
"div",
|
|
1724
2323
|
{
|
|
1725
2324
|
className: "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",
|
|
1726
2325
|
"data-testid": "image-editor-process-notice",
|
|
1727
2326
|
children: [
|
|
1728
|
-
/* @__PURE__ */
|
|
1729
|
-
/* @__PURE__ */
|
|
2327
|
+
/* @__PURE__ */ jsx9("span", { children: "Legacy rendering (process 1). Tint needs the current process." }),
|
|
2328
|
+
/* @__PURE__ */ jsx9(
|
|
1730
2329
|
"button",
|
|
1731
2330
|
{
|
|
1732
2331
|
type: "button",
|
|
@@ -1739,7 +2338,7 @@ var ImageEditor = ({
|
|
|
1739
2338
|
}
|
|
1740
2339
|
)
|
|
1741
2340
|
] }, spec.id)),
|
|
1742
|
-
/* @__PURE__ */
|
|
2341
|
+
/* @__PURE__ */ jsx9("div", { className: "border-t border-slate-700/60 pt-3", children: /* @__PURE__ */ jsx9(
|
|
1743
2342
|
AdjustmentLayersPanel,
|
|
1744
2343
|
{
|
|
1745
2344
|
controller,
|
|
@@ -1748,9 +2347,19 @@ var ImageEditor = ({
|
|
|
1748
2347
|
}
|
|
1749
2348
|
) })
|
|
1750
2349
|
] }),
|
|
1751
|
-
activeTab === "
|
|
1752
|
-
/* @__PURE__ */
|
|
1753
|
-
/* @__PURE__ */
|
|
2350
|
+
activeTab === "layers" && /* @__PURE__ */ jsxs9("div", { className: "flex flex-col gap-3 text-xs", children: [
|
|
2351
|
+
/* @__PURE__ */ jsx9("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Layers" }),
|
|
2352
|
+
/* @__PURE__ */ jsx9(LayersPanel, { controller })
|
|
2353
|
+
] }),
|
|
2354
|
+
activeTab === "paint" && /* @__PURE__ */ jsxs9("div", { className: "flex flex-col gap-3 text-xs", children: [
|
|
2355
|
+
/* @__PURE__ */ jsx9("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Paint" }),
|
|
2356
|
+
/* @__PURE__ */ jsx9(BrushOptionsBar, { controller }),
|
|
2357
|
+
paintError && /* @__PURE__ */ jsx9("p", { role: "alert", className: "text-rose-400", "data-testid": "paint-error", children: paintError }),
|
|
2358
|
+
/* @__PURE__ */ jsx9("div", { className: "border-t border-slate-700/60 pt-3", children: /* @__PURE__ */ jsx9(LayersPanel, { controller }) })
|
|
2359
|
+
] }),
|
|
2360
|
+
activeTab === "annotate" && /* @__PURE__ */ jsxs9("div", { className: "flex flex-col gap-3 text-xs", children: [
|
|
2361
|
+
/* @__PURE__ */ jsx9("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Draw & Annotate" }),
|
|
2362
|
+
/* @__PURE__ */ jsx9("div", { className: "grid grid-cols-3 gap-1.5", children: ["pen", "rect", "circle", "arrow", "line"].map((t) => /* @__PURE__ */ jsx9(
|
|
1754
2363
|
"button",
|
|
1755
2364
|
{
|
|
1756
2365
|
type: "button",
|
|
@@ -1760,9 +2369,9 @@ var ImageEditor = ({
|
|
|
1760
2369
|
},
|
|
1761
2370
|
t
|
|
1762
2371
|
)) }),
|
|
1763
|
-
/* @__PURE__ */
|
|
1764
|
-
/* @__PURE__ */
|
|
1765
|
-
/* @__PURE__ */
|
|
2372
|
+
/* @__PURE__ */ jsxs9("div", { className: "flex items-center gap-2 mt-2", children: [
|
|
2373
|
+
/* @__PURE__ */ jsx9("label", { className: "text-slate-400", children: "Color:" }),
|
|
2374
|
+
/* @__PURE__ */ jsx9(
|
|
1766
2375
|
"input",
|
|
1767
2376
|
{
|
|
1768
2377
|
type: "color",
|
|
@@ -1771,15 +2380,15 @@ var ImageEditor = ({
|
|
|
1771
2380
|
className: "w-8 h-8 rounded border border-slate-700 cursor-pointer bg-transparent"
|
|
1772
2381
|
}
|
|
1773
2382
|
),
|
|
1774
|
-
/* @__PURE__ */
|
|
1775
|
-
/* @__PURE__ */
|
|
1776
|
-
/* @__PURE__ */
|
|
1777
|
-
/* @__PURE__ */
|
|
2383
|
+
/* @__PURE__ */ jsxs9("div", { className: "flex-1 ml-2", children: [
|
|
2384
|
+
/* @__PURE__ */ jsxs9("div", { className: "flex justify-between text-slate-400 mb-0.5", children: [
|
|
2385
|
+
/* @__PURE__ */ jsx9("span", { children: "Width:" }),
|
|
2386
|
+
/* @__PURE__ */ jsxs9("span", { className: "font-mono", children: [
|
|
1778
2387
|
strokeWidth,
|
|
1779
2388
|
"px"
|
|
1780
2389
|
] })
|
|
1781
2390
|
] }),
|
|
1782
|
-
/* @__PURE__ */
|
|
2391
|
+
/* @__PURE__ */ jsx9(
|
|
1783
2392
|
"input",
|
|
1784
2393
|
{
|
|
1785
2394
|
type: "range",
|
|
@@ -1792,10 +2401,10 @@ var ImageEditor = ({
|
|
|
1792
2401
|
)
|
|
1793
2402
|
] })
|
|
1794
2403
|
] }),
|
|
1795
|
-
/* @__PURE__ */
|
|
1796
|
-
/* @__PURE__ */
|
|
1797
|
-
/* @__PURE__ */
|
|
1798
|
-
/* @__PURE__ */
|
|
2404
|
+
/* @__PURE__ */ jsx9("div", { className: "h-px bg-slate-800 my-1" }),
|
|
2405
|
+
/* @__PURE__ */ jsx9("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Add Text" }),
|
|
2406
|
+
/* @__PURE__ */ jsxs9("div", { className: "flex gap-2", children: [
|
|
2407
|
+
/* @__PURE__ */ jsx9(
|
|
1799
2408
|
"input",
|
|
1800
2409
|
{
|
|
1801
2410
|
type: "text",
|
|
@@ -1806,7 +2415,7 @@ var ImageEditor = ({
|
|
|
1806
2415
|
className: "flex-1 bg-slate-950 border border-slate-700 rounded-lg px-2.5 py-1.5 text-xs text-slate-100 placeholder-slate-500 focus:outline-none focus:border-indigo-500"
|
|
1807
2416
|
}
|
|
1808
2417
|
),
|
|
1809
|
-
/* @__PURE__ */
|
|
2418
|
+
/* @__PURE__ */ jsx9(
|
|
1810
2419
|
"button",
|
|
1811
2420
|
{
|
|
1812
2421
|
type: "button",
|
|
@@ -1816,14 +2425,14 @@ var ImageEditor = ({
|
|
|
1816
2425
|
}
|
|
1817
2426
|
)
|
|
1818
2427
|
] }),
|
|
1819
|
-
/* @__PURE__ */
|
|
1820
|
-
/* @__PURE__ */
|
|
1821
|
-
/* @__PURE__ */
|
|
2428
|
+
/* @__PURE__ */ jsxs9("div", { className: "flex items-center justify-between text-slate-400", children: [
|
|
2429
|
+
/* @__PURE__ */ jsx9("span", { children: "Font Size:" }),
|
|
2430
|
+
/* @__PURE__ */ jsxs9("span", { className: "font-mono", children: [
|
|
1822
2431
|
fontSize,
|
|
1823
2432
|
"px"
|
|
1824
2433
|
] })
|
|
1825
2434
|
] }),
|
|
1826
|
-
/* @__PURE__ */
|
|
2435
|
+
/* @__PURE__ */ jsx9(
|
|
1827
2436
|
"input",
|
|
1828
2437
|
{
|
|
1829
2438
|
type: "range",
|
|
@@ -1834,14 +2443,14 @@ var ImageEditor = ({
|
|
|
1834
2443
|
className: "w-full accent-indigo-500"
|
|
1835
2444
|
}
|
|
1836
2445
|
),
|
|
1837
|
-
/* @__PURE__ */
|
|
1838
|
-
/* @__PURE__ */
|
|
1839
|
-
/* @__PURE__ */
|
|
2446
|
+
/* @__PURE__ */ jsx9("div", { className: "h-px bg-slate-800 my-1" }),
|
|
2447
|
+
/* @__PURE__ */ jsxs9("div", { className: "flex items-center justify-between", children: [
|
|
2448
|
+
/* @__PURE__ */ jsxs9("span", { className: "text-slate-400", children: [
|
|
1840
2449
|
"Layers (",
|
|
1841
2450
|
state.annotations.length,
|
|
1842
2451
|
")"
|
|
1843
2452
|
] }),
|
|
1844
|
-
state.selectedAnnotationId && /* @__PURE__ */
|
|
2453
|
+
state.selectedAnnotationId && /* @__PURE__ */ jsx9(
|
|
1845
2454
|
"button",
|
|
1846
2455
|
{
|
|
1847
2456
|
type: "button",
|
|
@@ -1850,7 +2459,7 @@ var ImageEditor = ({
|
|
|
1850
2459
|
children: "Delete Selected"
|
|
1851
2460
|
}
|
|
1852
2461
|
),
|
|
1853
|
-
state.annotations.length > 0 && /* @__PURE__ */
|
|
2462
|
+
state.annotations.length > 0 && /* @__PURE__ */ jsx9(
|
|
1854
2463
|
"button",
|
|
1855
2464
|
{
|
|
1856
2465
|
type: "button",
|
|
@@ -1861,10 +2470,10 @@ var ImageEditor = ({
|
|
|
1861
2470
|
)
|
|
1862
2471
|
] })
|
|
1863
2472
|
] }),
|
|
1864
|
-
activeTab === "depth" && /* @__PURE__ */
|
|
1865
|
-
/* @__PURE__ */
|
|
1866
|
-
/* @__PURE__ */
|
|
1867
|
-
/* @__PURE__ */
|
|
2473
|
+
activeTab === "depth" && /* @__PURE__ */ jsxs9("div", { className: "flex flex-col gap-3 text-xs", children: [
|
|
2474
|
+
/* @__PURE__ */ jsxs9("div", { className: "flex items-center justify-between", children: [
|
|
2475
|
+
/* @__PURE__ */ jsx9("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Depth Masking" }),
|
|
2476
|
+
/* @__PURE__ */ jsx9(
|
|
1868
2477
|
"button",
|
|
1869
2478
|
{
|
|
1870
2479
|
type: "button",
|
|
@@ -1874,8 +2483,8 @@ var ImageEditor = ({
|
|
|
1874
2483
|
}
|
|
1875
2484
|
)
|
|
1876
2485
|
] }),
|
|
1877
|
-
/* @__PURE__ */
|
|
1878
|
-
/* @__PURE__ */
|
|
2486
|
+
/* @__PURE__ */ jsxs9("label", { className: "flex items-center gap-2 cursor-pointer text-slate-300", children: [
|
|
2487
|
+
/* @__PURE__ */ jsx9(
|
|
1879
2488
|
"input",
|
|
1880
2489
|
{
|
|
1881
2490
|
type: "checkbox",
|
|
@@ -1884,12 +2493,12 @@ var ImageEditor = ({
|
|
|
1884
2493
|
className: "rounded accent-indigo-500"
|
|
1885
2494
|
}
|
|
1886
2495
|
),
|
|
1887
|
-
/* @__PURE__ */
|
|
2496
|
+
/* @__PURE__ */ jsx9("span", { children: "Enable Depth / Alpha Mask" })
|
|
1888
2497
|
] }),
|
|
1889
|
-
state.depthMask.enabled && /* @__PURE__ */
|
|
1890
|
-
/* @__PURE__ */
|
|
1891
|
-
/* @__PURE__ */
|
|
1892
|
-
/* @__PURE__ */
|
|
2498
|
+
state.depthMask.enabled && /* @__PURE__ */ jsxs9(Fragment3, { children: [
|
|
2499
|
+
/* @__PURE__ */ jsxs9("div", { children: [
|
|
2500
|
+
/* @__PURE__ */ jsx9("label", { className: "text-slate-400 block mb-1", children: "Mask Image URL:" }),
|
|
2501
|
+
/* @__PURE__ */ jsx9(
|
|
1893
2502
|
"input",
|
|
1894
2503
|
{
|
|
1895
2504
|
type: "text",
|
|
@@ -1900,8 +2509,8 @@ var ImageEditor = ({
|
|
|
1900
2509
|
}
|
|
1901
2510
|
)
|
|
1902
2511
|
] }),
|
|
1903
|
-
/* @__PURE__ */
|
|
1904
|
-
/* @__PURE__ */
|
|
2512
|
+
/* @__PURE__ */ jsxs9("div", { className: "flex gap-2", children: [
|
|
2513
|
+
/* @__PURE__ */ jsx9(
|
|
1905
2514
|
"button",
|
|
1906
2515
|
{
|
|
1907
2516
|
type: "button",
|
|
@@ -1910,7 +2519,7 @@ var ImageEditor = ({
|
|
|
1910
2519
|
children: "Threshold Mode"
|
|
1911
2520
|
}
|
|
1912
2521
|
),
|
|
1913
|
-
/* @__PURE__ */
|
|
2522
|
+
/* @__PURE__ */ jsx9(
|
|
1914
2523
|
"button",
|
|
1915
2524
|
{
|
|
1916
2525
|
type: "button",
|
|
@@ -1920,12 +2529,12 @@ var ImageEditor = ({
|
|
|
1920
2529
|
}
|
|
1921
2530
|
)
|
|
1922
2531
|
] }),
|
|
1923
|
-
/* @__PURE__ */
|
|
1924
|
-
/* @__PURE__ */
|
|
1925
|
-
/* @__PURE__ */
|
|
1926
|
-
/* @__PURE__ */
|
|
2532
|
+
/* @__PURE__ */ jsxs9("div", { children: [
|
|
2533
|
+
/* @__PURE__ */ jsxs9("div", { className: "flex justify-between text-slate-400 mb-1", children: [
|
|
2534
|
+
/* @__PURE__ */ jsx9("span", { children: "Depth Start (Lower):" }),
|
|
2535
|
+
/* @__PURE__ */ jsx9("span", { className: "font-mono", children: state.depthMask.depthRange[0] })
|
|
1927
2536
|
] }),
|
|
1928
|
-
/* @__PURE__ */
|
|
2537
|
+
/* @__PURE__ */ jsx9(
|
|
1929
2538
|
"input",
|
|
1930
2539
|
{
|
|
1931
2540
|
type: "range",
|
|
@@ -1939,12 +2548,12 @@ var ImageEditor = ({
|
|
|
1939
2548
|
}
|
|
1940
2549
|
)
|
|
1941
2550
|
] }),
|
|
1942
|
-
/* @__PURE__ */
|
|
1943
|
-
/* @__PURE__ */
|
|
1944
|
-
/* @__PURE__ */
|
|
1945
|
-
/* @__PURE__ */
|
|
2551
|
+
/* @__PURE__ */ jsxs9("div", { children: [
|
|
2552
|
+
/* @__PURE__ */ jsxs9("div", { className: "flex justify-between text-slate-400 mb-1", children: [
|
|
2553
|
+
/* @__PURE__ */ jsx9("span", { children: "Depth Stop (Upper):" }),
|
|
2554
|
+
/* @__PURE__ */ jsx9("span", { className: "font-mono", children: state.depthMask.depthRange[1] })
|
|
1946
2555
|
] }),
|
|
1947
|
-
/* @__PURE__ */
|
|
2556
|
+
/* @__PURE__ */ jsx9(
|
|
1948
2557
|
"input",
|
|
1949
2558
|
{
|
|
1950
2559
|
type: "range",
|
|
@@ -1958,12 +2567,12 @@ var ImageEditor = ({
|
|
|
1958
2567
|
}
|
|
1959
2568
|
)
|
|
1960
2569
|
] }),
|
|
1961
|
-
/* @__PURE__ */
|
|
1962
|
-
/* @__PURE__ */
|
|
1963
|
-
/* @__PURE__ */
|
|
1964
|
-
/* @__PURE__ */
|
|
2570
|
+
/* @__PURE__ */ jsxs9("div", { children: [
|
|
2571
|
+
/* @__PURE__ */ jsxs9("div", { className: "flex justify-between text-slate-400 mb-1", children: [
|
|
2572
|
+
/* @__PURE__ */ jsx9("span", { children: "Softness:" }),
|
|
2573
|
+
/* @__PURE__ */ jsx9("span", { className: "font-mono", children: state.depthMask.softness })
|
|
1965
2574
|
] }),
|
|
1966
|
-
/* @__PURE__ */
|
|
2575
|
+
/* @__PURE__ */ jsx9(
|
|
1967
2576
|
"input",
|
|
1968
2577
|
{
|
|
1969
2578
|
type: "range",
|
|
@@ -1975,8 +2584,8 @@ var ImageEditor = ({
|
|
|
1975
2584
|
}
|
|
1976
2585
|
)
|
|
1977
2586
|
] }),
|
|
1978
|
-
/* @__PURE__ */
|
|
1979
|
-
/* @__PURE__ */
|
|
2587
|
+
/* @__PURE__ */ jsxs9("label", { className: "flex items-center gap-2 cursor-pointer text-slate-300 mt-1", children: [
|
|
2588
|
+
/* @__PURE__ */ jsx9(
|
|
1980
2589
|
"input",
|
|
1981
2590
|
{
|
|
1982
2591
|
type: "checkbox",
|
|
@@ -1985,12 +2594,12 @@ var ImageEditor = ({
|
|
|
1985
2594
|
className: "rounded accent-indigo-500"
|
|
1986
2595
|
}
|
|
1987
2596
|
),
|
|
1988
|
-
/* @__PURE__ */
|
|
2597
|
+
/* @__PURE__ */ jsx9("span", { children: "Invert Mask" })
|
|
1989
2598
|
] })
|
|
1990
2599
|
] })
|
|
1991
2600
|
] }),
|
|
1992
|
-
activeTab === "select" && /* @__PURE__ */
|
|
1993
|
-
/* @__PURE__ */
|
|
2601
|
+
activeTab === "select" && /* @__PURE__ */ jsxs9("div", { className: "flex flex-col gap-4 text-xs", children: [
|
|
2602
|
+
/* @__PURE__ */ jsx9(
|
|
1994
2603
|
SelectionOptionsBar,
|
|
1995
2604
|
{
|
|
1996
2605
|
controller,
|
|
@@ -2000,51 +2609,68 @@ var ImageEditor = ({
|
|
|
2000
2609
|
quickMask: Boolean(state.quickMask)
|
|
2001
2610
|
}
|
|
2002
2611
|
),
|
|
2003
|
-
selectionError && /* @__PURE__ */
|
|
2004
|
-
/* @__PURE__ */
|
|
2612
|
+
selectionError && /* @__PURE__ */ jsx9("p", { role: "alert", className: "text-rose-400", children: selectionError }),
|
|
2613
|
+
/* @__PURE__ */ jsx9(RefineEdgePanel, { controller, selection: state.selection ?? null, layers: state.adjustments.layers })
|
|
2005
2614
|
] }),
|
|
2006
|
-
activeTab === "ai" && /* @__PURE__ */
|
|
2007
|
-
/* @__PURE__ */
|
|
2008
|
-
/* @__PURE__ */
|
|
2009
|
-
|
|
2010
|
-
|
|
2615
|
+
activeTab === "ai" && /* @__PURE__ */ jsxs9("div", { className: "flex flex-col gap-4 text-xs", children: [
|
|
2616
|
+
/* @__PURE__ */ jsxs9("label", { className: "flex items-center justify-between gap-2 text-slate-300", children: [
|
|
2617
|
+
/* @__PURE__ */ jsx9("span", { children: "Results go to" }),
|
|
2618
|
+
/* @__PURE__ */ jsxs9(
|
|
2619
|
+
"select",
|
|
2620
|
+
{
|
|
2621
|
+
"aria-label": "AI result target",
|
|
2622
|
+
value: aiTarget,
|
|
2623
|
+
onChange: (e) => setAiTarget(e.target.value),
|
|
2624
|
+
className: "bg-slate-800 border border-slate-700 rounded px-1.5 py-1",
|
|
2625
|
+
children: [
|
|
2626
|
+
/* @__PURE__ */ jsx9("option", { value: "auto", children: controller.isMultiLayer ? "Auto (new layer)" : "Auto (replace image)" }),
|
|
2627
|
+
/* @__PURE__ */ jsx9("option", { value: "new-layer", children: "New layer / mask" }),
|
|
2628
|
+
/* @__PURE__ */ jsx9("option", { value: "replace", children: "Replace image" })
|
|
2629
|
+
]
|
|
2630
|
+
}
|
|
2631
|
+
)
|
|
2632
|
+
] }),
|
|
2633
|
+
/* @__PURE__ */ jsxs9("div", { className: "p-3 rounded-lg bg-slate-950/60 border border-slate-800 flex flex-col gap-2.5", children: [
|
|
2634
|
+
/* @__PURE__ */ jsxs9("div", { className: "flex items-center justify-between", children: [
|
|
2635
|
+
/* @__PURE__ */ jsxs9("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px] flex items-center gap-1.5", children: [
|
|
2636
|
+
/* @__PURE__ */ jsx9("svg", { className: "w-3.5 h-3.5 text-indigo-400", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: /* @__PURE__ */ jsx9("path", { d: "M6 3v12M18 9a3 3 0 1 0 0-6 3 3 0 0 0 0 6zM6 21a3 3 0 1 0 0-6 3 3 0 0 0 0 6z" }) }),
|
|
2011
2637
|
"Background Removal"
|
|
2012
2638
|
] }),
|
|
2013
|
-
isRemovingBg && /* @__PURE__ */
|
|
2639
|
+
isRemovingBg && /* @__PURE__ */ jsx9("span", { className: "inline-flex items-center px-1.5 py-0.5 rounded text-[10px] font-medium bg-indigo-900/60 text-indigo-300 animate-pulse", children: "Removing..." })
|
|
2014
2640
|
] }),
|
|
2015
|
-
/* @__PURE__ */
|
|
2016
|
-
/* @__PURE__ */
|
|
2641
|
+
/* @__PURE__ */ jsx9("p", { className: "text-slate-400 text-[11px] leading-relaxed", children: "Isolate foreground subject with AI alpha transparency segmentation." }),
|
|
2642
|
+
/* @__PURE__ */ jsx9(
|
|
2017
2643
|
"button",
|
|
2018
2644
|
{
|
|
2019
2645
|
type: "button",
|
|
2020
2646
|
onClick: handleRemoveBackground,
|
|
2021
2647
|
disabled: isRemovingBg || isInpainting,
|
|
2022
2648
|
className: "mt-1 px-3 py-2 rounded-lg bg-indigo-600 hover:bg-indigo-500 disabled:opacity-50 text-white font-medium shadow transition-colors flex items-center justify-center gap-2",
|
|
2023
|
-
children: isRemovingBg ? /* @__PURE__ */
|
|
2024
|
-
/* @__PURE__ */
|
|
2649
|
+
children: isRemovingBg ? /* @__PURE__ */ jsxs9(Fragment3, { children: [
|
|
2650
|
+
/* @__PURE__ */ jsx9("div", { className: "w-3.5 h-3.5 border-2 border-white/30 border-t-white rounded-full animate-spin" }),
|
|
2025
2651
|
"Removing Background..."
|
|
2026
|
-
] }) : /* @__PURE__ */
|
|
2027
|
-
/* @__PURE__ */
|
|
2652
|
+
] }) : /* @__PURE__ */ jsxs9(Fragment3, { children: [
|
|
2653
|
+
/* @__PURE__ */ jsx9("svg", { className: "w-3.5 h-3.5", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: /* @__PURE__ */ jsx9("path", { d: "M12 2v4M12 18v4M4.93 4.93l2.83 2.83M16.24 16.24l2.83 2.83M2 12h4M18 12h4M4.93 19.07l2.83-2.83M16.24 7.76l2.83-2.83" }) }),
|
|
2028
2654
|
"Remove Background"
|
|
2029
2655
|
] })
|
|
2030
2656
|
}
|
|
2031
2657
|
)
|
|
2032
2658
|
] }),
|
|
2033
|
-
/* @__PURE__ */
|
|
2034
|
-
/* @__PURE__ */
|
|
2035
|
-
/* @__PURE__ */
|
|
2036
|
-
/* @__PURE__ */
|
|
2037
|
-
/* @__PURE__ */
|
|
2038
|
-
/* @__PURE__ */
|
|
2659
|
+
/* @__PURE__ */ jsxs9("div", { className: "p-3 rounded-lg bg-slate-950/60 border border-slate-800 flex flex-col gap-2.5", children: [
|
|
2660
|
+
/* @__PURE__ */ jsxs9("div", { className: "flex items-center justify-between", children: [
|
|
2661
|
+
/* @__PURE__ */ jsxs9("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px] flex items-center gap-1.5", children: [
|
|
2662
|
+
/* @__PURE__ */ jsxs9("svg", { className: "w-3.5 h-3.5 text-purple-400", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
|
|
2663
|
+
/* @__PURE__ */ jsx9("path", { d: "m21.64 3.64-1.28-1.28a1.2 1.2 0 0 0-1.7 0l-9.72 9.72-1.94 4.86 4.86-1.94 9.72-9.72a1.2 1.2 0 0 0 .06-1.64z" }),
|
|
2664
|
+
/* @__PURE__ */ jsx9("path", { d: "m14 7 3 3" })
|
|
2039
2665
|
] }),
|
|
2040
2666
|
"Generative Inpainting"
|
|
2041
2667
|
] }),
|
|
2042
|
-
isInpainting && /* @__PURE__ */
|
|
2668
|
+
isInpainting && /* @__PURE__ */ jsx9("span", { className: "inline-flex items-center px-1.5 py-0.5 rounded text-[10px] font-medium bg-purple-900/60 text-purple-300 animate-pulse", children: "Inpainting..." })
|
|
2043
2669
|
] }),
|
|
2044
|
-
/* @__PURE__ */
|
|
2045
|
-
/* @__PURE__ */
|
|
2046
|
-
/* @__PURE__ */
|
|
2047
|
-
/* @__PURE__ */
|
|
2670
|
+
/* @__PURE__ */ jsx9("p", { className: "text-slate-400 text-[11px] leading-relaxed", children: "Replace, modify, or fill selected regions using prompt-guided generative inpainting." }),
|
|
2671
|
+
/* @__PURE__ */ jsxs9("div", { className: "flex flex-col gap-1", children: [
|
|
2672
|
+
/* @__PURE__ */ jsx9("label", { htmlFor: "ai-inpaint-prompt-react", className: "text-slate-300 text-[11px] font-medium", children: "Prompt" }),
|
|
2673
|
+
/* @__PURE__ */ jsx9(
|
|
2048
2674
|
"textarea",
|
|
2049
2675
|
{
|
|
2050
2676
|
id: "ai-inpaint-prompt-react",
|
|
@@ -2056,10 +2682,10 @@ var ImageEditor = ({
|
|
|
2056
2682
|
}
|
|
2057
2683
|
)
|
|
2058
2684
|
] }),
|
|
2059
|
-
/* @__PURE__ */
|
|
2060
|
-
/* @__PURE__ */
|
|
2061
|
-
/* @__PURE__ */
|
|
2062
|
-
/* @__PURE__ */
|
|
2685
|
+
/* @__PURE__ */ jsxs9("div", { className: "flex flex-col gap-1", children: [
|
|
2686
|
+
/* @__PURE__ */ jsx9("span", { className: "text-slate-300 text-[11px] font-medium", children: "Target Mask Region" }),
|
|
2687
|
+
/* @__PURE__ */ jsxs9("div", { className: "grid grid-cols-2 gap-1.5", children: [
|
|
2688
|
+
/* @__PURE__ */ jsxs9(
|
|
2063
2689
|
"button",
|
|
2064
2690
|
{
|
|
2065
2691
|
type: "button",
|
|
@@ -2072,7 +2698,7 @@ var ImageEditor = ({
|
|
|
2072
2698
|
]
|
|
2073
2699
|
}
|
|
2074
2700
|
),
|
|
2075
|
-
/* @__PURE__ */
|
|
2701
|
+
/* @__PURE__ */ jsxs9(
|
|
2076
2702
|
"button",
|
|
2077
2703
|
{
|
|
2078
2704
|
type: "button",
|
|
@@ -2087,25 +2713,25 @@ var ImageEditor = ({
|
|
|
2087
2713
|
)
|
|
2088
2714
|
] })
|
|
2089
2715
|
] }),
|
|
2090
|
-
/* @__PURE__ */
|
|
2716
|
+
/* @__PURE__ */ jsx9(
|
|
2091
2717
|
"button",
|
|
2092
2718
|
{
|
|
2093
2719
|
type: "button",
|
|
2094
2720
|
onClick: handleInpaint,
|
|
2095
2721
|
disabled: isInpainting || isRemovingBg || !inpaintPrompt.trim(),
|
|
2096
2722
|
className: "mt-1 px-3 py-2 rounded-lg bg-purple-600 hover:bg-purple-500 disabled:opacity-50 text-white font-medium shadow transition-colors flex items-center justify-center gap-2",
|
|
2097
|
-
children: isInpainting ? /* @__PURE__ */
|
|
2098
|
-
/* @__PURE__ */
|
|
2723
|
+
children: isInpainting ? /* @__PURE__ */ jsxs9(Fragment3, { children: [
|
|
2724
|
+
/* @__PURE__ */ jsx9("div", { className: "w-3.5 h-3.5 border-2 border-white/30 border-t-white rounded-full animate-spin" }),
|
|
2099
2725
|
"Generating Inpaint..."
|
|
2100
|
-
] }) : /* @__PURE__ */
|
|
2101
|
-
/* @__PURE__ */
|
|
2726
|
+
] }) : /* @__PURE__ */ jsxs9(Fragment3, { children: [
|
|
2727
|
+
/* @__PURE__ */ jsx9("svg", { className: "w-3.5 h-3.5", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: /* @__PURE__ */ jsx9("path", { d: "M12 2v4M12 18v4M4.93 4.93l2.83 2.83M16.24 16.24l2.83 2.83M2 12h4M18 12h4M4.93 19.07l2.83-2.83M16.24 7.76l2.83-2.83" }) }),
|
|
2102
2728
|
"Apply Inpainting"
|
|
2103
2729
|
] })
|
|
2104
2730
|
}
|
|
2105
2731
|
)
|
|
2106
2732
|
] }),
|
|
2107
|
-
aiError && /* @__PURE__ */
|
|
2108
|
-
aiSuccess && /* @__PURE__ */
|
|
2733
|
+
aiError && /* @__PURE__ */ jsx9("div", { className: "p-2.5 rounded-lg bg-rose-950/60 border border-rose-800 text-rose-300 text-[11px]", children: aiError }),
|
|
2734
|
+
aiSuccess && /* @__PURE__ */ jsx9("div", { className: "p-2.5 rounded-lg bg-emerald-950/60 border border-emerald-800 text-emerald-300 text-[11px]", children: aiSuccess })
|
|
2109
2735
|
] })
|
|
2110
2736
|
] })
|
|
2111
2737
|
] })
|
|
@@ -2121,8 +2747,10 @@ export {
|
|
|
2121
2747
|
useImageHistogram,
|
|
2122
2748
|
CurvesEditor,
|
|
2123
2749
|
AdjustmentLayersPanel,
|
|
2750
|
+
LayersPanel,
|
|
2751
|
+
BrushOptionsBar,
|
|
2124
2752
|
SelectionOptionsBar,
|
|
2125
2753
|
RefineEdgePanel,
|
|
2126
2754
|
ImageEditor
|
|
2127
2755
|
};
|
|
2128
|
-
//# sourceMappingURL=chunk-
|
|
2756
|
+
//# sourceMappingURL=chunk-OSOGA2OJ.js.map
|