@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
|
@@ -31,10 +31,12 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
31
31
|
var image_editor_exports = {};
|
|
32
32
|
__export(image_editor_exports, {
|
|
33
33
|
AdjustmentLayersPanel: () => AdjustmentLayersPanel,
|
|
34
|
+
BrushOptionsBar: () => BrushOptionsBar,
|
|
34
35
|
ControlSlider: () => ControlSlider,
|
|
35
36
|
CurvesEditor: () => CurvesEditor,
|
|
36
37
|
Histogram: () => Histogram,
|
|
37
38
|
ImageEditor: () => ImageEditor,
|
|
39
|
+
LayersPanel: () => LayersPanel,
|
|
38
40
|
RefineEdgePanel: () => RefineEdgePanel,
|
|
39
41
|
SelectionOptionsBar: () => SelectionOptionsBar,
|
|
40
42
|
useImageEditor: () => useImageEditor,
|
|
@@ -138,11 +140,11 @@ function useImageEditor(options = {}) {
|
|
|
138
140
|
[controller]
|
|
139
141
|
);
|
|
140
142
|
const applyInpaintedImage = (0, import_react.useCallback)(
|
|
141
|
-
(newDataUrl) => controller.applyInpaintedImage(newDataUrl),
|
|
143
|
+
(newDataUrl, options2) => controller.applyInpaintedImage(newDataUrl, options2),
|
|
142
144
|
[controller]
|
|
143
145
|
);
|
|
144
146
|
const applyBackgroundRemovedImage = (0, import_react.useCallback)(
|
|
145
|
-
(newDataUrl) => controller.applyBackgroundRemovedImage(newDataUrl),
|
|
147
|
+
(newDataUrl, options2) => controller.applyBackgroundRemovedImage(newDataUrl, options2),
|
|
146
148
|
[controller]
|
|
147
149
|
);
|
|
148
150
|
return (0, import_react.useMemo)(
|
|
@@ -201,8 +203,8 @@ function useImageEditor(options = {}) {
|
|
|
201
203
|
}
|
|
202
204
|
|
|
203
205
|
// src/image-editor/ImageEditor.tsx
|
|
204
|
-
var
|
|
205
|
-
var
|
|
206
|
+
var import_react9 = __toESM(require("react"), 1);
|
|
207
|
+
var import_image_editor10 = require("@pixerate/editor/image-editor");
|
|
206
208
|
|
|
207
209
|
// src/image-editor/ControlSlider.tsx
|
|
208
210
|
var import_image_editor2 = require("@pixerate/editor/image-editor");
|
|
@@ -633,10 +635,492 @@ var AdjustmentLayersPanel = ({ controller, layers: layersProp, histogram }) => {
|
|
|
633
635
|
] });
|
|
634
636
|
};
|
|
635
637
|
|
|
636
|
-
// src/image-editor/
|
|
638
|
+
// src/image-editor/LayersPanel.tsx
|
|
637
639
|
var import_react5 = require("react");
|
|
638
640
|
var import_image_editor6 = require("@pixerate/editor/image-editor");
|
|
639
641
|
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
642
|
+
var iconButton = "px-1.5 py-0.5 rounded text-[10px] bg-slate-800 text-slate-300 hover:text-white disabled:opacity-40";
|
|
643
|
+
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"}`;
|
|
644
|
+
var Thumbnail = ({
|
|
645
|
+
pixels,
|
|
646
|
+
size,
|
|
647
|
+
label,
|
|
648
|
+
onClick,
|
|
649
|
+
selected,
|
|
650
|
+
testId
|
|
651
|
+
}) => {
|
|
652
|
+
const ref = (0, import_react5.useRef)(null);
|
|
653
|
+
(0, import_react5.useEffect)(() => {
|
|
654
|
+
const canvas = ref.current;
|
|
655
|
+
const ctx = canvas?.getContext("2d");
|
|
656
|
+
if (!canvas || !ctx || !pixels || typeof ctx.createImageData !== "function") return;
|
|
657
|
+
canvas.width = pixels.width;
|
|
658
|
+
canvas.height = pixels.height;
|
|
659
|
+
const img = ctx.createImageData(pixels.width, pixels.height);
|
|
660
|
+
img.data.set(pixels.data);
|
|
661
|
+
ctx.putImageData(img, 0, 0);
|
|
662
|
+
}, [pixels]);
|
|
663
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
664
|
+
"canvas",
|
|
665
|
+
{
|
|
666
|
+
ref,
|
|
667
|
+
role: onClick ? "button" : "img",
|
|
668
|
+
"aria-label": label,
|
|
669
|
+
"data-testid": testId,
|
|
670
|
+
onClick,
|
|
671
|
+
style: { width: size, height: size },
|
|
672
|
+
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"}`
|
|
673
|
+
}
|
|
674
|
+
);
|
|
675
|
+
};
|
|
676
|
+
var LayersPanel = ({ controller, thumbnailSize = 32, psd = true, psdFileName = "image", className }) => {
|
|
677
|
+
const [, setTick] = (0, import_react5.useState)(0);
|
|
678
|
+
(0, import_react5.useEffect)(() => controller.subscribe(() => setTick((n) => n + 1)), [controller]);
|
|
679
|
+
const [error, setError] = (0, import_react5.useState)(null);
|
|
680
|
+
const [renaming, setRenaming] = (0, import_react5.useState)(null);
|
|
681
|
+
const [menu, setMenu] = (0, import_react5.useState)(null);
|
|
682
|
+
const [drag, setDrag] = (0, import_react5.useState)(null);
|
|
683
|
+
const [psdNotes, setPsdNotes] = (0, import_react5.useState)([]);
|
|
684
|
+
const fileInput = (0, import_react5.useRef)(null);
|
|
685
|
+
const psdTask = async (fn) => {
|
|
686
|
+
try {
|
|
687
|
+
setError(null);
|
|
688
|
+
setPsdNotes(await fn());
|
|
689
|
+
} catch (err) {
|
|
690
|
+
setError(err instanceof Error ? err.message : String(err));
|
|
691
|
+
}
|
|
692
|
+
};
|
|
693
|
+
const commitNext = (0, import_react5.useRef)(true);
|
|
694
|
+
const state = controller.getState();
|
|
695
|
+
const rows = (0, import_image_editor6.layerPanelRows)(state.layers, state.activeLayerId);
|
|
696
|
+
const active = controller.getActiveLayer();
|
|
697
|
+
const attempt = (fn) => {
|
|
698
|
+
try {
|
|
699
|
+
setError(null);
|
|
700
|
+
fn();
|
|
701
|
+
} catch (err) {
|
|
702
|
+
setError(err instanceof Error ? err.message : String(err));
|
|
703
|
+
}
|
|
704
|
+
};
|
|
705
|
+
const run = async (action) => {
|
|
706
|
+
setMenu(null);
|
|
707
|
+
setError(await (0, import_image_editor6.runLayerMenuAction)(controller, action));
|
|
708
|
+
};
|
|
709
|
+
const prop = (key, value) => attempt(() => {
|
|
710
|
+
controller.setLayerProperties(active.id, { [key]: value }, commitNext.current);
|
|
711
|
+
commitNext.current = false;
|
|
712
|
+
});
|
|
713
|
+
const sliderEvents = {
|
|
714
|
+
onPointerDown: () => commitNext.current = true,
|
|
715
|
+
onPointerUp: () => commitNext.current = true,
|
|
716
|
+
onKeyDown: () => commitNext.current = true
|
|
717
|
+
};
|
|
718
|
+
const onDrop = (e, row) => {
|
|
719
|
+
e.preventDefault();
|
|
720
|
+
const id = e.dataTransfer.getData("text/x-pixerate-layer") || drag;
|
|
721
|
+
setDrag(null);
|
|
722
|
+
if (!id) return;
|
|
723
|
+
const rect = e.currentTarget.getBoundingClientRect();
|
|
724
|
+
const fraction = rect.height ? (e.clientY - rect.top) / rect.height : 0.5;
|
|
725
|
+
const placement = (0, import_image_editor6.dropPlacement)(state.layers, id, row.layer.id, (0, import_image_editor6.dropZone)(fraction, row.layer.type === "group"));
|
|
726
|
+
if (placement) attempt(() => controller.moveLayer(id, placement));
|
|
727
|
+
};
|
|
728
|
+
const thumb = (layer) => layer.type === "pixel" ? controller.getLayerThumbnail(layer.id, { maxSide: 64 }) : null;
|
|
729
|
+
const maskThumb = (layer) => layer.mask && !layer.mask.source ? controller.getLayerThumbnail(layer.id, { maxSide: 64, mask: true }) : null;
|
|
730
|
+
const locked = (layer) => Boolean(layer.locks?.all || layer.locks?.pixels || layer.locks?.position || layer.locks?.transparency);
|
|
731
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: `flex flex-col gap-2 text-xs text-slate-200 ${className ?? ""}`, "data-testid": "layers-panel", onClick: () => menu && setMenu(null), children: [
|
|
732
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex flex-wrap items-center gap-1", role: "toolbar", "aria-label": "Layer actions", children: [
|
|
733
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { type: "button", className: iconButton, onClick: () => run("new-layer"), children: "New Layer" }),
|
|
734
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { type: "button", className: iconButton, onClick: () => run("new-group"), children: "New Group" }),
|
|
735
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { type: "button", className: iconButton, disabled: !active, onClick: () => run("duplicate"), children: "Duplicate" }),
|
|
736
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { type: "button", className: iconButton, disabled: !active, onClick: () => run("add-mask"), children: "Add Mask" }),
|
|
737
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { type: "button", className: iconButton, disabled: !active, onClick: () => run("delete"), children: "Delete" }),
|
|
738
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
739
|
+
"button",
|
|
740
|
+
{
|
|
741
|
+
type: "button",
|
|
742
|
+
className: iconButton,
|
|
743
|
+
"aria-label": "Layer menu",
|
|
744
|
+
"aria-haspopup": "menu",
|
|
745
|
+
onClick: (e) => {
|
|
746
|
+
e.stopPropagation();
|
|
747
|
+
const r = e.currentTarget.getBoundingClientRect();
|
|
748
|
+
setMenu(menu ? null : { x: r.left, y: r.bottom });
|
|
749
|
+
},
|
|
750
|
+
children: "\u22EF"
|
|
751
|
+
}
|
|
752
|
+
),
|
|
753
|
+
psd && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
|
|
754
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { type: "button", className: iconButton, onClick: () => fileInput.current?.click(), children: "Open PSD\u2026" }),
|
|
755
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { type: "button", className: iconButton, onClick: () => psdTask(async () => (await (0, import_image_editor6.downloadPsd)(controller, psdFileName)).warnings), children: "Export PSD" }),
|
|
756
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
757
|
+
"input",
|
|
758
|
+
{
|
|
759
|
+
ref: fileInput,
|
|
760
|
+
type: "file",
|
|
761
|
+
accept: import_image_editor6.PSD_FILE_ACCEPT,
|
|
762
|
+
className: "hidden",
|
|
763
|
+
"data-testid": "psd-file-input",
|
|
764
|
+
"aria-label": "Open a PSD file",
|
|
765
|
+
onChange: (e) => {
|
|
766
|
+
const file = e.currentTarget.files?.[0];
|
|
767
|
+
e.currentTarget.value = "";
|
|
768
|
+
if (file) psdTask(() => (0, import_image_editor6.openPsdFile)(controller, file));
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
)
|
|
772
|
+
] })
|
|
773
|
+
] }),
|
|
774
|
+
psdNotes.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("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: [
|
|
775
|
+
psdNotes.map((n) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("li", { children: n }, n)),
|
|
776
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { type: "button", className: "underline", onClick: () => setPsdNotes([]), children: "Dismiss" }) })
|
|
777
|
+
] }),
|
|
778
|
+
active && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "grid grid-cols-2 gap-x-2 gap-y-1 items-center", "data-testid": "layer-properties", children: [
|
|
779
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("label", { className: "flex flex-col gap-0.5", children: [
|
|
780
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { className: "text-slate-400", children: [
|
|
781
|
+
"Opacity ",
|
|
782
|
+
Math.round(active.opacity * 100),
|
|
783
|
+
"%"
|
|
784
|
+
] }),
|
|
785
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
786
|
+
"input",
|
|
787
|
+
{
|
|
788
|
+
type: "range",
|
|
789
|
+
min: 0,
|
|
790
|
+
max: 100,
|
|
791
|
+
"aria-label": "Layer opacity",
|
|
792
|
+
value: Math.round(active.opacity * 100),
|
|
793
|
+
...sliderEvents,
|
|
794
|
+
onChange: (e) => prop("opacity", Number(e.target.value) / 100),
|
|
795
|
+
className: "accent-indigo-500"
|
|
796
|
+
}
|
|
797
|
+
)
|
|
798
|
+
] }),
|
|
799
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("label", { className: "flex flex-col gap-0.5", children: [
|
|
800
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { className: "text-slate-400", children: [
|
|
801
|
+
"Fill ",
|
|
802
|
+
Math.round(((active.type === "pixel" ? active.fillOpacity : void 0) ?? 1) * 100),
|
|
803
|
+
"%"
|
|
804
|
+
] }),
|
|
805
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
806
|
+
"input",
|
|
807
|
+
{
|
|
808
|
+
type: "range",
|
|
809
|
+
min: 0,
|
|
810
|
+
max: 100,
|
|
811
|
+
"aria-label": "Layer fill",
|
|
812
|
+
disabled: active.type !== "pixel",
|
|
813
|
+
value: Math.round(((active.type === "pixel" ? active.fillOpacity : void 0) ?? 1) * 100),
|
|
814
|
+
...sliderEvents,
|
|
815
|
+
onChange: (e) => prop("fillOpacity", Number(e.target.value) / 100),
|
|
816
|
+
className: "accent-indigo-500 disabled:opacity-40"
|
|
817
|
+
}
|
|
818
|
+
)
|
|
819
|
+
] }),
|
|
820
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
|
|
821
|
+
"select",
|
|
822
|
+
{
|
|
823
|
+
"aria-label": "Layer blend mode",
|
|
824
|
+
className: "bg-slate-800 border border-slate-700 rounded px-1.5 py-1",
|
|
825
|
+
value: active.blend,
|
|
826
|
+
onChange: (e) => attempt(() => controller.setLayerProperties(active.id, { blend: e.target.value })),
|
|
827
|
+
children: [
|
|
828
|
+
active.type === "group" && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("option", { value: "pass-through", children: "Pass Through" }),
|
|
829
|
+
import_image_editor6.BLEND_MODES.map((m) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("option", { value: m.id, children: m.label }, m.id))
|
|
830
|
+
]
|
|
831
|
+
}
|
|
832
|
+
),
|
|
833
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("label", { className: "flex items-center gap-1.5 text-slate-300", children: [
|
|
834
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
835
|
+
"input",
|
|
836
|
+
{
|
|
837
|
+
type: "checkbox",
|
|
838
|
+
"aria-label": "Clip to layer below",
|
|
839
|
+
checked: Boolean(active.clipped),
|
|
840
|
+
onChange: (e) => attempt(() => controller.setLayerProperties(active.id, { clipped: e.target.checked })),
|
|
841
|
+
className: "accent-indigo-500"
|
|
842
|
+
}
|
|
843
|
+
),
|
|
844
|
+
"Clip to below"
|
|
845
|
+
] }),
|
|
846
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("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__ */ (0, import_jsx_runtime5.jsxs)("label", { className: "flex items-center gap-1", children: [
|
|
847
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
848
|
+
"input",
|
|
849
|
+
{
|
|
850
|
+
type: "checkbox",
|
|
851
|
+
"aria-label": `Lock ${k}`,
|
|
852
|
+
checked: Boolean(active.locks?.[k]),
|
|
853
|
+
onChange: (e) => attempt(() => controller.setLayerProperties(active.id, { locks: { ...active.locks, [k]: e.target.checked } })),
|
|
854
|
+
className: "accent-indigo-500"
|
|
855
|
+
}
|
|
856
|
+
),
|
|
857
|
+
k
|
|
858
|
+
] }, k)) })
|
|
859
|
+
] }),
|
|
860
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("ul", { className: "flex flex-col gap-0.5", role: "tree", "aria-label": "Layers", children: rows.map((row) => {
|
|
861
|
+
const { layer } = row;
|
|
862
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
|
|
863
|
+
"li",
|
|
864
|
+
{
|
|
865
|
+
role: "treeitem",
|
|
866
|
+
"aria-selected": row.active,
|
|
867
|
+
"aria-level": row.depth + 1,
|
|
868
|
+
"data-layer-id": layer.id,
|
|
869
|
+
draggable: !layer.locks?.all,
|
|
870
|
+
onDragStart: (e) => {
|
|
871
|
+
e.dataTransfer.setData("text/x-pixerate-layer", layer.id);
|
|
872
|
+
setDrag(layer.id);
|
|
873
|
+
},
|
|
874
|
+
onDragOver: (e) => e.preventDefault(),
|
|
875
|
+
onDrop: (e) => onDrop(e, row),
|
|
876
|
+
tabIndex: 0,
|
|
877
|
+
onClick: () => attempt(() => controller.setActiveLayer(layer.id, "pixels")),
|
|
878
|
+
onKeyDown: (e) => {
|
|
879
|
+
if (e.target === e.currentTarget && (e.key === "Enter" || e.key === " ")) {
|
|
880
|
+
e.preventDefault();
|
|
881
|
+
attempt(() => controller.setActiveLayer(layer.id, "pixels"));
|
|
882
|
+
}
|
|
883
|
+
},
|
|
884
|
+
onContextMenu: (e) => {
|
|
885
|
+
e.preventDefault();
|
|
886
|
+
controller.setActiveLayer(layer.id, state.activeLayerId === layer.id ? state.activeTarget : "pixels");
|
|
887
|
+
setMenu({ x: e.clientX, y: e.clientY });
|
|
888
|
+
},
|
|
889
|
+
className: rowClass(row.active),
|
|
890
|
+
style: { paddingLeft: 6 + row.depth * 14 },
|
|
891
|
+
children: [
|
|
892
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
893
|
+
"button",
|
|
894
|
+
{
|
|
895
|
+
type: "button",
|
|
896
|
+
"aria-label": layer.visible ? `Hide ${row.label}` : `Show ${row.label}`,
|
|
897
|
+
"aria-pressed": layer.visible,
|
|
898
|
+
className: "w-4 text-center text-slate-300",
|
|
899
|
+
onClick: (e) => {
|
|
900
|
+
e.stopPropagation();
|
|
901
|
+
attempt(() => controller.setLayerProperties(layer.id, { visible: !layer.visible }));
|
|
902
|
+
},
|
|
903
|
+
children: layer.visible ? "\u25CF" : "\u25CB"
|
|
904
|
+
}
|
|
905
|
+
),
|
|
906
|
+
layer.type === "group" && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
907
|
+
"button",
|
|
908
|
+
{
|
|
909
|
+
type: "button",
|
|
910
|
+
"aria-label": layer.collapsed ? `Expand ${row.label}` : `Collapse ${row.label}`,
|
|
911
|
+
className: "w-3 text-slate-400",
|
|
912
|
+
onClick: (e) => {
|
|
913
|
+
e.stopPropagation();
|
|
914
|
+
attempt(() => controller.setLayerProperties(layer.id, { collapsed: !layer.collapsed }));
|
|
915
|
+
},
|
|
916
|
+
children: layer.collapsed ? "\u25B8" : "\u25BE"
|
|
917
|
+
}
|
|
918
|
+
),
|
|
919
|
+
row.clipped && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "text-slate-500", "aria-label": "Clipped", children: "\u21B3" }),
|
|
920
|
+
layer.type === "pixel" ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
921
|
+
Thumbnail,
|
|
922
|
+
{
|
|
923
|
+
pixels: thumb(layer),
|
|
924
|
+
size: thumbnailSize,
|
|
925
|
+
label: `${row.label} pixels`,
|
|
926
|
+
selected: row.active && state.activeTarget === "pixels" && Boolean(layer.mask)
|
|
927
|
+
}
|
|
928
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
929
|
+
"span",
|
|
930
|
+
{
|
|
931
|
+
className: "shrink-0 grid place-items-center rounded bg-slate-800 text-[9px] text-slate-400 ring-1 ring-slate-700",
|
|
932
|
+
style: { width: thumbnailSize, height: thumbnailSize },
|
|
933
|
+
children: (0, import_image_editor6.layerBadge)(layer)
|
|
934
|
+
}
|
|
935
|
+
),
|
|
936
|
+
layer.mask && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
937
|
+
Thumbnail,
|
|
938
|
+
{
|
|
939
|
+
pixels: maskThumb(layer),
|
|
940
|
+
size: thumbnailSize,
|
|
941
|
+
label: `${row.label} mask${layer.mask.enabled === false ? " (disabled)" : ""}`,
|
|
942
|
+
testId: "layer-mask-thumbnail",
|
|
943
|
+
selected: row.active && state.activeTarget === "mask",
|
|
944
|
+
onClick: (e) => {
|
|
945
|
+
e.stopPropagation();
|
|
946
|
+
if (e.shiftKey) attempt(() => controller.setLayerMaskProps(layer.id, { enabled: layer.mask.enabled === false }));
|
|
947
|
+
else attempt(() => controller.setActiveLayer(layer.id, "mask"));
|
|
948
|
+
}
|
|
949
|
+
}
|
|
950
|
+
),
|
|
951
|
+
renaming === layer.id ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
952
|
+
"input",
|
|
953
|
+
{
|
|
954
|
+
autoFocus: true,
|
|
955
|
+
"aria-label": "Layer name",
|
|
956
|
+
defaultValue: layer.name || row.label,
|
|
957
|
+
className: "flex-1 min-w-0 bg-slate-900 border border-slate-600 rounded px-1",
|
|
958
|
+
onClick: (e) => e.stopPropagation(),
|
|
959
|
+
onBlur: (e) => {
|
|
960
|
+
setRenaming(null);
|
|
961
|
+
if (e.target.value.trim() && e.target.value !== layer.name) attempt(() => controller.setLayerProperties(layer.id, { name: e.target.value }));
|
|
962
|
+
},
|
|
963
|
+
onKeyDown: (e) => {
|
|
964
|
+
if (e.key === "Enter") e.target.blur();
|
|
965
|
+
if (e.key === "Escape") setRenaming(null);
|
|
966
|
+
}
|
|
967
|
+
}
|
|
968
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
969
|
+
"span",
|
|
970
|
+
{
|
|
971
|
+
className: `flex-1 min-w-0 truncate ${layer.visible ? "" : "text-slate-500"}`,
|
|
972
|
+
onDoubleClick: (e) => {
|
|
973
|
+
e.stopPropagation();
|
|
974
|
+
setRenaming(layer.id);
|
|
975
|
+
},
|
|
976
|
+
children: row.label
|
|
977
|
+
}
|
|
978
|
+
),
|
|
979
|
+
locked(layer) && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { "aria-label": "Locked", className: "text-slate-400", children: "\u{1F512}" }),
|
|
980
|
+
layer.blend !== "normal" && layer.blend !== "pass-through" && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "text-[9px] text-slate-500", children: layer.blend })
|
|
981
|
+
]
|
|
982
|
+
},
|
|
983
|
+
layer.id
|
|
984
|
+
);
|
|
985
|
+
}) }),
|
|
986
|
+
error && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { role: "alert", className: "text-rose-400", children: error }),
|
|
987
|
+
menu && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
988
|
+
"div",
|
|
989
|
+
{
|
|
990
|
+
role: "menu",
|
|
991
|
+
"aria-label": "Layer menu",
|
|
992
|
+
className: "fixed z-50 min-w-44 rounded border border-slate-700 bg-slate-900 py-1 shadow-xl",
|
|
993
|
+
style: { left: menu.x, top: menu.y },
|
|
994
|
+
onClick: (e) => e.stopPropagation(),
|
|
995
|
+
onKeyDown: (e) => e.key === "Escape" && setMenu(null),
|
|
996
|
+
children: (0, import_image_editor6.layerMenu)(controller).map((item) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
997
|
+
"button",
|
|
998
|
+
{
|
|
999
|
+
type: "button",
|
|
1000
|
+
role: "menuitem",
|
|
1001
|
+
disabled: !item.enabled,
|
|
1002
|
+
className: "block w-full px-3 py-1 text-left hover:bg-slate-800 disabled:opacity-40",
|
|
1003
|
+
onClick: () => run(item.action),
|
|
1004
|
+
children: item.label
|
|
1005
|
+
},
|
|
1006
|
+
item.action
|
|
1007
|
+
))
|
|
1008
|
+
}
|
|
1009
|
+
)
|
|
1010
|
+
] });
|
|
1011
|
+
};
|
|
1012
|
+
|
|
1013
|
+
// src/image-editor/BrushOptionsBar.tsx
|
|
1014
|
+
var import_react6 = require("react");
|
|
1015
|
+
var import_image_editor7 = require("@pixerate/editor/image-editor");
|
|
1016
|
+
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
1017
|
+
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"}`;
|
|
1018
|
+
var BrushOptionsBar = ({ controller, className }) => {
|
|
1019
|
+
const [, setTick] = (0, import_react6.useState)(0);
|
|
1020
|
+
(0, import_react6.useEffect)(() => controller.subscribe(() => setTick((n) => n + 1)), [controller]);
|
|
1021
|
+
const [error, setError] = (0, import_react6.useState)(null);
|
|
1022
|
+
const state = controller.getState();
|
|
1023
|
+
const { brush, foreground, background } = state.paint;
|
|
1024
|
+
const attempt = (fn) => {
|
|
1025
|
+
try {
|
|
1026
|
+
setError(null);
|
|
1027
|
+
fn();
|
|
1028
|
+
} catch (err) {
|
|
1029
|
+
setError(err instanceof Error ? err.message : String(err));
|
|
1030
|
+
}
|
|
1031
|
+
};
|
|
1032
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: `flex flex-col gap-2 text-xs text-slate-200 ${className ?? ""}`, "data-testid": "brush-options", children: [
|
|
1033
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "flex gap-1", role: "toolbar", "aria-label": "Paint tools", children: import_image_editor7.PAINT_TOOLS.map((t) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1034
|
+
"button",
|
|
1035
|
+
{
|
|
1036
|
+
type: "button",
|
|
1037
|
+
"aria-pressed": state.activeTool === t.id,
|
|
1038
|
+
title: `${t.label} (${t.shortcut})`,
|
|
1039
|
+
className: chip(state.activeTool === t.id),
|
|
1040
|
+
onClick: () => controller.setTool(t.id),
|
|
1041
|
+
children: t.label
|
|
1042
|
+
},
|
|
1043
|
+
t.id
|
|
1044
|
+
)) }),
|
|
1045
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex items-center gap-2", role: "group", "aria-label": "Colours", children: [
|
|
1046
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("label", { className: "flex items-center gap-1", children: [
|
|
1047
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1048
|
+
"input",
|
|
1049
|
+
{
|
|
1050
|
+
type: "color",
|
|
1051
|
+
"aria-label": "Foreground colour",
|
|
1052
|
+
value: foreground,
|
|
1053
|
+
onChange: (e) => controller.setPaintOptions({ foreground: e.target.value }),
|
|
1054
|
+
className: "h-6 w-8 rounded border border-slate-700 bg-transparent"
|
|
1055
|
+
}
|
|
1056
|
+
),
|
|
1057
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "text-slate-400", children: "FG" })
|
|
1058
|
+
] }),
|
|
1059
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("label", { className: "flex items-center gap-1", children: [
|
|
1060
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1061
|
+
"input",
|
|
1062
|
+
{
|
|
1063
|
+
type: "color",
|
|
1064
|
+
"aria-label": "Background colour",
|
|
1065
|
+
value: background,
|
|
1066
|
+
onChange: (e) => controller.setPaintOptions({ background: e.target.value }),
|
|
1067
|
+
className: "h-6 w-8 rounded border border-slate-700 bg-transparent"
|
|
1068
|
+
}
|
|
1069
|
+
),
|
|
1070
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "text-slate-400", children: "BG" })
|
|
1071
|
+
] }),
|
|
1072
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", title: "Swap colours (X)", className: chip(false), onClick: () => controller.swapPaintColors(), children: "Swap" }),
|
|
1073
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", title: "Default colours (D)", className: chip(false), onClick: () => controller.resetPaintColors(), children: "Default" })
|
|
1074
|
+
] }),
|
|
1075
|
+
import_image_editor7.BRUSH_OPTION_CONTROLS.map((spec) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1076
|
+
ControlSlider,
|
|
1077
|
+
{
|
|
1078
|
+
spec,
|
|
1079
|
+
value: brush[spec.id],
|
|
1080
|
+
onChange: (v) => controller.setPaintOptions({ brush: { [spec.id]: v } })
|
|
1081
|
+
},
|
|
1082
|
+
spec.id
|
|
1083
|
+
)),
|
|
1084
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex flex-wrap gap-3 text-slate-300", role: "group", "aria-label": "Pressure", children: [
|
|
1085
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("label", { className: "flex items-center gap-1.5", children: [
|
|
1086
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1087
|
+
"input",
|
|
1088
|
+
{
|
|
1089
|
+
type: "checkbox",
|
|
1090
|
+
"aria-label": "Pressure controls size",
|
|
1091
|
+
checked: brush.pressureSize,
|
|
1092
|
+
onChange: (e) => controller.setPaintOptions({ brush: { pressureSize: e.target.checked } }),
|
|
1093
|
+
className: "accent-indigo-500"
|
|
1094
|
+
}
|
|
1095
|
+
),
|
|
1096
|
+
"Pressure size"
|
|
1097
|
+
] }),
|
|
1098
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("label", { className: "flex items-center gap-1.5", children: [
|
|
1099
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1100
|
+
"input",
|
|
1101
|
+
{
|
|
1102
|
+
type: "checkbox",
|
|
1103
|
+
"aria-label": "Pressure controls opacity",
|
|
1104
|
+
checked: brush.pressureOpacity,
|
|
1105
|
+
onChange: (e) => controller.setPaintOptions({ brush: { pressureOpacity: e.target.checked } }),
|
|
1106
|
+
className: "accent-indigo-500"
|
|
1107
|
+
}
|
|
1108
|
+
),
|
|
1109
|
+
"Pressure opacity"
|
|
1110
|
+
] })
|
|
1111
|
+
] }),
|
|
1112
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex gap-1", children: [
|
|
1113
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", className: chip(false), onClick: () => attempt(() => controller.fillPixels()), children: "Fill" }),
|
|
1114
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", className: chip(false), onClick: () => attempt(() => controller.clearPixels()), children: "Clear" })
|
|
1115
|
+
] }),
|
|
1116
|
+
error && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { role: "alert", className: "text-rose-400", children: error })
|
|
1117
|
+
] });
|
|
1118
|
+
};
|
|
1119
|
+
|
|
1120
|
+
// src/image-editor/SelectionOptionsBar.tsx
|
|
1121
|
+
var import_react7 = require("react");
|
|
1122
|
+
var import_image_editor8 = require("@pixerate/editor/image-editor");
|
|
1123
|
+
var import_jsx_runtime7 = require("react/jsx-runtime");
|
|
640
1124
|
var MODIFY_OPS = [
|
|
641
1125
|
{ op: "grow", label: "Grow" },
|
|
642
1126
|
{ op: "shrink", label: "Shrink" },
|
|
@@ -653,20 +1137,20 @@ var SelectionOptionsBar = ({
|
|
|
653
1137
|
quickMask,
|
|
654
1138
|
className = ""
|
|
655
1139
|
}) => {
|
|
656
|
-
const [radius, setRadius] = (0,
|
|
1140
|
+
const [radius, setRadius] = (0, import_react7.useState)(2);
|
|
657
1141
|
const set = (patch) => onOptionsChange({ ...options, ...patch });
|
|
658
1142
|
const sel = selection === void 0 ? controller.getSelection() : selection;
|
|
659
1143
|
const qm = quickMask ?? Boolean(controller.getState().quickMask);
|
|
660
|
-
return /* @__PURE__ */ (0,
|
|
661
|
-
/* @__PURE__ */ (0,
|
|
662
|
-
/* @__PURE__ */ (0,
|
|
663
|
-
options.tool === "wand" && /* @__PURE__ */ (0,
|
|
664
|
-
/* @__PURE__ */ (0,
|
|
665
|
-
/* @__PURE__ */ (0,
|
|
666
|
-
/* @__PURE__ */ (0,
|
|
667
|
-
/* @__PURE__ */ (0,
|
|
1144
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { "data-testid": "selection-options-bar", className: `flex flex-col gap-3 text-xs ${className}`, children: [
|
|
1145
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { role: "group", "aria-label": "Selection tool", className: "flex flex-wrap gap-1", children: import_image_editor8.SELECTION_TOOLS.map((t) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("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)) }),
|
|
1146
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { role: "group", "aria-label": "Selection mode", className: "flex flex-wrap gap-1", children: import_image_editor8.SELECTION_MODES.map((m) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("button", { type: "button", "aria-pressed": options.mode === m, className: btn(options.mode === m), onClick: () => set({ mode: m }), children: import_image_editor8.SELECTION_MODE_LABELS[m] }, m)) }),
|
|
1147
|
+
options.tool === "wand" && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex flex-col gap-2", children: [
|
|
1148
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("label", { className: "flex flex-col gap-1", children: [
|
|
1149
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { className: "flex justify-between text-slate-400", children: [
|
|
1150
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { children: "Tolerance" }),
|
|
1151
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "font-mono", children: options.tolerance })
|
|
668
1152
|
] }),
|
|
669
|
-
/* @__PURE__ */ (0,
|
|
1153
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
670
1154
|
"input",
|
|
671
1155
|
{
|
|
672
1156
|
type: "range",
|
|
@@ -680,29 +1164,45 @@ var SelectionOptionsBar = ({
|
|
|
680
1164
|
}
|
|
681
1165
|
)
|
|
682
1166
|
] }),
|
|
683
|
-
/* @__PURE__ */ (0,
|
|
684
|
-
/* @__PURE__ */ (0,
|
|
1167
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("label", { className: "flex items-center gap-2 text-slate-300", children: [
|
|
1168
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("input", { type: "checkbox", checked: options.contiguous, onChange: (e) => set({ contiguous: e.target.checked }) }),
|
|
685
1169
|
"Contiguous"
|
|
686
1170
|
] }),
|
|
687
|
-
/* @__PURE__ */ (0,
|
|
688
|
-
/* @__PURE__ */ (0,
|
|
1171
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("label", { className: "flex items-center gap-2 text-slate-300", children: [
|
|
1172
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("input", { type: "checkbox", checked: options.sampleMerged, onChange: (e) => set({ sampleMerged: e.target.checked }) }),
|
|
689
1173
|
"Sample adjusted image"
|
|
1174
|
+
] }),
|
|
1175
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("label", { className: "flex items-center gap-2 text-slate-300", children: [
|
|
1176
|
+
"Sample",
|
|
1177
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
|
|
1178
|
+
"select",
|
|
1179
|
+
{
|
|
1180
|
+
"aria-label": "Sample from",
|
|
1181
|
+
value: options.sample ?? "composite",
|
|
1182
|
+
onChange: (e) => set({ sample: e.target.value }),
|
|
1183
|
+
className: "bg-slate-800 border border-slate-700 rounded px-1 py-0.5",
|
|
1184
|
+
children: [
|
|
1185
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("option", { value: "composite", children: "All layers" }),
|
|
1186
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("option", { value: "active-layer", children: "Active layer" })
|
|
1187
|
+
]
|
|
1188
|
+
}
|
|
1189
|
+
)
|
|
690
1190
|
] })
|
|
691
1191
|
] }),
|
|
692
|
-
/* @__PURE__ */ (0,
|
|
693
|
-
/* @__PURE__ */ (0,
|
|
1192
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("label", { className: "flex items-center gap-2 text-slate-300", children: [
|
|
1193
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("input", { type: "checkbox", checked: options.antiAlias, onChange: (e) => set({ antiAlias: e.target.checked }) }),
|
|
694
1194
|
"Anti-alias"
|
|
695
1195
|
] }),
|
|
696
|
-
/* @__PURE__ */ (0,
|
|
697
|
-
/* @__PURE__ */ (0,
|
|
698
|
-
/* @__PURE__ */ (0,
|
|
699
|
-
/* @__PURE__ */ (0,
|
|
700
|
-
/* @__PURE__ */ (0,
|
|
1196
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex flex-wrap gap-1", children: [
|
|
1197
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("button", { type: "button", className: btn(false), onClick: () => controller.selectAll(), children: "Select All" }),
|
|
1198
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("button", { type: "button", className: btn(false), disabled: !sel, onClick: () => controller.deselect(), children: "Deselect" }),
|
|
1199
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("button", { type: "button", className: btn(false), onClick: () => controller.invertSelection(), children: "Inverse" }),
|
|
1200
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("button", { type: "button", "aria-pressed": qm, className: btn(qm), onClick: () => controller.setQuickMask(!qm), children: "Quick Mask" })
|
|
701
1201
|
] }),
|
|
702
|
-
/* @__PURE__ */ (0,
|
|
703
|
-
/* @__PURE__ */ (0,
|
|
1202
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex flex-wrap items-center gap-1", children: [
|
|
1203
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("label", { className: "flex items-center gap-1 text-slate-400", children: [
|
|
704
1204
|
"Radius",
|
|
705
|
-
/* @__PURE__ */ (0,
|
|
1205
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
706
1206
|
"input",
|
|
707
1207
|
{
|
|
708
1208
|
type: "number",
|
|
@@ -716,24 +1216,24 @@ var SelectionOptionsBar = ({
|
|
|
716
1216
|
}
|
|
717
1217
|
)
|
|
718
1218
|
] }),
|
|
719
|
-
MODIFY_OPS.map(({ op, label }) => /* @__PURE__ */ (0,
|
|
1219
|
+
MODIFY_OPS.map(({ op, label }) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("button", { type: "button", className: btn(false), disabled: !sel, onClick: () => controller.modifySelection(op, radius), children: label }, op))
|
|
720
1220
|
] }),
|
|
721
|
-
/* @__PURE__ */ (0,
|
|
1221
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("p", { "data-testid": "selection-status", className: "text-slate-400", children: (0, import_image_editor8.describeSelection)(sel) })
|
|
722
1222
|
] });
|
|
723
1223
|
};
|
|
724
1224
|
|
|
725
1225
|
// src/image-editor/RefineEdgePanel.tsx
|
|
726
|
-
var
|
|
727
|
-
var
|
|
728
|
-
var
|
|
1226
|
+
var import_react8 = require("react");
|
|
1227
|
+
var import_image_editor9 = require("@pixerate/editor/image-editor");
|
|
1228
|
+
var import_jsx_runtime8 = require("react/jsx-runtime");
|
|
729
1229
|
var DEFAULT_PARAMS = { radius: 8, smartRadius: false, smooth: 0, feather: 0, contrast: 0, shiftEdge: 0 };
|
|
730
1230
|
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";
|
|
731
1231
|
var RefineEdgePanel = ({ controller, selection, layers, className = "" }) => {
|
|
732
|
-
const [params, setParams] = (0,
|
|
733
|
-
const [busy, setBusy] = (0,
|
|
734
|
-
const [error, setError] = (0,
|
|
735
|
-
const [layerId, setLayerId] = (0,
|
|
736
|
-
const [newKind, setNewKind] = (0,
|
|
1232
|
+
const [params, setParams] = (0, import_react8.useState)(DEFAULT_PARAMS);
|
|
1233
|
+
const [busy, setBusy] = (0, import_react8.useState)(false);
|
|
1234
|
+
const [error, setError] = (0, import_react8.useState)(null);
|
|
1235
|
+
const [layerId, setLayerId] = (0, import_react8.useState)("");
|
|
1236
|
+
const [newKind, setNewKind] = (0, import_react8.useState)("hueSaturation");
|
|
737
1237
|
const sel = selection === void 0 ? controller.getSelection() : selection;
|
|
738
1238
|
const list = layers ?? controller.getAdjustmentLayers();
|
|
739
1239
|
const target = list.find((l) => l.id === layerId) ?? list[list.length - 1];
|
|
@@ -748,17 +1248,17 @@ var RefineEdgePanel = ({ controller, selection, layers, className = "" }) => {
|
|
|
748
1248
|
setBusy(false);
|
|
749
1249
|
}
|
|
750
1250
|
};
|
|
751
|
-
return /* @__PURE__ */ (0,
|
|
752
|
-
/* @__PURE__ */ (0,
|
|
753
|
-
|
|
754
|
-
/* @__PURE__ */ (0,
|
|
755
|
-
/* @__PURE__ */ (0,
|
|
756
|
-
/* @__PURE__ */ (0,
|
|
1251
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { "data-testid": "refine-edge-panel", className: `flex flex-col gap-3 text-xs ${className}`, children: [
|
|
1252
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Refine Edge" }),
|
|
1253
|
+
import_image_editor9.REFINE_EDGE_CONTROLS.map((c) => /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("label", { className: "flex flex-col gap-1", children: [
|
|
1254
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("span", { className: "flex justify-between text-slate-400", children: [
|
|
1255
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { children: c.label }),
|
|
1256
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("span", { className: "font-mono", children: [
|
|
757
1257
|
params[c.id],
|
|
758
1258
|
c.unit
|
|
759
1259
|
] })
|
|
760
1260
|
] }),
|
|
761
|
-
/* @__PURE__ */ (0,
|
|
1261
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
762
1262
|
"input",
|
|
763
1263
|
{
|
|
764
1264
|
type: "range",
|
|
@@ -772,67 +1272,67 @@ var RefineEdgePanel = ({ controller, selection, layers, className = "" }) => {
|
|
|
772
1272
|
}
|
|
773
1273
|
)
|
|
774
1274
|
] }, c.id)),
|
|
775
|
-
/* @__PURE__ */ (0,
|
|
776
|
-
/* @__PURE__ */ (0,
|
|
1275
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("label", { className: "flex items-center gap-2 text-slate-300", children: [
|
|
1276
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("input", { type: "checkbox", checked: params.smartRadius, onChange: (e) => setParams({ ...params, smartRadius: e.target.checked }) }),
|
|
777
1277
|
"Smart Radius"
|
|
778
1278
|
] }),
|
|
779
|
-
/* @__PURE__ */ (0,
|
|
780
|
-
/* @__PURE__ */ (0,
|
|
781
|
-
/* @__PURE__ */ (0,
|
|
1279
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex gap-1", children: [
|
|
1280
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("button", { type: "button", className: button, disabled: !sel || busy, onClick: refine, children: "Refine Edge" }),
|
|
1281
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("button", { type: "button", className: button, onClick: () => setParams(DEFAULT_PARAMS), children: "Reset" })
|
|
782
1282
|
] }),
|
|
783
|
-
error && /* @__PURE__ */ (0,
|
|
784
|
-
/* @__PURE__ */ (0,
|
|
785
|
-
/* @__PURE__ */ (0,
|
|
786
|
-
/* @__PURE__ */ (0,
|
|
1283
|
+
error && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { role: "alert", className: "text-rose-400", children: error }),
|
|
1284
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px] mt-2", children: "Layer Mask" }),
|
|
1285
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex gap-1 items-center", children: [
|
|
1286
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
787
1287
|
"select",
|
|
788
1288
|
{
|
|
789
1289
|
"aria-label": "New masked layer kind",
|
|
790
1290
|
value: newKind,
|
|
791
1291
|
onChange: (e) => setNewKind(e.target.value),
|
|
792
1292
|
className: "flex-1 bg-slate-950 border border-slate-800 rounded px-1 py-1 text-slate-200",
|
|
793
|
-
children:
|
|
1293
|
+
children: import_image_editor9.ADJUSTMENT_KINDS.map((k) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("option", { value: k.kind, children: k.label }, k.kind))
|
|
794
1294
|
}
|
|
795
1295
|
),
|
|
796
|
-
/* @__PURE__ */ (0,
|
|
1296
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
797
1297
|
"button",
|
|
798
1298
|
{
|
|
799
1299
|
type: "button",
|
|
800
1300
|
className: button,
|
|
801
1301
|
disabled: !sel,
|
|
802
|
-
onClick: () => sel && setLayerId(controller.addAdjustmentLayer({ kind: newKind, mask: (0,
|
|
1302
|
+
onClick: () => sel && setLayerId(controller.addAdjustmentLayer({ kind: newKind, mask: (0, import_image_editor9.layerMaskFromSelection)(sel) })),
|
|
803
1303
|
children: "New Masked Layer"
|
|
804
1304
|
}
|
|
805
1305
|
)
|
|
806
1306
|
] }),
|
|
807
|
-
list.length > 0 && target && /* @__PURE__ */ (0,
|
|
808
|
-
/* @__PURE__ */ (0,
|
|
1307
|
+
list.length > 0 && target && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex flex-col gap-2", children: [
|
|
1308
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
809
1309
|
"select",
|
|
810
1310
|
{
|
|
811
1311
|
"aria-label": "Mask layer",
|
|
812
1312
|
value: target.id,
|
|
813
1313
|
onChange: (e) => setLayerId(e.target.value),
|
|
814
1314
|
className: "bg-slate-950 border border-slate-800 rounded px-1 py-1 text-slate-200",
|
|
815
|
-
children: list.map((l) => /* @__PURE__ */ (0,
|
|
816
|
-
l.name || (0,
|
|
1315
|
+
children: list.map((l) => /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("option", { value: l.id, children: [
|
|
1316
|
+
l.name || (0, import_image_editor9.adjustmentKindLabel)(l.kind),
|
|
817
1317
|
l.mask ? " (masked)" : ""
|
|
818
1318
|
] }, l.id))
|
|
819
1319
|
}
|
|
820
1320
|
),
|
|
821
|
-
/* @__PURE__ */ (0,
|
|
822
|
-
/* @__PURE__ */ (0,
|
|
823
|
-
/* @__PURE__ */ (0,
|
|
824
|
-
/* @__PURE__ */ (0,
|
|
1321
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex flex-wrap gap-1", children: [
|
|
1322
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("button", { type: "button", className: button, disabled: !sel, onClick: () => controller.applySelectionAsLayerMask(target.id), children: "Mask with Selection" }),
|
|
1323
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("button", { type: "button", className: button, disabled: !target.mask, onClick: () => controller.selectionFromLayerMask(target.id), children: "Load as Selection" }),
|
|
1324
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("button", { type: "button", className: button, disabled: !target.mask, onClick: () => controller.setAdjustmentLayerMask(target.id, null), children: "Remove Mask" })
|
|
825
1325
|
] }),
|
|
826
|
-
target.mask && /* @__PURE__ */ (0,
|
|
827
|
-
/* @__PURE__ */ (0,
|
|
828
|
-
/* @__PURE__ */ (0,
|
|
829
|
-
/* @__PURE__ */ (0,
|
|
830
|
-
/* @__PURE__ */ (0,
|
|
1326
|
+
target.mask && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
|
|
1327
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("label", { className: "flex flex-col gap-1", children: [
|
|
1328
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("span", { className: "flex justify-between text-slate-400", children: [
|
|
1329
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { children: "Density" }),
|
|
1330
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("span", { className: "font-mono", children: [
|
|
831
1331
|
Math.round((target.mask.density ?? 1) * 100),
|
|
832
1332
|
"%"
|
|
833
1333
|
] })
|
|
834
1334
|
] }),
|
|
835
|
-
/* @__PURE__ */ (0,
|
|
1335
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
836
1336
|
"input",
|
|
837
1337
|
{
|
|
838
1338
|
type: "range",
|
|
@@ -846,15 +1346,15 @@ var RefineEdgePanel = ({ controller, selection, layers, className = "" }) => {
|
|
|
846
1346
|
}
|
|
847
1347
|
)
|
|
848
1348
|
] }),
|
|
849
|
-
/* @__PURE__ */ (0,
|
|
850
|
-
/* @__PURE__ */ (0,
|
|
851
|
-
/* @__PURE__ */ (0,
|
|
852
|
-
/* @__PURE__ */ (0,
|
|
1349
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("label", { className: "flex flex-col gap-1", children: [
|
|
1350
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("span", { className: "flex justify-between text-slate-400", children: [
|
|
1351
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { children: "Mask Feather" }),
|
|
1352
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("span", { className: "font-mono", children: [
|
|
853
1353
|
target.mask.feather ?? 0,
|
|
854
1354
|
"px"
|
|
855
1355
|
] })
|
|
856
1356
|
] }),
|
|
857
|
-
/* @__PURE__ */ (0,
|
|
1357
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
858
1358
|
"input",
|
|
859
1359
|
{
|
|
860
1360
|
type: "range",
|
|
@@ -874,7 +1374,7 @@ var RefineEdgePanel = ({ controller, selection, layers, className = "" }) => {
|
|
|
874
1374
|
};
|
|
875
1375
|
|
|
876
1376
|
// src/image-editor/ImageEditor.tsx
|
|
877
|
-
var
|
|
1377
|
+
var import_jsx_runtime9 = require("react/jsx-runtime");
|
|
878
1378
|
var ImageEditor = ({
|
|
879
1379
|
editor: externalEditor,
|
|
880
1380
|
src,
|
|
@@ -889,26 +1389,36 @@ var ImageEditor = ({
|
|
|
889
1389
|
const internalEditor = useImageEditor({ image: src });
|
|
890
1390
|
const editor = externalEditor || internalEditor;
|
|
891
1391
|
const { state, controller } = editor;
|
|
892
|
-
const legacyProcess = (0,
|
|
893
|
-
const canvasRef = (0,
|
|
894
|
-
const containerRef = (0,
|
|
895
|
-
const [activeTab, setActiveTab] = (0,
|
|
896
|
-
const [activeCropRatio, setActiveCropRatio] = (0,
|
|
897
|
-
const [selectionOptions, setSelectionOptions] = (0,
|
|
898
|
-
const selectionOptionsRef = (0,
|
|
1392
|
+
const legacyProcess = (0, import_image_editor10.normalizeProcessVersion)(state.adjustments.process) < import_image_editor10.LATEST_PROCESS_VERSION;
|
|
1393
|
+
const canvasRef = (0, import_react9.useRef)(null);
|
|
1394
|
+
const containerRef = (0, import_react9.useRef)(null);
|
|
1395
|
+
const [activeTab, setActiveTab] = (0, import_react9.useState)("crop");
|
|
1396
|
+
const [activeCropRatio, setActiveCropRatio] = (0, import_react9.useState)(null);
|
|
1397
|
+
const [selectionOptions, setSelectionOptions] = (0, import_react9.useState)(import_image_editor10.DEFAULT_SELECTION_TOOL_OPTIONS);
|
|
1398
|
+
const selectionOptionsRef = (0, import_react9.useRef)(selectionOptions);
|
|
899
1399
|
selectionOptionsRef.current = selectionOptions;
|
|
900
|
-
const selectionGestureRef = (0,
|
|
1400
|
+
const selectionGestureRef = (0, import_react9.useRef)(null);
|
|
901
1401
|
if (!selectionGestureRef.current || selectionGestureRef.current.controller !== controller) {
|
|
902
|
-
selectionGestureRef.current = new
|
|
1402
|
+
selectionGestureRef.current = new import_image_editor10.SelectionGesture(controller, () => selectionOptionsRef.current);
|
|
903
1403
|
}
|
|
904
|
-
const selectionDraftRef = (0,
|
|
905
|
-
const [selectionError, setSelectionError] = (0,
|
|
906
|
-
const
|
|
907
|
-
const
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
1404
|
+
const selectionDraftRef = (0, import_react9.useRef)(null);
|
|
1405
|
+
const [selectionError, setSelectionError] = (0, import_react9.useState)(null);
|
|
1406
|
+
const paintGestureRef = (0, import_react9.useRef)(null);
|
|
1407
|
+
const paintControllerRef = (0, import_react9.useRef)(controller);
|
|
1408
|
+
if (!paintGestureRef.current || paintControllerRef.current !== controller) {
|
|
1409
|
+
paintGestureRef.current = new import_image_editor10.PaintGesture(controller);
|
|
1410
|
+
paintControllerRef.current = controller;
|
|
1411
|
+
}
|
|
1412
|
+
const brushCursorRef = (0, import_react9.useRef)(null);
|
|
1413
|
+
const [paintError, setPaintError] = (0, import_react9.useState)(null);
|
|
1414
|
+
const [inpaintPrompt, setInpaintPrompt] = (0, import_react9.useState)("");
|
|
1415
|
+
const [inpaintMaskMode, setInpaintMaskMode] = (0, import_react9.useState)("annotations");
|
|
1416
|
+
const [isRemovingBg, setIsRemovingBg] = (0, import_react9.useState)(false);
|
|
1417
|
+
const [isInpainting, setIsInpainting] = (0, import_react9.useState)(false);
|
|
1418
|
+
const [aiError, setAiError] = (0, import_react9.useState)(null);
|
|
1419
|
+
const [aiSuccess, setAiSuccess] = (0, import_react9.useState)(null);
|
|
1420
|
+
const [aiTarget, setAiTarget] = (0, import_react9.useState)("auto");
|
|
1421
|
+
const aiOptions = aiTarget === "auto" ? {} : { target: aiTarget };
|
|
912
1422
|
const effectiveOnInpaint = onInpaint || aiHooks?.onInpaint;
|
|
913
1423
|
const effectiveOnRemoveBg = onRemoveBackground || aiHooks?.onRemoveBackground;
|
|
914
1424
|
const handleRemoveBackground = async () => {
|
|
@@ -920,10 +1430,10 @@ var ImageEditor = ({
|
|
|
920
1430
|
setAiSuccess(null);
|
|
921
1431
|
setIsRemovingBg(true);
|
|
922
1432
|
try {
|
|
923
|
-
const imageDataUrl = await
|
|
1433
|
+
const imageDataUrl = await controller.toAIInputDataURL(aiOptions);
|
|
924
1434
|
const result = await effectiveOnRemoveBg({ imageDataUrl });
|
|
925
1435
|
if (typeof result === "string") {
|
|
926
|
-
await editor.applyBackgroundRemovedImage(result);
|
|
1436
|
+
await editor.applyBackgroundRemovedImage(result, aiOptions);
|
|
927
1437
|
setAiSuccess("Background removed successfully!");
|
|
928
1438
|
} else {
|
|
929
1439
|
setAiSuccess("Background removal complete.");
|
|
@@ -947,7 +1457,7 @@ var ImageEditor = ({
|
|
|
947
1457
|
setAiSuccess(null);
|
|
948
1458
|
setIsInpainting(true);
|
|
949
1459
|
try {
|
|
950
|
-
const imageDataUrl = await
|
|
1460
|
+
const imageDataUrl = await controller.toAIInputDataURL(aiOptions);
|
|
951
1461
|
const maskDataUrl = await editor.toMaskDataURL({
|
|
952
1462
|
useAnnotations: inpaintMaskMode === "annotations",
|
|
953
1463
|
useCrop: inpaintMaskMode === "crop"
|
|
@@ -959,7 +1469,7 @@ var ImageEditor = ({
|
|
|
959
1469
|
cropBox: inpaintMaskMode === "crop" ? editor.state.crop : null
|
|
960
1470
|
});
|
|
961
1471
|
if (typeof result === "string") {
|
|
962
|
-
await editor.applyInpaintedImage(result);
|
|
1472
|
+
await editor.applyInpaintedImage(result, aiOptions);
|
|
963
1473
|
setAiSuccess("Inpainting applied successfully!");
|
|
964
1474
|
} else {
|
|
965
1475
|
setAiSuccess("Inpainting complete.");
|
|
@@ -970,18 +1480,18 @@ var ImageEditor = ({
|
|
|
970
1480
|
setIsInpainting(false);
|
|
971
1481
|
}
|
|
972
1482
|
};
|
|
973
|
-
const [activeColor, setActiveColor] = (0,
|
|
974
|
-
const [strokeWidth, setStrokeWidth] = (0,
|
|
975
|
-
const [fontSize, setFontSize] = (0,
|
|
976
|
-
const [textInput, setTextInput] = (0,
|
|
977
|
-
const draftAnnotationRef = (0,
|
|
978
|
-
const draftCropRef = (0,
|
|
979
|
-
const cropDragInfoRef = (0,
|
|
980
|
-
const annotationDragInfoRef = (0,
|
|
981
|
-
const isDrawingRef = (0,
|
|
982
|
-
const currentPathRef = (0,
|
|
983
|
-
const dragStartPointRef = (0,
|
|
984
|
-
const renderCanvas = (0,
|
|
1483
|
+
const [activeColor, setActiveColor] = (0, import_react9.useState)("#ef4444");
|
|
1484
|
+
const [strokeWidth, setStrokeWidth] = (0, import_react9.useState)(3);
|
|
1485
|
+
const [fontSize, setFontSize] = (0, import_react9.useState)(24);
|
|
1486
|
+
const [textInput, setTextInput] = (0, import_react9.useState)("");
|
|
1487
|
+
const draftAnnotationRef = (0, import_react9.useRef)(null);
|
|
1488
|
+
const draftCropRef = (0, import_react9.useRef)(null);
|
|
1489
|
+
const cropDragInfoRef = (0, import_react9.useRef)(null);
|
|
1490
|
+
const annotationDragInfoRef = (0, import_react9.useRef)(null);
|
|
1491
|
+
const isDrawingRef = (0, import_react9.useRef)(false);
|
|
1492
|
+
const currentPathRef = (0, import_react9.useRef)([]);
|
|
1493
|
+
const dragStartPointRef = (0, import_react9.useRef)(null);
|
|
1494
|
+
const renderCanvas = (0, import_react9.useCallback)(
|
|
985
1495
|
async (draftAnnotation, draftCrop) => {
|
|
986
1496
|
const canvas = canvasRef.current;
|
|
987
1497
|
const container = containerRef.current;
|
|
@@ -996,12 +1506,13 @@ var ImageEditor = ({
|
|
|
996
1506
|
interactive: true,
|
|
997
1507
|
draftAnnotation: draftAnnotation !== void 0 ? draftAnnotation : draftAnnotationRef.current,
|
|
998
1508
|
draftCrop: draftCrop !== void 0 ? draftCrop : draftCropRef.current,
|
|
999
|
-
draftSelectionPath: selectionDraftRef.current
|
|
1509
|
+
draftSelectionPath: selectionDraftRef.current,
|
|
1510
|
+
brushCursor: activeTab === "paint" && brushCursorRef.current && controller.getState().activeTool !== "move" ? { ...brushCursorRef.current, diameter: controller.getState().paint.brush.size } : null
|
|
1000
1511
|
});
|
|
1001
1512
|
},
|
|
1002
1513
|
[controller, activeTab]
|
|
1003
1514
|
);
|
|
1004
|
-
(0,
|
|
1515
|
+
(0, import_react9.useEffect)(() => {
|
|
1005
1516
|
const raf = typeof requestAnimationFrame !== "undefined" ? requestAnimationFrame : (cb) => setTimeout(cb, 0);
|
|
1006
1517
|
const caf = typeof cancelAnimationFrame !== "undefined" ? cancelAnimationFrame : (id) => clearTimeout(id);
|
|
1007
1518
|
const animId = raf(() => {
|
|
@@ -1009,12 +1520,12 @@ var ImageEditor = ({
|
|
|
1009
1520
|
});
|
|
1010
1521
|
return () => caf(animId);
|
|
1011
1522
|
}, [renderCanvas, state]);
|
|
1012
|
-
(0,
|
|
1523
|
+
(0, import_react9.useEffect)(() => {
|
|
1013
1524
|
if (activeTab === "crop" && editor.state.activeTool !== "crop") {
|
|
1014
1525
|
editor.setTool("crop");
|
|
1015
1526
|
}
|
|
1016
1527
|
}, [activeTab, editor]);
|
|
1017
|
-
(0,
|
|
1528
|
+
(0, import_react9.useEffect)(() => {
|
|
1018
1529
|
const container = containerRef.current;
|
|
1019
1530
|
if (!container) return;
|
|
1020
1531
|
if (typeof ResizeObserver !== "undefined") {
|
|
@@ -1040,13 +1551,60 @@ var ImageEditor = ({
|
|
|
1040
1551
|
y: (e.clientY - rect.top) * scaleY
|
|
1041
1552
|
};
|
|
1042
1553
|
};
|
|
1554
|
+
const paintSamples = (e) => {
|
|
1555
|
+
const canvas = canvasRef.current;
|
|
1556
|
+
const native = e.nativeEvent;
|
|
1557
|
+
const events = typeof native.getCoalescedEvents === "function" ? native.getCoalescedEvents() : [];
|
|
1558
|
+
const list = events.length ? events : [native];
|
|
1559
|
+
const rect = canvas.getBoundingClientRect();
|
|
1560
|
+
const sx = rect.width > 0 ? canvas.width / rect.width : 1;
|
|
1561
|
+
const sy = rect.height > 0 ? canvas.height / rect.height : 1;
|
|
1562
|
+
const s = controller.getState();
|
|
1563
|
+
return list.map((ev) => ({
|
|
1564
|
+
...(0, import_image_editor10.canvasToImagePoint)({ x: (ev.clientX - rect.left) * sx, y: (ev.clientY - rect.top) * sy }, s, canvas.width, canvas.height, false),
|
|
1565
|
+
pressure: (0, import_image_editor10.pointerPressure)(ev)
|
|
1566
|
+
}));
|
|
1567
|
+
};
|
|
1568
|
+
const handlePaintPointerDown = (e) => {
|
|
1569
|
+
if (activeTab !== "paint" || e.button !== 0) return;
|
|
1570
|
+
e.preventDefault();
|
|
1571
|
+
try {
|
|
1572
|
+
e.currentTarget.setPointerCapture?.(e.pointerId);
|
|
1573
|
+
} catch {
|
|
1574
|
+
}
|
|
1575
|
+
const [first] = paintSamples(e);
|
|
1576
|
+
setPaintError(paintGestureRef.current.down(first));
|
|
1577
|
+
};
|
|
1578
|
+
const handlePaintPointerMove = (e) => {
|
|
1579
|
+
if (activeTab !== "paint") return;
|
|
1580
|
+
const samples = paintSamples(e);
|
|
1581
|
+
brushCursorRef.current = samples[samples.length - 1] ?? null;
|
|
1582
|
+
if (paintGestureRef.current.active) paintGestureRef.current.moveTo(samples);
|
|
1583
|
+
else void renderCanvas();
|
|
1584
|
+
};
|
|
1585
|
+
const handlePaintPointerUp = () => {
|
|
1586
|
+
if (activeTab !== "paint") return;
|
|
1587
|
+
const g = paintGestureRef.current;
|
|
1588
|
+
if (g.active) {
|
|
1589
|
+
g.up();
|
|
1590
|
+
if (g.error) setPaintError(g.error);
|
|
1591
|
+
}
|
|
1592
|
+
};
|
|
1593
|
+
(0, import_react9.useEffect)(() => {
|
|
1594
|
+
if (activeTab !== "paint" || typeof window === "undefined") return;
|
|
1595
|
+
const onKey = (e) => {
|
|
1596
|
+
if ((0, import_image_editor10.handlePaintShortcut)(controller, e)) e.preventDefault();
|
|
1597
|
+
};
|
|
1598
|
+
window.addEventListener("keydown", onKey);
|
|
1599
|
+
return () => window.removeEventListener("keydown", onKey);
|
|
1600
|
+
}, [activeTab, controller]);
|
|
1043
1601
|
const handleMouseDown = (e) => {
|
|
1044
1602
|
const canvas = canvasRef.current;
|
|
1045
1603
|
if (!canvas) return;
|
|
1046
1604
|
const canvasPt = getCanvasEventPoint(e);
|
|
1047
1605
|
if (activeTab === "select") {
|
|
1048
1606
|
const gesture = selectionGestureRef.current;
|
|
1049
|
-
const imgPt = (0,
|
|
1607
|
+
const imgPt = (0, import_image_editor10.canvasToImagePoint)(canvasPt, state, canvas.width, canvas.height, false);
|
|
1050
1608
|
void gesture.begin(imgPt, e).then(() => setSelectionError(gesture.error));
|
|
1051
1609
|
return;
|
|
1052
1610
|
}
|
|
@@ -1057,8 +1615,8 @@ var ImageEditor = ({
|
|
|
1057
1615
|
width: state.imageDimensions.width,
|
|
1058
1616
|
height: state.imageDimensions.height
|
|
1059
1617
|
};
|
|
1060
|
-
const handle = (0,
|
|
1061
|
-
const startImgPt = (0,
|
|
1618
|
+
const handle = (0, import_image_editor10.hitTestCrop)(canvasPt, state, canvas.width, canvas.height, 14, activeCrop);
|
|
1619
|
+
const startImgPt = (0, import_image_editor10.canvasToImagePoint)(canvasPt, state, canvas.width, canvas.height, true);
|
|
1062
1620
|
if (handle) {
|
|
1063
1621
|
cropDragInfoRef.current = {
|
|
1064
1622
|
handle,
|
|
@@ -1077,12 +1635,12 @@ var ImageEditor = ({
|
|
|
1077
1635
|
return;
|
|
1078
1636
|
}
|
|
1079
1637
|
if (activeTab === "annotate") {
|
|
1080
|
-
const imgPt = (0,
|
|
1638
|
+
const imgPt = (0, import_image_editor10.canvasToImagePoint)(canvasPt, state, canvas.width, canvas.height, false);
|
|
1081
1639
|
dragStartPointRef.current = imgPt;
|
|
1082
1640
|
if (state.activeTool === "select") {
|
|
1083
1641
|
for (let i = state.annotations.length - 1; i >= 0; i--) {
|
|
1084
1642
|
const ann = state.annotations[i];
|
|
1085
|
-
if ((0,
|
|
1643
|
+
if ((0, import_image_editor10.hitTestAnnotation)(imgPt, ann)) {
|
|
1086
1644
|
editor.selectAnnotation(ann.id);
|
|
1087
1645
|
annotationDragInfoRef.current = {
|
|
1088
1646
|
annId: ann.id,
|
|
@@ -1118,7 +1676,7 @@ var ImageEditor = ({
|
|
|
1118
1676
|
const canvasPt = getCanvasEventPoint(e);
|
|
1119
1677
|
if (activeTab === "select") {
|
|
1120
1678
|
const gesture = selectionGestureRef.current;
|
|
1121
|
-
if (gesture.active && gesture.move((0,
|
|
1679
|
+
if (gesture.active && gesture.move((0, import_image_editor10.canvasToImagePoint)(canvasPt, state, canvas.width, canvas.height, false))) {
|
|
1122
1680
|
selectionDraftRef.current = gesture.draftPath;
|
|
1123
1681
|
renderCanvas();
|
|
1124
1682
|
}
|
|
@@ -1127,7 +1685,7 @@ var ImageEditor = ({
|
|
|
1127
1685
|
if (activeTab === "crop") {
|
|
1128
1686
|
const drag = cropDragInfoRef.current;
|
|
1129
1687
|
if (drag) {
|
|
1130
|
-
const currentImgPt = (0,
|
|
1688
|
+
const currentImgPt = (0, import_image_editor10.canvasToImagePoint)(canvasPt, state, canvas.width, canvas.height, true);
|
|
1131
1689
|
let newCrop;
|
|
1132
1690
|
if (drag.handle === "new") {
|
|
1133
1691
|
const minX = Math.max(0, Math.min(drag.startImgPt.x, currentImgPt.x));
|
|
@@ -1141,7 +1699,7 @@ var ImageEditor = ({
|
|
|
1141
1699
|
height: Math.round(Math.max(20, maxY - minY))
|
|
1142
1700
|
};
|
|
1143
1701
|
} else {
|
|
1144
|
-
newCrop = (0,
|
|
1702
|
+
newCrop = (0, import_image_editor10.calculateCropDrag)(
|
|
1145
1703
|
drag.startCrop,
|
|
1146
1704
|
drag.handle,
|
|
1147
1705
|
drag.startImgPt,
|
|
@@ -1161,15 +1719,15 @@ var ImageEditor = ({
|
|
|
1161
1719
|
width: state.imageDimensions.width,
|
|
1162
1720
|
height: state.imageDimensions.height
|
|
1163
1721
|
};
|
|
1164
|
-
const handle = (0,
|
|
1165
|
-
canvas.style.cursor = (0,
|
|
1722
|
+
const handle = (0, import_image_editor10.hitTestCrop)(canvasPt, state, canvas.width, canvas.height, 14, activeCrop);
|
|
1723
|
+
canvas.style.cursor = (0, import_image_editor10.getCropCursor)(handle, state.transform);
|
|
1166
1724
|
}
|
|
1167
1725
|
return;
|
|
1168
1726
|
}
|
|
1169
1727
|
if (activeTab === "annotate") {
|
|
1170
1728
|
if (annotationDragInfoRef.current) {
|
|
1171
1729
|
const drag = annotationDragInfoRef.current;
|
|
1172
|
-
const currentImgPt2 = (0,
|
|
1730
|
+
const currentImgPt2 = (0, import_image_editor10.canvasToImagePoint)(canvasPt, state, canvas.width, canvas.height, false);
|
|
1173
1731
|
const dx = currentImgPt2.x - drag.startImgPt.x;
|
|
1174
1732
|
const dy = currentImgPt2.y - drag.startImgPt.y;
|
|
1175
1733
|
editor.updateAnnotation(drag.annId, {
|
|
@@ -1181,10 +1739,10 @@ var ImageEditor = ({
|
|
|
1181
1739
|
const startPt = dragStartPointRef.current;
|
|
1182
1740
|
if (!startPt) {
|
|
1183
1741
|
if (state.activeTool === "select") {
|
|
1184
|
-
const imgPt = (0,
|
|
1742
|
+
const imgPt = (0, import_image_editor10.canvasToImagePoint)(canvasPt, state, canvas.width, canvas.height, false);
|
|
1185
1743
|
let hovered = false;
|
|
1186
1744
|
for (let i = state.annotations.length - 1; i >= 0; i--) {
|
|
1187
|
-
if ((0,
|
|
1745
|
+
if ((0, import_image_editor10.hitTestAnnotation)(imgPt, state.annotations[i])) {
|
|
1188
1746
|
hovered = true;
|
|
1189
1747
|
break;
|
|
1190
1748
|
}
|
|
@@ -1195,7 +1753,7 @@ var ImageEditor = ({
|
|
|
1195
1753
|
}
|
|
1196
1754
|
return;
|
|
1197
1755
|
}
|
|
1198
|
-
const currentImgPt = (0,
|
|
1756
|
+
const currentImgPt = (0, import_image_editor10.canvasToImagePoint)(canvasPt, state, canvas.width, canvas.height, false);
|
|
1199
1757
|
if (state.activeTool === "pen" && isDrawingRef.current) {
|
|
1200
1758
|
currentPathRef.current.push(currentImgPt);
|
|
1201
1759
|
const draft = {
|
|
@@ -1275,7 +1833,7 @@ var ImageEditor = ({
|
|
|
1275
1833
|
if (activeTab === "select") {
|
|
1276
1834
|
const gesture = selectionGestureRef.current;
|
|
1277
1835
|
if (gesture.active) {
|
|
1278
|
-
gesture.end((0,
|
|
1836
|
+
gesture.end((0, import_image_editor10.canvasToImagePoint)(canvasPt, state, canvas.width, canvas.height, false), e);
|
|
1279
1837
|
selectionDraftRef.current = null;
|
|
1280
1838
|
renderCanvas();
|
|
1281
1839
|
}
|
|
@@ -1299,7 +1857,7 @@ var ImageEditor = ({
|
|
|
1299
1857
|
}
|
|
1300
1858
|
const startPt = dragStartPointRef.current;
|
|
1301
1859
|
if (startPt) {
|
|
1302
|
-
const currentImgPt = (0,
|
|
1860
|
+
const currentImgPt = (0, import_image_editor10.canvasToImagePoint)(canvasPt, state, canvas.width, canvas.height, false);
|
|
1303
1861
|
if (state.activeTool === "pen" && isDrawingRef.current) {
|
|
1304
1862
|
if (currentPathRef.current.length > 1) {
|
|
1305
1863
|
editor.addAnnotation({
|
|
@@ -1395,15 +1953,15 @@ var ImageEditor = ({
|
|
|
1395
1953
|
link.click();
|
|
1396
1954
|
}
|
|
1397
1955
|
};
|
|
1398
|
-
return /* @__PURE__ */ (0,
|
|
1956
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
|
|
1399
1957
|
"div",
|
|
1400
1958
|
{
|
|
1401
1959
|
className: `flex flex-col bg-slate-900 border border-slate-800 rounded-xl overflow-hidden shadow-2xl text-slate-100 ${className}`,
|
|
1402
1960
|
style,
|
|
1403
1961
|
children: [
|
|
1404
|
-
/* @__PURE__ */ (0,
|
|
1405
|
-
/* @__PURE__ */ (0,
|
|
1406
|
-
/* @__PURE__ */ (0,
|
|
1962
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("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: [
|
|
1963
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "flex items-center gap-1 bg-slate-900 p-1 rounded-lg border border-slate-800 text-xs", children: [
|
|
1964
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1407
1965
|
"button",
|
|
1408
1966
|
{
|
|
1409
1967
|
type: "button",
|
|
@@ -1415,7 +1973,7 @@ var ImageEditor = ({
|
|
|
1415
1973
|
children: "Transform & Crop"
|
|
1416
1974
|
}
|
|
1417
1975
|
),
|
|
1418
|
-
/* @__PURE__ */ (0,
|
|
1976
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1419
1977
|
"button",
|
|
1420
1978
|
{
|
|
1421
1979
|
type: "button",
|
|
@@ -1427,7 +1985,31 @@ var ImageEditor = ({
|
|
|
1427
1985
|
children: "Adjustments"
|
|
1428
1986
|
}
|
|
1429
1987
|
),
|
|
1430
|
-
/* @__PURE__ */ (0,
|
|
1988
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1989
|
+
"button",
|
|
1990
|
+
{
|
|
1991
|
+
type: "button",
|
|
1992
|
+
onClick: () => {
|
|
1993
|
+
setActiveTab("layers");
|
|
1994
|
+
editor.setTool("select");
|
|
1995
|
+
},
|
|
1996
|
+
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"}`,
|
|
1997
|
+
children: "Layers"
|
|
1998
|
+
}
|
|
1999
|
+
),
|
|
2000
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
2001
|
+
"button",
|
|
2002
|
+
{
|
|
2003
|
+
type: "button",
|
|
2004
|
+
onClick: () => {
|
|
2005
|
+
setActiveTab("paint");
|
|
2006
|
+
editor.setTool("brush");
|
|
2007
|
+
},
|
|
2008
|
+
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"}`,
|
|
2009
|
+
children: "Paint"
|
|
2010
|
+
}
|
|
2011
|
+
),
|
|
2012
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1431
2013
|
"button",
|
|
1432
2014
|
{
|
|
1433
2015
|
type: "button",
|
|
@@ -1439,7 +2021,7 @@ var ImageEditor = ({
|
|
|
1439
2021
|
children: "Select & Mask"
|
|
1440
2022
|
}
|
|
1441
2023
|
),
|
|
1442
|
-
/* @__PURE__ */ (0,
|
|
2024
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1443
2025
|
"button",
|
|
1444
2026
|
{
|
|
1445
2027
|
type: "button",
|
|
@@ -1451,7 +2033,7 @@ var ImageEditor = ({
|
|
|
1451
2033
|
children: "Annotations"
|
|
1452
2034
|
}
|
|
1453
2035
|
),
|
|
1454
|
-
/* @__PURE__ */ (0,
|
|
2036
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1455
2037
|
"button",
|
|
1456
2038
|
{
|
|
1457
2039
|
type: "button",
|
|
@@ -1463,7 +2045,7 @@ var ImageEditor = ({
|
|
|
1463
2045
|
children: "Depth Mask"
|
|
1464
2046
|
}
|
|
1465
2047
|
),
|
|
1466
|
-
/* @__PURE__ */ (0,
|
|
2048
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
|
|
1467
2049
|
"button",
|
|
1468
2050
|
{
|
|
1469
2051
|
type: "button",
|
|
@@ -1473,14 +2055,14 @@ var ImageEditor = ({
|
|
|
1473
2055
|
},
|
|
1474
2056
|
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"}`,
|
|
1475
2057
|
children: [
|
|
1476
|
-
/* @__PURE__ */ (0,
|
|
2058
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("svg", { className: "w-3.5 h-3.5", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("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" }) }),
|
|
1477
2059
|
"AI Tools"
|
|
1478
2060
|
]
|
|
1479
2061
|
}
|
|
1480
2062
|
)
|
|
1481
2063
|
] }),
|
|
1482
|
-
/* @__PURE__ */ (0,
|
|
1483
|
-
/* @__PURE__ */ (0,
|
|
2064
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "flex items-center gap-2 text-xs", children: [
|
|
2065
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1484
2066
|
"button",
|
|
1485
2067
|
{
|
|
1486
2068
|
type: "button",
|
|
@@ -1491,7 +2073,7 @@ var ImageEditor = ({
|
|
|
1491
2073
|
children: "\u21A9"
|
|
1492
2074
|
}
|
|
1493
2075
|
),
|
|
1494
|
-
/* @__PURE__ */ (0,
|
|
2076
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1495
2077
|
"button",
|
|
1496
2078
|
{
|
|
1497
2079
|
type: "button",
|
|
@@ -1502,7 +2084,7 @@ var ImageEditor = ({
|
|
|
1502
2084
|
children: "\u21AA"
|
|
1503
2085
|
}
|
|
1504
2086
|
),
|
|
1505
|
-
/* @__PURE__ */ (0,
|
|
2087
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1506
2088
|
"button",
|
|
1507
2089
|
{
|
|
1508
2090
|
type: "button",
|
|
@@ -1511,8 +2093,8 @@ var ImageEditor = ({
|
|
|
1511
2093
|
children: "Reset"
|
|
1512
2094
|
}
|
|
1513
2095
|
),
|
|
1514
|
-
/* @__PURE__ */ (0,
|
|
1515
|
-
/* @__PURE__ */ (0,
|
|
2096
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "h-4 w-px bg-slate-800 mx-1" }),
|
|
2097
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1516
2098
|
"button",
|
|
1517
2099
|
{
|
|
1518
2100
|
type: "button",
|
|
@@ -1522,11 +2104,11 @@ var ImageEditor = ({
|
|
|
1522
2104
|
children: "-"
|
|
1523
2105
|
}
|
|
1524
2106
|
),
|
|
1525
|
-
/* @__PURE__ */ (0,
|
|
2107
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("span", { className: "font-mono text-slate-400 w-10 text-center", children: [
|
|
1526
2108
|
Math.round(state.zoom * 100),
|
|
1527
2109
|
"%"
|
|
1528
2110
|
] }),
|
|
1529
|
-
/* @__PURE__ */ (0,
|
|
2111
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1530
2112
|
"button",
|
|
1531
2113
|
{
|
|
1532
2114
|
type: "button",
|
|
@@ -1536,8 +2118,8 @@ var ImageEditor = ({
|
|
|
1536
2118
|
children: "+"
|
|
1537
2119
|
}
|
|
1538
2120
|
),
|
|
1539
|
-
/* @__PURE__ */ (0,
|
|
1540
|
-
/* @__PURE__ */ (0,
|
|
2121
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "h-4 w-px bg-slate-800 mx-1" }),
|
|
2122
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1541
2123
|
"button",
|
|
1542
2124
|
{
|
|
1543
2125
|
type: "button",
|
|
@@ -1548,7 +2130,7 @@ var ImageEditor = ({
|
|
|
1548
2130
|
children: "Hold Compare"
|
|
1549
2131
|
}
|
|
1550
2132
|
),
|
|
1551
|
-
/* @__PURE__ */ (0,
|
|
2133
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1552
2134
|
"button",
|
|
1553
2135
|
{
|
|
1554
2136
|
type: "button",
|
|
@@ -1559,13 +2141,13 @@ var ImageEditor = ({
|
|
|
1559
2141
|
)
|
|
1560
2142
|
] })
|
|
1561
2143
|
] }),
|
|
1562
|
-
/* @__PURE__ */ (0,
|
|
1563
|
-
/* @__PURE__ */ (0,
|
|
2144
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "flex flex-col md:flex-row flex-1 min-h-[520px] md:h-[560px]", children: [
|
|
2145
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1564
2146
|
"div",
|
|
1565
2147
|
{
|
|
1566
2148
|
ref: containerRef,
|
|
1567
2149
|
className: "relative flex-1 h-[360px] md:h-full min-h-[320px] bg-slate-950 overflow-hidden select-none",
|
|
1568
|
-
children: /* @__PURE__ */ (0,
|
|
2150
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1569
2151
|
"canvas",
|
|
1570
2152
|
{
|
|
1571
2153
|
ref: canvasRef,
|
|
@@ -1573,16 +2155,21 @@ var ImageEditor = ({
|
|
|
1573
2155
|
onMouseMove: handleMouseMove,
|
|
1574
2156
|
onMouseUp: handleMouseUp,
|
|
1575
2157
|
onMouseLeave: handleMouseUp,
|
|
1576
|
-
|
|
2158
|
+
onPointerDown: handlePaintPointerDown,
|
|
2159
|
+
onPointerMove: handlePaintPointerMove,
|
|
2160
|
+
onPointerUp: handlePaintPointerUp,
|
|
2161
|
+
onPointerCancel: () => paintGestureRef.current?.cancel(),
|
|
2162
|
+
"data-testid": "image-editor-canvas",
|
|
2163
|
+
className: `absolute inset-0 w-full h-full block ${activeTab === "paint" ? "touch-none cursor-crosshair" : ""}`
|
|
1577
2164
|
}
|
|
1578
2165
|
)
|
|
1579
2166
|
}
|
|
1580
2167
|
),
|
|
1581
|
-
/* @__PURE__ */ (0,
|
|
1582
|
-
activeTab === "crop" && /* @__PURE__ */ (0,
|
|
1583
|
-
/* @__PURE__ */ (0,
|
|
1584
|
-
/* @__PURE__ */ (0,
|
|
1585
|
-
/* @__PURE__ */ (0,
|
|
2168
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("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: [
|
|
2169
|
+
activeTab === "crop" && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "flex flex-col gap-4 text-xs", children: [
|
|
2170
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Aspect Ratio Presets" }),
|
|
2171
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "grid grid-cols-3 gap-2", children: [
|
|
2172
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1586
2173
|
"button",
|
|
1587
2174
|
{
|
|
1588
2175
|
type: "button",
|
|
@@ -1594,7 +2181,7 @@ var ImageEditor = ({
|
|
|
1594
2181
|
children: "Freeform"
|
|
1595
2182
|
}
|
|
1596
2183
|
),
|
|
1597
|
-
/* @__PURE__ */ (0,
|
|
2184
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1598
2185
|
"button",
|
|
1599
2186
|
{
|
|
1600
2187
|
type: "button",
|
|
@@ -1606,7 +2193,7 @@ var ImageEditor = ({
|
|
|
1606
2193
|
children: "1:1 Square"
|
|
1607
2194
|
}
|
|
1608
2195
|
),
|
|
1609
|
-
/* @__PURE__ */ (0,
|
|
2196
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1610
2197
|
"button",
|
|
1611
2198
|
{
|
|
1612
2199
|
type: "button",
|
|
@@ -1618,7 +2205,7 @@ var ImageEditor = ({
|
|
|
1618
2205
|
children: "4:3 Standard"
|
|
1619
2206
|
}
|
|
1620
2207
|
),
|
|
1621
|
-
/* @__PURE__ */ (0,
|
|
2208
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1622
2209
|
"button",
|
|
1623
2210
|
{
|
|
1624
2211
|
type: "button",
|
|
@@ -1630,7 +2217,7 @@ var ImageEditor = ({
|
|
|
1630
2217
|
children: "16:9 Wide"
|
|
1631
2218
|
}
|
|
1632
2219
|
),
|
|
1633
|
-
/* @__PURE__ */ (0,
|
|
2220
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1634
2221
|
"button",
|
|
1635
2222
|
{
|
|
1636
2223
|
type: "button",
|
|
@@ -1642,7 +2229,7 @@ var ImageEditor = ({
|
|
|
1642
2229
|
children: "3:2 Photo"
|
|
1643
2230
|
}
|
|
1644
2231
|
),
|
|
1645
|
-
/* @__PURE__ */ (0,
|
|
2232
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1646
2233
|
"button",
|
|
1647
2234
|
{
|
|
1648
2235
|
type: "button",
|
|
@@ -1655,10 +2242,10 @@ var ImageEditor = ({
|
|
|
1655
2242
|
}
|
|
1656
2243
|
)
|
|
1657
2244
|
] }),
|
|
1658
|
-
/* @__PURE__ */ (0,
|
|
1659
|
-
/* @__PURE__ */ (0,
|
|
1660
|
-
/* @__PURE__ */ (0,
|
|
1661
|
-
/* @__PURE__ */ (0,
|
|
2245
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "h-px bg-slate-800 my-1" }),
|
|
2246
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Orientation & Flips" }),
|
|
2247
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "grid grid-cols-2 gap-2", children: [
|
|
2248
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1662
2249
|
"button",
|
|
1663
2250
|
{
|
|
1664
2251
|
type: "button",
|
|
@@ -1667,7 +2254,7 @@ var ImageEditor = ({
|
|
|
1667
2254
|
children: "Rotate -90\xB0"
|
|
1668
2255
|
}
|
|
1669
2256
|
),
|
|
1670
|
-
/* @__PURE__ */ (0,
|
|
2257
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1671
2258
|
"button",
|
|
1672
2259
|
{
|
|
1673
2260
|
type: "button",
|
|
@@ -1676,7 +2263,7 @@ var ImageEditor = ({
|
|
|
1676
2263
|
children: "Rotate +90\xB0"
|
|
1677
2264
|
}
|
|
1678
2265
|
),
|
|
1679
|
-
/* @__PURE__ */ (0,
|
|
2266
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1680
2267
|
"button",
|
|
1681
2268
|
{
|
|
1682
2269
|
type: "button",
|
|
@@ -1685,7 +2272,7 @@ var ImageEditor = ({
|
|
|
1685
2272
|
children: "Flip Horizontal"
|
|
1686
2273
|
}
|
|
1687
2274
|
),
|
|
1688
|
-
/* @__PURE__ */ (0,
|
|
2275
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1689
2276
|
"button",
|
|
1690
2277
|
{
|
|
1691
2278
|
type: "button",
|
|
@@ -1696,10 +2283,10 @@ var ImageEditor = ({
|
|
|
1696
2283
|
)
|
|
1697
2284
|
] })
|
|
1698
2285
|
] }),
|
|
1699
|
-
activeTab === "adjust" && /* @__PURE__ */ (0,
|
|
1700
|
-
/* @__PURE__ */ (0,
|
|
1701
|
-
/* @__PURE__ */ (0,
|
|
1702
|
-
/* @__PURE__ */ (0,
|
|
2286
|
+
activeTab === "adjust" && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "flex flex-col gap-3 text-xs", children: [
|
|
2287
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "flex items-center justify-between", children: [
|
|
2288
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Color Adjustments" }),
|
|
2289
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1703
2290
|
"button",
|
|
1704
2291
|
{
|
|
1705
2292
|
type: "button",
|
|
@@ -1709,25 +2296,25 @@ var ImageEditor = ({
|
|
|
1709
2296
|
}
|
|
1710
2297
|
)
|
|
1711
2298
|
] }),
|
|
1712
|
-
/* @__PURE__ */ (0,
|
|
1713
|
-
|
|
1714
|
-
/* @__PURE__ */ (0,
|
|
2299
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Histogram, { histogram: editor.histogram }),
|
|
2300
|
+
import_image_editor10.IMAGE_ADJUSTMENT_CONTROLS.map((spec) => /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_react9.default.Fragment, { children: [
|
|
2301
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1715
2302
|
ControlSlider,
|
|
1716
2303
|
{
|
|
1717
2304
|
spec,
|
|
1718
|
-
value: (0,
|
|
2305
|
+
value: (0, import_image_editor10.readAdjustmentControls)(state.adjustments)[spec.id],
|
|
1719
2306
|
disabled: spec.requiresProcess !== void 0 && legacyProcess,
|
|
1720
2307
|
onChange: (v) => editor.setAdjustments({ [spec.id]: v })
|
|
1721
2308
|
}
|
|
1722
2309
|
),
|
|
1723
|
-
spec.id === "tint" && legacyProcess && /* @__PURE__ */ (0,
|
|
2310
|
+
spec.id === "tint" && legacyProcess && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
|
|
1724
2311
|
"div",
|
|
1725
2312
|
{
|
|
1726
2313
|
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",
|
|
1727
2314
|
"data-testid": "image-editor-process-notice",
|
|
1728
2315
|
children: [
|
|
1729
|
-
/* @__PURE__ */ (0,
|
|
1730
|
-
/* @__PURE__ */ (0,
|
|
2316
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { children: "Legacy rendering (process 1). Tint needs the current process." }),
|
|
2317
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1731
2318
|
"button",
|
|
1732
2319
|
{
|
|
1733
2320
|
type: "button",
|
|
@@ -1740,7 +2327,7 @@ var ImageEditor = ({
|
|
|
1740
2327
|
}
|
|
1741
2328
|
)
|
|
1742
2329
|
] }, spec.id)),
|
|
1743
|
-
/* @__PURE__ */ (0,
|
|
2330
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "border-t border-slate-700/60 pt-3", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1744
2331
|
AdjustmentLayersPanel,
|
|
1745
2332
|
{
|
|
1746
2333
|
controller,
|
|
@@ -1749,9 +2336,19 @@ var ImageEditor = ({
|
|
|
1749
2336
|
}
|
|
1750
2337
|
) })
|
|
1751
2338
|
] }),
|
|
1752
|
-
activeTab === "
|
|
1753
|
-
/* @__PURE__ */ (0,
|
|
1754
|
-
/* @__PURE__ */ (0,
|
|
2339
|
+
activeTab === "layers" && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "flex flex-col gap-3 text-xs", children: [
|
|
2340
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Layers" }),
|
|
2341
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(LayersPanel, { controller })
|
|
2342
|
+
] }),
|
|
2343
|
+
activeTab === "paint" && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "flex flex-col gap-3 text-xs", children: [
|
|
2344
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Paint" }),
|
|
2345
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(BrushOptionsBar, { controller }),
|
|
2346
|
+
paintError && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("p", { role: "alert", className: "text-rose-400", "data-testid": "paint-error", children: paintError }),
|
|
2347
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "border-t border-slate-700/60 pt-3", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(LayersPanel, { controller }) })
|
|
2348
|
+
] }),
|
|
2349
|
+
activeTab === "annotate" && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "flex flex-col gap-3 text-xs", children: [
|
|
2350
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Draw & Annotate" }),
|
|
2351
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "grid grid-cols-3 gap-1.5", children: ["pen", "rect", "circle", "arrow", "line"].map((t) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1755
2352
|
"button",
|
|
1756
2353
|
{
|
|
1757
2354
|
type: "button",
|
|
@@ -1761,9 +2358,9 @@ var ImageEditor = ({
|
|
|
1761
2358
|
},
|
|
1762
2359
|
t
|
|
1763
2360
|
)) }),
|
|
1764
|
-
/* @__PURE__ */ (0,
|
|
1765
|
-
/* @__PURE__ */ (0,
|
|
1766
|
-
/* @__PURE__ */ (0,
|
|
2361
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "flex items-center gap-2 mt-2", children: [
|
|
2362
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("label", { className: "text-slate-400", children: "Color:" }),
|
|
2363
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1767
2364
|
"input",
|
|
1768
2365
|
{
|
|
1769
2366
|
type: "color",
|
|
@@ -1772,15 +2369,15 @@ var ImageEditor = ({
|
|
|
1772
2369
|
className: "w-8 h-8 rounded border border-slate-700 cursor-pointer bg-transparent"
|
|
1773
2370
|
}
|
|
1774
2371
|
),
|
|
1775
|
-
/* @__PURE__ */ (0,
|
|
1776
|
-
/* @__PURE__ */ (0,
|
|
1777
|
-
/* @__PURE__ */ (0,
|
|
1778
|
-
/* @__PURE__ */ (0,
|
|
2372
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "flex-1 ml-2", children: [
|
|
2373
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "flex justify-between text-slate-400 mb-0.5", children: [
|
|
2374
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { children: "Width:" }),
|
|
2375
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("span", { className: "font-mono", children: [
|
|
1779
2376
|
strokeWidth,
|
|
1780
2377
|
"px"
|
|
1781
2378
|
] })
|
|
1782
2379
|
] }),
|
|
1783
|
-
/* @__PURE__ */ (0,
|
|
2380
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1784
2381
|
"input",
|
|
1785
2382
|
{
|
|
1786
2383
|
type: "range",
|
|
@@ -1793,10 +2390,10 @@ var ImageEditor = ({
|
|
|
1793
2390
|
)
|
|
1794
2391
|
] })
|
|
1795
2392
|
] }),
|
|
1796
|
-
/* @__PURE__ */ (0,
|
|
1797
|
-
/* @__PURE__ */ (0,
|
|
1798
|
-
/* @__PURE__ */ (0,
|
|
1799
|
-
/* @__PURE__ */ (0,
|
|
2393
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "h-px bg-slate-800 my-1" }),
|
|
2394
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Add Text" }),
|
|
2395
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "flex gap-2", children: [
|
|
2396
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1800
2397
|
"input",
|
|
1801
2398
|
{
|
|
1802
2399
|
type: "text",
|
|
@@ -1807,7 +2404,7 @@ var ImageEditor = ({
|
|
|
1807
2404
|
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"
|
|
1808
2405
|
}
|
|
1809
2406
|
),
|
|
1810
|
-
/* @__PURE__ */ (0,
|
|
2407
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1811
2408
|
"button",
|
|
1812
2409
|
{
|
|
1813
2410
|
type: "button",
|
|
@@ -1817,14 +2414,14 @@ var ImageEditor = ({
|
|
|
1817
2414
|
}
|
|
1818
2415
|
)
|
|
1819
2416
|
] }),
|
|
1820
|
-
/* @__PURE__ */ (0,
|
|
1821
|
-
/* @__PURE__ */ (0,
|
|
1822
|
-
/* @__PURE__ */ (0,
|
|
2417
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "flex items-center justify-between text-slate-400", children: [
|
|
2418
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { children: "Font Size:" }),
|
|
2419
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("span", { className: "font-mono", children: [
|
|
1823
2420
|
fontSize,
|
|
1824
2421
|
"px"
|
|
1825
2422
|
] })
|
|
1826
2423
|
] }),
|
|
1827
|
-
/* @__PURE__ */ (0,
|
|
2424
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1828
2425
|
"input",
|
|
1829
2426
|
{
|
|
1830
2427
|
type: "range",
|
|
@@ -1835,14 +2432,14 @@ var ImageEditor = ({
|
|
|
1835
2432
|
className: "w-full accent-indigo-500"
|
|
1836
2433
|
}
|
|
1837
2434
|
),
|
|
1838
|
-
/* @__PURE__ */ (0,
|
|
1839
|
-
/* @__PURE__ */ (0,
|
|
1840
|
-
/* @__PURE__ */ (0,
|
|
2435
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "h-px bg-slate-800 my-1" }),
|
|
2436
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "flex items-center justify-between", children: [
|
|
2437
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("span", { className: "text-slate-400", children: [
|
|
1841
2438
|
"Layers (",
|
|
1842
2439
|
state.annotations.length,
|
|
1843
2440
|
")"
|
|
1844
2441
|
] }),
|
|
1845
|
-
state.selectedAnnotationId && /* @__PURE__ */ (0,
|
|
2442
|
+
state.selectedAnnotationId && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1846
2443
|
"button",
|
|
1847
2444
|
{
|
|
1848
2445
|
type: "button",
|
|
@@ -1851,7 +2448,7 @@ var ImageEditor = ({
|
|
|
1851
2448
|
children: "Delete Selected"
|
|
1852
2449
|
}
|
|
1853
2450
|
),
|
|
1854
|
-
state.annotations.length > 0 && /* @__PURE__ */ (0,
|
|
2451
|
+
state.annotations.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1855
2452
|
"button",
|
|
1856
2453
|
{
|
|
1857
2454
|
type: "button",
|
|
@@ -1862,10 +2459,10 @@ var ImageEditor = ({
|
|
|
1862
2459
|
)
|
|
1863
2460
|
] })
|
|
1864
2461
|
] }),
|
|
1865
|
-
activeTab === "depth" && /* @__PURE__ */ (0,
|
|
1866
|
-
/* @__PURE__ */ (0,
|
|
1867
|
-
/* @__PURE__ */ (0,
|
|
1868
|
-
/* @__PURE__ */ (0,
|
|
2462
|
+
activeTab === "depth" && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "flex flex-col gap-3 text-xs", children: [
|
|
2463
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "flex items-center justify-between", children: [
|
|
2464
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Depth Masking" }),
|
|
2465
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1869
2466
|
"button",
|
|
1870
2467
|
{
|
|
1871
2468
|
type: "button",
|
|
@@ -1875,8 +2472,8 @@ var ImageEditor = ({
|
|
|
1875
2472
|
}
|
|
1876
2473
|
)
|
|
1877
2474
|
] }),
|
|
1878
|
-
/* @__PURE__ */ (0,
|
|
1879
|
-
/* @__PURE__ */ (0,
|
|
2475
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("label", { className: "flex items-center gap-2 cursor-pointer text-slate-300", children: [
|
|
2476
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1880
2477
|
"input",
|
|
1881
2478
|
{
|
|
1882
2479
|
type: "checkbox",
|
|
@@ -1885,12 +2482,12 @@ var ImageEditor = ({
|
|
|
1885
2482
|
className: "rounded accent-indigo-500"
|
|
1886
2483
|
}
|
|
1887
2484
|
),
|
|
1888
|
-
/* @__PURE__ */ (0,
|
|
2485
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { children: "Enable Depth / Alpha Mask" })
|
|
1889
2486
|
] }),
|
|
1890
|
-
state.depthMask.enabled && /* @__PURE__ */ (0,
|
|
1891
|
-
/* @__PURE__ */ (0,
|
|
1892
|
-
/* @__PURE__ */ (0,
|
|
1893
|
-
/* @__PURE__ */ (0,
|
|
2487
|
+
state.depthMask.enabled && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_jsx_runtime9.Fragment, { children: [
|
|
2488
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { children: [
|
|
2489
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("label", { className: "text-slate-400 block mb-1", children: "Mask Image URL:" }),
|
|
2490
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1894
2491
|
"input",
|
|
1895
2492
|
{
|
|
1896
2493
|
type: "text",
|
|
@@ -1901,8 +2498,8 @@ var ImageEditor = ({
|
|
|
1901
2498
|
}
|
|
1902
2499
|
)
|
|
1903
2500
|
] }),
|
|
1904
|
-
/* @__PURE__ */ (0,
|
|
1905
|
-
/* @__PURE__ */ (0,
|
|
2501
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "flex gap-2", children: [
|
|
2502
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1906
2503
|
"button",
|
|
1907
2504
|
{
|
|
1908
2505
|
type: "button",
|
|
@@ -1911,7 +2508,7 @@ var ImageEditor = ({
|
|
|
1911
2508
|
children: "Threshold Mode"
|
|
1912
2509
|
}
|
|
1913
2510
|
),
|
|
1914
|
-
/* @__PURE__ */ (0,
|
|
2511
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1915
2512
|
"button",
|
|
1916
2513
|
{
|
|
1917
2514
|
type: "button",
|
|
@@ -1921,12 +2518,12 @@ var ImageEditor = ({
|
|
|
1921
2518
|
}
|
|
1922
2519
|
)
|
|
1923
2520
|
] }),
|
|
1924
|
-
/* @__PURE__ */ (0,
|
|
1925
|
-
/* @__PURE__ */ (0,
|
|
1926
|
-
/* @__PURE__ */ (0,
|
|
1927
|
-
/* @__PURE__ */ (0,
|
|
2521
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { children: [
|
|
2522
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "flex justify-between text-slate-400 mb-1", children: [
|
|
2523
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { children: "Depth Start (Lower):" }),
|
|
2524
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "font-mono", children: state.depthMask.depthRange[0] })
|
|
1928
2525
|
] }),
|
|
1929
|
-
/* @__PURE__ */ (0,
|
|
2526
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1930
2527
|
"input",
|
|
1931
2528
|
{
|
|
1932
2529
|
type: "range",
|
|
@@ -1940,12 +2537,12 @@ var ImageEditor = ({
|
|
|
1940
2537
|
}
|
|
1941
2538
|
)
|
|
1942
2539
|
] }),
|
|
1943
|
-
/* @__PURE__ */ (0,
|
|
1944
|
-
/* @__PURE__ */ (0,
|
|
1945
|
-
/* @__PURE__ */ (0,
|
|
1946
|
-
/* @__PURE__ */ (0,
|
|
2540
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { children: [
|
|
2541
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "flex justify-between text-slate-400 mb-1", children: [
|
|
2542
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { children: "Depth Stop (Upper):" }),
|
|
2543
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "font-mono", children: state.depthMask.depthRange[1] })
|
|
1947
2544
|
] }),
|
|
1948
|
-
/* @__PURE__ */ (0,
|
|
2545
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1949
2546
|
"input",
|
|
1950
2547
|
{
|
|
1951
2548
|
type: "range",
|
|
@@ -1959,12 +2556,12 @@ var ImageEditor = ({
|
|
|
1959
2556
|
}
|
|
1960
2557
|
)
|
|
1961
2558
|
] }),
|
|
1962
|
-
/* @__PURE__ */ (0,
|
|
1963
|
-
/* @__PURE__ */ (0,
|
|
1964
|
-
/* @__PURE__ */ (0,
|
|
1965
|
-
/* @__PURE__ */ (0,
|
|
2559
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { children: [
|
|
2560
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "flex justify-between text-slate-400 mb-1", children: [
|
|
2561
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { children: "Softness:" }),
|
|
2562
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "font-mono", children: state.depthMask.softness })
|
|
1966
2563
|
] }),
|
|
1967
|
-
/* @__PURE__ */ (0,
|
|
2564
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1968
2565
|
"input",
|
|
1969
2566
|
{
|
|
1970
2567
|
type: "range",
|
|
@@ -1976,8 +2573,8 @@ var ImageEditor = ({
|
|
|
1976
2573
|
}
|
|
1977
2574
|
)
|
|
1978
2575
|
] }),
|
|
1979
|
-
/* @__PURE__ */ (0,
|
|
1980
|
-
/* @__PURE__ */ (0,
|
|
2576
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("label", { className: "flex items-center gap-2 cursor-pointer text-slate-300 mt-1", children: [
|
|
2577
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1981
2578
|
"input",
|
|
1982
2579
|
{
|
|
1983
2580
|
type: "checkbox",
|
|
@@ -1986,12 +2583,12 @@ var ImageEditor = ({
|
|
|
1986
2583
|
className: "rounded accent-indigo-500"
|
|
1987
2584
|
}
|
|
1988
2585
|
),
|
|
1989
|
-
/* @__PURE__ */ (0,
|
|
2586
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { children: "Invert Mask" })
|
|
1990
2587
|
] })
|
|
1991
2588
|
] })
|
|
1992
2589
|
] }),
|
|
1993
|
-
activeTab === "select" && /* @__PURE__ */ (0,
|
|
1994
|
-
/* @__PURE__ */ (0,
|
|
2590
|
+
activeTab === "select" && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "flex flex-col gap-4 text-xs", children: [
|
|
2591
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1995
2592
|
SelectionOptionsBar,
|
|
1996
2593
|
{
|
|
1997
2594
|
controller,
|
|
@@ -2001,51 +2598,68 @@ var ImageEditor = ({
|
|
|
2001
2598
|
quickMask: Boolean(state.quickMask)
|
|
2002
2599
|
}
|
|
2003
2600
|
),
|
|
2004
|
-
selectionError && /* @__PURE__ */ (0,
|
|
2005
|
-
/* @__PURE__ */ (0,
|
|
2601
|
+
selectionError && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("p", { role: "alert", className: "text-rose-400", children: selectionError }),
|
|
2602
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(RefineEdgePanel, { controller, selection: state.selection ?? null, layers: state.adjustments.layers })
|
|
2006
2603
|
] }),
|
|
2007
|
-
activeTab === "ai" && /* @__PURE__ */ (0,
|
|
2008
|
-
/* @__PURE__ */ (0,
|
|
2009
|
-
/* @__PURE__ */ (0,
|
|
2010
|
-
|
|
2011
|
-
|
|
2604
|
+
activeTab === "ai" && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "flex flex-col gap-4 text-xs", children: [
|
|
2605
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("label", { className: "flex items-center justify-between gap-2 text-slate-300", children: [
|
|
2606
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { children: "Results go to" }),
|
|
2607
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
|
|
2608
|
+
"select",
|
|
2609
|
+
{
|
|
2610
|
+
"aria-label": "AI result target",
|
|
2611
|
+
value: aiTarget,
|
|
2612
|
+
onChange: (e) => setAiTarget(e.target.value),
|
|
2613
|
+
className: "bg-slate-800 border border-slate-700 rounded px-1.5 py-1",
|
|
2614
|
+
children: [
|
|
2615
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("option", { value: "auto", children: controller.isMultiLayer ? "Auto (new layer)" : "Auto (replace image)" }),
|
|
2616
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("option", { value: "new-layer", children: "New layer / mask" }),
|
|
2617
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("option", { value: "replace", children: "Replace image" })
|
|
2618
|
+
]
|
|
2619
|
+
}
|
|
2620
|
+
)
|
|
2621
|
+
] }),
|
|
2622
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "p-3 rounded-lg bg-slate-950/60 border border-slate-800 flex flex-col gap-2.5", children: [
|
|
2623
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "flex items-center justify-between", children: [
|
|
2624
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px] flex items-center gap-1.5", children: [
|
|
2625
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("svg", { className: "w-3.5 h-3.5 text-indigo-400", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("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" }) }),
|
|
2012
2626
|
"Background Removal"
|
|
2013
2627
|
] }),
|
|
2014
|
-
isRemovingBg && /* @__PURE__ */ (0,
|
|
2628
|
+
isRemovingBg && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("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..." })
|
|
2015
2629
|
] }),
|
|
2016
|
-
/* @__PURE__ */ (0,
|
|
2017
|
-
/* @__PURE__ */ (0,
|
|
2630
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("p", { className: "text-slate-400 text-[11px] leading-relaxed", children: "Isolate foreground subject with AI alpha transparency segmentation." }),
|
|
2631
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
2018
2632
|
"button",
|
|
2019
2633
|
{
|
|
2020
2634
|
type: "button",
|
|
2021
2635
|
onClick: handleRemoveBackground,
|
|
2022
2636
|
disabled: isRemovingBg || isInpainting,
|
|
2023
2637
|
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",
|
|
2024
|
-
children: isRemovingBg ? /* @__PURE__ */ (0,
|
|
2025
|
-
/* @__PURE__ */ (0,
|
|
2638
|
+
children: isRemovingBg ? /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_jsx_runtime9.Fragment, { children: [
|
|
2639
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "w-3.5 h-3.5 border-2 border-white/30 border-t-white rounded-full animate-spin" }),
|
|
2026
2640
|
"Removing Background..."
|
|
2027
|
-
] }) : /* @__PURE__ */ (0,
|
|
2028
|
-
/* @__PURE__ */ (0,
|
|
2641
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_jsx_runtime9.Fragment, { children: [
|
|
2642
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("svg", { className: "w-3.5 h-3.5", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("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" }) }),
|
|
2029
2643
|
"Remove Background"
|
|
2030
2644
|
] })
|
|
2031
2645
|
}
|
|
2032
2646
|
)
|
|
2033
2647
|
] }),
|
|
2034
|
-
/* @__PURE__ */ (0,
|
|
2035
|
-
/* @__PURE__ */ (0,
|
|
2036
|
-
/* @__PURE__ */ (0,
|
|
2037
|
-
/* @__PURE__ */ (0,
|
|
2038
|
-
/* @__PURE__ */ (0,
|
|
2039
|
-
/* @__PURE__ */ (0,
|
|
2648
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "p-3 rounded-lg bg-slate-950/60 border border-slate-800 flex flex-col gap-2.5", children: [
|
|
2649
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "flex items-center justify-between", children: [
|
|
2650
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px] flex items-center gap-1.5", children: [
|
|
2651
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("svg", { className: "w-3.5 h-3.5 text-purple-400", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
|
|
2652
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("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" }),
|
|
2653
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("path", { d: "m14 7 3 3" })
|
|
2040
2654
|
] }),
|
|
2041
2655
|
"Generative Inpainting"
|
|
2042
2656
|
] }),
|
|
2043
|
-
isInpainting && /* @__PURE__ */ (0,
|
|
2657
|
+
isInpainting && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("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..." })
|
|
2044
2658
|
] }),
|
|
2045
|
-
/* @__PURE__ */ (0,
|
|
2046
|
-
/* @__PURE__ */ (0,
|
|
2047
|
-
/* @__PURE__ */ (0,
|
|
2048
|
-
/* @__PURE__ */ (0,
|
|
2659
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("p", { className: "text-slate-400 text-[11px] leading-relaxed", children: "Replace, modify, or fill selected regions using prompt-guided generative inpainting." }),
|
|
2660
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "flex flex-col gap-1", children: [
|
|
2661
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("label", { htmlFor: "ai-inpaint-prompt-react", className: "text-slate-300 text-[11px] font-medium", children: "Prompt" }),
|
|
2662
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
2049
2663
|
"textarea",
|
|
2050
2664
|
{
|
|
2051
2665
|
id: "ai-inpaint-prompt-react",
|
|
@@ -2057,10 +2671,10 @@ var ImageEditor = ({
|
|
|
2057
2671
|
}
|
|
2058
2672
|
)
|
|
2059
2673
|
] }),
|
|
2060
|
-
/* @__PURE__ */ (0,
|
|
2061
|
-
/* @__PURE__ */ (0,
|
|
2062
|
-
/* @__PURE__ */ (0,
|
|
2063
|
-
/* @__PURE__ */ (0,
|
|
2674
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "flex flex-col gap-1", children: [
|
|
2675
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "text-slate-300 text-[11px] font-medium", children: "Target Mask Region" }),
|
|
2676
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "grid grid-cols-2 gap-1.5", children: [
|
|
2677
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
|
|
2064
2678
|
"button",
|
|
2065
2679
|
{
|
|
2066
2680
|
type: "button",
|
|
@@ -2073,7 +2687,7 @@ var ImageEditor = ({
|
|
|
2073
2687
|
]
|
|
2074
2688
|
}
|
|
2075
2689
|
),
|
|
2076
|
-
/* @__PURE__ */ (0,
|
|
2690
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
|
|
2077
2691
|
"button",
|
|
2078
2692
|
{
|
|
2079
2693
|
type: "button",
|
|
@@ -2088,25 +2702,25 @@ var ImageEditor = ({
|
|
|
2088
2702
|
)
|
|
2089
2703
|
] })
|
|
2090
2704
|
] }),
|
|
2091
|
-
/* @__PURE__ */ (0,
|
|
2705
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
2092
2706
|
"button",
|
|
2093
2707
|
{
|
|
2094
2708
|
type: "button",
|
|
2095
2709
|
onClick: handleInpaint,
|
|
2096
2710
|
disabled: isInpainting || isRemovingBg || !inpaintPrompt.trim(),
|
|
2097
2711
|
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",
|
|
2098
|
-
children: isInpainting ? /* @__PURE__ */ (0,
|
|
2099
|
-
/* @__PURE__ */ (0,
|
|
2712
|
+
children: isInpainting ? /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_jsx_runtime9.Fragment, { children: [
|
|
2713
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "w-3.5 h-3.5 border-2 border-white/30 border-t-white rounded-full animate-spin" }),
|
|
2100
2714
|
"Generating Inpaint..."
|
|
2101
|
-
] }) : /* @__PURE__ */ (0,
|
|
2102
|
-
/* @__PURE__ */ (0,
|
|
2715
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_jsx_runtime9.Fragment, { children: [
|
|
2716
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("svg", { className: "w-3.5 h-3.5", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("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" }) }),
|
|
2103
2717
|
"Apply Inpainting"
|
|
2104
2718
|
] })
|
|
2105
2719
|
}
|
|
2106
2720
|
)
|
|
2107
2721
|
] }),
|
|
2108
|
-
aiError && /* @__PURE__ */ (0,
|
|
2109
|
-
aiSuccess && /* @__PURE__ */ (0,
|
|
2722
|
+
aiError && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "p-2.5 rounded-lg bg-rose-950/60 border border-rose-800 text-rose-300 text-[11px]", children: aiError }),
|
|
2723
|
+
aiSuccess && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "p-2.5 rounded-lg bg-emerald-950/60 border border-emerald-800 text-emerald-300 text-[11px]", children: aiSuccess })
|
|
2110
2724
|
] })
|
|
2111
2725
|
] })
|
|
2112
2726
|
] })
|
|
@@ -2117,10 +2731,12 @@ var ImageEditor = ({
|
|
|
2117
2731
|
// Annotate the CommonJS export names for ESM import in node:
|
|
2118
2732
|
0 && (module.exports = {
|
|
2119
2733
|
AdjustmentLayersPanel,
|
|
2734
|
+
BrushOptionsBar,
|
|
2120
2735
|
ControlSlider,
|
|
2121
2736
|
CurvesEditor,
|
|
2122
2737
|
Histogram,
|
|
2123
2738
|
ImageEditor,
|
|
2739
|
+
LayersPanel,
|
|
2124
2740
|
RefineEdgePanel,
|
|
2125
2741
|
SelectionOptionsBar,
|
|
2126
2742
|
useImageEditor,
|