@pixerate/editor-react 0.58.0 → 0.59.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-SYTV33TW.js} +810 -254
- package/dist/chunk-SYTV33TW.js.map +1 -0
- package/dist/image-editor/index.cjs +822 -275
- package/dist/image-editor/index.cjs.map +1 -1
- package/dist/image-editor/index.d.cts +26 -1
- package/dist/image-editor/index.d.ts +26 -1
- package/dist/image-editor/index.js +5 -1
- package/dist/index.cjs +1251 -704
- 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
|
@@ -159,7 +159,7 @@ function useImageEditor(options = {}) {
|
|
|
159
159
|
}
|
|
160
160
|
|
|
161
161
|
// src/image-editor/ImageEditor.tsx
|
|
162
|
-
import
|
|
162
|
+
import React8, { useRef as useRef4, useEffect as useEffect6, useState as useState9, useCallback as useCallback2 } from "react";
|
|
163
163
|
import {
|
|
164
164
|
canvasToImagePoint,
|
|
165
165
|
hitTestCrop,
|
|
@@ -171,7 +171,10 @@ import {
|
|
|
171
171
|
IMAGE_ADJUSTMENT_CONTROLS,
|
|
172
172
|
readAdjustmentControls,
|
|
173
173
|
DEFAULT_SELECTION_TOOL_OPTIONS,
|
|
174
|
-
SelectionGesture
|
|
174
|
+
SelectionGesture,
|
|
175
|
+
PaintGesture,
|
|
176
|
+
handlePaintShortcut,
|
|
177
|
+
pointerPressure
|
|
175
178
|
} from "@pixerate/editor/image-editor";
|
|
176
179
|
|
|
177
180
|
// src/image-editor/ControlSlider.tsx
|
|
@@ -622,15 +625,471 @@ var AdjustmentLayersPanel = ({ controller, layers: layersProp, histogram }) => {
|
|
|
622
625
|
] });
|
|
623
626
|
};
|
|
624
627
|
|
|
628
|
+
// src/image-editor/LayersPanel.tsx
|
|
629
|
+
import { useEffect as useEffect4, useRef as useRef3, useState as useState5 } from "react";
|
|
630
|
+
import {
|
|
631
|
+
BLEND_MODES as BLEND_MODES2,
|
|
632
|
+
dropPlacement,
|
|
633
|
+
dropZone,
|
|
634
|
+
layerBadge,
|
|
635
|
+
layerMenu,
|
|
636
|
+
layerPanelRows,
|
|
637
|
+
runLayerMenuAction
|
|
638
|
+
} from "@pixerate/editor/image-editor";
|
|
639
|
+
import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
640
|
+
var iconButton = "px-1.5 py-0.5 rounded text-[10px] bg-slate-800 text-slate-300 hover:text-white disabled:opacity-40";
|
|
641
|
+
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"}`;
|
|
642
|
+
var Thumbnail = ({
|
|
643
|
+
pixels,
|
|
644
|
+
size,
|
|
645
|
+
label,
|
|
646
|
+
onClick,
|
|
647
|
+
selected,
|
|
648
|
+
testId
|
|
649
|
+
}) => {
|
|
650
|
+
const ref = useRef3(null);
|
|
651
|
+
useEffect4(() => {
|
|
652
|
+
const canvas = ref.current;
|
|
653
|
+
const ctx = canvas?.getContext("2d");
|
|
654
|
+
if (!canvas || !ctx || !pixels || typeof ctx.createImageData !== "function") return;
|
|
655
|
+
canvas.width = pixels.width;
|
|
656
|
+
canvas.height = pixels.height;
|
|
657
|
+
const img = ctx.createImageData(pixels.width, pixels.height);
|
|
658
|
+
img.data.set(pixels.data);
|
|
659
|
+
ctx.putImageData(img, 0, 0);
|
|
660
|
+
}, [pixels]);
|
|
661
|
+
return /* @__PURE__ */ jsx5(
|
|
662
|
+
"canvas",
|
|
663
|
+
{
|
|
664
|
+
ref,
|
|
665
|
+
role: onClick ? "button" : "img",
|
|
666
|
+
"aria-label": label,
|
|
667
|
+
"data-testid": testId,
|
|
668
|
+
onClick,
|
|
669
|
+
style: { width: size, height: size },
|
|
670
|
+
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"}`
|
|
671
|
+
}
|
|
672
|
+
);
|
|
673
|
+
};
|
|
674
|
+
var LayersPanel = ({ controller, thumbnailSize = 32, className }) => {
|
|
675
|
+
const [, setTick] = useState5(0);
|
|
676
|
+
useEffect4(() => controller.subscribe(() => setTick((n) => n + 1)), [controller]);
|
|
677
|
+
const [error, setError] = useState5(null);
|
|
678
|
+
const [renaming, setRenaming] = useState5(null);
|
|
679
|
+
const [menu, setMenu] = useState5(null);
|
|
680
|
+
const [drag, setDrag] = useState5(null);
|
|
681
|
+
const commitNext = useRef3(true);
|
|
682
|
+
const state = controller.getState();
|
|
683
|
+
const rows = layerPanelRows(state.layers, state.activeLayerId);
|
|
684
|
+
const active = controller.getActiveLayer();
|
|
685
|
+
const attempt = (fn) => {
|
|
686
|
+
try {
|
|
687
|
+
setError(null);
|
|
688
|
+
fn();
|
|
689
|
+
} catch (err) {
|
|
690
|
+
setError(err instanceof Error ? err.message : String(err));
|
|
691
|
+
}
|
|
692
|
+
};
|
|
693
|
+
const run = async (action) => {
|
|
694
|
+
setMenu(null);
|
|
695
|
+
setError(await runLayerMenuAction(controller, action));
|
|
696
|
+
};
|
|
697
|
+
const prop = (key, value) => attempt(() => {
|
|
698
|
+
controller.setLayerProperties(active.id, { [key]: value }, commitNext.current);
|
|
699
|
+
commitNext.current = false;
|
|
700
|
+
});
|
|
701
|
+
const sliderEvents = {
|
|
702
|
+
onPointerDown: () => commitNext.current = true,
|
|
703
|
+
onPointerUp: () => commitNext.current = true,
|
|
704
|
+
onKeyDown: () => commitNext.current = true
|
|
705
|
+
};
|
|
706
|
+
const onDrop = (e, row) => {
|
|
707
|
+
e.preventDefault();
|
|
708
|
+
const id = e.dataTransfer.getData("text/x-pixerate-layer") || drag;
|
|
709
|
+
setDrag(null);
|
|
710
|
+
if (!id) return;
|
|
711
|
+
const rect = e.currentTarget.getBoundingClientRect();
|
|
712
|
+
const fraction = rect.height ? (e.clientY - rect.top) / rect.height : 0.5;
|
|
713
|
+
const placement = dropPlacement(state.layers, id, row.layer.id, dropZone(fraction, row.layer.type === "group"));
|
|
714
|
+
if (placement) attempt(() => controller.moveLayer(id, placement));
|
|
715
|
+
};
|
|
716
|
+
const thumb = (layer) => layer.type === "pixel" ? controller.getLayerThumbnail(layer.id, { maxSide: 64 }) : null;
|
|
717
|
+
const maskThumb = (layer) => layer.mask && !layer.mask.source ? controller.getLayerThumbnail(layer.id, { maxSide: 64, mask: true }) : null;
|
|
718
|
+
const locked = (layer) => Boolean(layer.locks?.all || layer.locks?.pixels || layer.locks?.position || layer.locks?.transparency);
|
|
719
|
+
return /* @__PURE__ */ jsxs5("div", { className: `flex flex-col gap-2 text-xs text-slate-200 ${className ?? ""}`, "data-testid": "layers-panel", onClick: () => menu && setMenu(null), children: [
|
|
720
|
+
/* @__PURE__ */ jsxs5("div", { className: "flex flex-wrap items-center gap-1", role: "toolbar", "aria-label": "Layer actions", children: [
|
|
721
|
+
/* @__PURE__ */ jsx5("button", { type: "button", className: iconButton, onClick: () => run("new-layer"), children: "New Layer" }),
|
|
722
|
+
/* @__PURE__ */ jsx5("button", { type: "button", className: iconButton, onClick: () => run("new-group"), children: "New Group" }),
|
|
723
|
+
/* @__PURE__ */ jsx5("button", { type: "button", className: iconButton, disabled: !active, onClick: () => run("duplicate"), children: "Duplicate" }),
|
|
724
|
+
/* @__PURE__ */ jsx5("button", { type: "button", className: iconButton, disabled: !active, onClick: () => run("add-mask"), children: "Add Mask" }),
|
|
725
|
+
/* @__PURE__ */ jsx5("button", { type: "button", className: iconButton, disabled: !active, onClick: () => run("delete"), children: "Delete" }),
|
|
726
|
+
/* @__PURE__ */ jsx5(
|
|
727
|
+
"button",
|
|
728
|
+
{
|
|
729
|
+
type: "button",
|
|
730
|
+
className: iconButton,
|
|
731
|
+
"aria-label": "Layer menu",
|
|
732
|
+
"aria-haspopup": "menu",
|
|
733
|
+
onClick: (e) => {
|
|
734
|
+
e.stopPropagation();
|
|
735
|
+
const r = e.currentTarget.getBoundingClientRect();
|
|
736
|
+
setMenu(menu ? null : { x: r.left, y: r.bottom });
|
|
737
|
+
},
|
|
738
|
+
children: "\u22EF"
|
|
739
|
+
}
|
|
740
|
+
)
|
|
741
|
+
] }),
|
|
742
|
+
active && /* @__PURE__ */ jsxs5("div", { className: "grid grid-cols-2 gap-x-2 gap-y-1 items-center", "data-testid": "layer-properties", children: [
|
|
743
|
+
/* @__PURE__ */ jsxs5("label", { className: "flex flex-col gap-0.5", children: [
|
|
744
|
+
/* @__PURE__ */ jsxs5("span", { className: "text-slate-400", children: [
|
|
745
|
+
"Opacity ",
|
|
746
|
+
Math.round(active.opacity * 100),
|
|
747
|
+
"%"
|
|
748
|
+
] }),
|
|
749
|
+
/* @__PURE__ */ jsx5(
|
|
750
|
+
"input",
|
|
751
|
+
{
|
|
752
|
+
type: "range",
|
|
753
|
+
min: 0,
|
|
754
|
+
max: 100,
|
|
755
|
+
"aria-label": "Layer opacity",
|
|
756
|
+
value: Math.round(active.opacity * 100),
|
|
757
|
+
...sliderEvents,
|
|
758
|
+
onChange: (e) => prop("opacity", Number(e.target.value) / 100),
|
|
759
|
+
className: "accent-indigo-500"
|
|
760
|
+
}
|
|
761
|
+
)
|
|
762
|
+
] }),
|
|
763
|
+
/* @__PURE__ */ jsxs5("label", { className: "flex flex-col gap-0.5", children: [
|
|
764
|
+
/* @__PURE__ */ jsxs5("span", { className: "text-slate-400", children: [
|
|
765
|
+
"Fill ",
|
|
766
|
+
Math.round(((active.type === "pixel" ? active.fillOpacity : void 0) ?? 1) * 100),
|
|
767
|
+
"%"
|
|
768
|
+
] }),
|
|
769
|
+
/* @__PURE__ */ jsx5(
|
|
770
|
+
"input",
|
|
771
|
+
{
|
|
772
|
+
type: "range",
|
|
773
|
+
min: 0,
|
|
774
|
+
max: 100,
|
|
775
|
+
"aria-label": "Layer fill",
|
|
776
|
+
disabled: active.type !== "pixel",
|
|
777
|
+
value: Math.round(((active.type === "pixel" ? active.fillOpacity : void 0) ?? 1) * 100),
|
|
778
|
+
...sliderEvents,
|
|
779
|
+
onChange: (e) => prop("fillOpacity", Number(e.target.value) / 100),
|
|
780
|
+
className: "accent-indigo-500 disabled:opacity-40"
|
|
781
|
+
}
|
|
782
|
+
)
|
|
783
|
+
] }),
|
|
784
|
+
/* @__PURE__ */ jsxs5(
|
|
785
|
+
"select",
|
|
786
|
+
{
|
|
787
|
+
"aria-label": "Layer blend mode",
|
|
788
|
+
className: "bg-slate-800 border border-slate-700 rounded px-1.5 py-1",
|
|
789
|
+
value: active.blend,
|
|
790
|
+
onChange: (e) => attempt(() => controller.setLayerProperties(active.id, { blend: e.target.value })),
|
|
791
|
+
children: [
|
|
792
|
+
active.type === "group" && /* @__PURE__ */ jsx5("option", { value: "pass-through", children: "Pass Through" }),
|
|
793
|
+
BLEND_MODES2.map((m) => /* @__PURE__ */ jsx5("option", { value: m.id, children: m.label }, m.id))
|
|
794
|
+
]
|
|
795
|
+
}
|
|
796
|
+
),
|
|
797
|
+
/* @__PURE__ */ jsxs5("label", { className: "flex items-center gap-1.5 text-slate-300", children: [
|
|
798
|
+
/* @__PURE__ */ jsx5(
|
|
799
|
+
"input",
|
|
800
|
+
{
|
|
801
|
+
type: "checkbox",
|
|
802
|
+
"aria-label": "Clip to layer below",
|
|
803
|
+
checked: Boolean(active.clipped),
|
|
804
|
+
onChange: (e) => attempt(() => controller.setLayerProperties(active.id, { clipped: e.target.checked })),
|
|
805
|
+
className: "accent-indigo-500"
|
|
806
|
+
}
|
|
807
|
+
),
|
|
808
|
+
"Clip to below"
|
|
809
|
+
] }),
|
|
810
|
+
/* @__PURE__ */ jsx5("div", { className: "col-span-2 flex flex-wrap gap-2 text-slate-400", role: "group", "aria-label": "Layer locks", children: ["pixels", "transparency", "position", "all"].map((k) => /* @__PURE__ */ jsxs5("label", { className: "flex items-center gap-1", children: [
|
|
811
|
+
/* @__PURE__ */ jsx5(
|
|
812
|
+
"input",
|
|
813
|
+
{
|
|
814
|
+
type: "checkbox",
|
|
815
|
+
"aria-label": `Lock ${k}`,
|
|
816
|
+
checked: Boolean(active.locks?.[k]),
|
|
817
|
+
onChange: (e) => attempt(() => controller.setLayerProperties(active.id, { locks: { ...active.locks, [k]: e.target.checked } })),
|
|
818
|
+
className: "accent-indigo-500"
|
|
819
|
+
}
|
|
820
|
+
),
|
|
821
|
+
k
|
|
822
|
+
] }, k)) })
|
|
823
|
+
] }),
|
|
824
|
+
/* @__PURE__ */ jsx5("ul", { className: "flex flex-col gap-0.5", role: "tree", "aria-label": "Layers", children: rows.map((row) => {
|
|
825
|
+
const { layer } = row;
|
|
826
|
+
return /* @__PURE__ */ jsxs5(
|
|
827
|
+
"li",
|
|
828
|
+
{
|
|
829
|
+
role: "treeitem",
|
|
830
|
+
"aria-selected": row.active,
|
|
831
|
+
"aria-level": row.depth + 1,
|
|
832
|
+
"data-layer-id": layer.id,
|
|
833
|
+
draggable: !layer.locks?.all,
|
|
834
|
+
onDragStart: (e) => {
|
|
835
|
+
e.dataTransfer.setData("text/x-pixerate-layer", layer.id);
|
|
836
|
+
setDrag(layer.id);
|
|
837
|
+
},
|
|
838
|
+
onDragOver: (e) => e.preventDefault(),
|
|
839
|
+
onDrop: (e) => onDrop(e, row),
|
|
840
|
+
tabIndex: 0,
|
|
841
|
+
onClick: () => attempt(() => controller.setActiveLayer(layer.id, "pixels")),
|
|
842
|
+
onKeyDown: (e) => {
|
|
843
|
+
if (e.target === e.currentTarget && (e.key === "Enter" || e.key === " ")) {
|
|
844
|
+
e.preventDefault();
|
|
845
|
+
attempt(() => controller.setActiveLayer(layer.id, "pixels"));
|
|
846
|
+
}
|
|
847
|
+
},
|
|
848
|
+
onContextMenu: (e) => {
|
|
849
|
+
e.preventDefault();
|
|
850
|
+
controller.setActiveLayer(layer.id, state.activeLayerId === layer.id ? state.activeTarget : "pixels");
|
|
851
|
+
setMenu({ x: e.clientX, y: e.clientY });
|
|
852
|
+
},
|
|
853
|
+
className: rowClass(row.active),
|
|
854
|
+
style: { paddingLeft: 6 + row.depth * 14 },
|
|
855
|
+
children: [
|
|
856
|
+
/* @__PURE__ */ jsx5(
|
|
857
|
+
"button",
|
|
858
|
+
{
|
|
859
|
+
type: "button",
|
|
860
|
+
"aria-label": layer.visible ? `Hide ${row.label}` : `Show ${row.label}`,
|
|
861
|
+
"aria-pressed": layer.visible,
|
|
862
|
+
className: "w-4 text-center text-slate-300",
|
|
863
|
+
onClick: (e) => {
|
|
864
|
+
e.stopPropagation();
|
|
865
|
+
attempt(() => controller.setLayerProperties(layer.id, { visible: !layer.visible }));
|
|
866
|
+
},
|
|
867
|
+
children: layer.visible ? "\u25CF" : "\u25CB"
|
|
868
|
+
}
|
|
869
|
+
),
|
|
870
|
+
layer.type === "group" && /* @__PURE__ */ jsx5(
|
|
871
|
+
"button",
|
|
872
|
+
{
|
|
873
|
+
type: "button",
|
|
874
|
+
"aria-label": layer.collapsed ? `Expand ${row.label}` : `Collapse ${row.label}`,
|
|
875
|
+
className: "w-3 text-slate-400",
|
|
876
|
+
onClick: (e) => {
|
|
877
|
+
e.stopPropagation();
|
|
878
|
+
attempt(() => controller.setLayerProperties(layer.id, { collapsed: !layer.collapsed }));
|
|
879
|
+
},
|
|
880
|
+
children: layer.collapsed ? "\u25B8" : "\u25BE"
|
|
881
|
+
}
|
|
882
|
+
),
|
|
883
|
+
row.clipped && /* @__PURE__ */ jsx5("span", { className: "text-slate-500", "aria-label": "Clipped", children: "\u21B3" }),
|
|
884
|
+
layer.type === "pixel" ? /* @__PURE__ */ jsx5(
|
|
885
|
+
Thumbnail,
|
|
886
|
+
{
|
|
887
|
+
pixels: thumb(layer),
|
|
888
|
+
size: thumbnailSize,
|
|
889
|
+
label: `${row.label} pixels`,
|
|
890
|
+
selected: row.active && state.activeTarget === "pixels" && Boolean(layer.mask)
|
|
891
|
+
}
|
|
892
|
+
) : /* @__PURE__ */ jsx5(
|
|
893
|
+
"span",
|
|
894
|
+
{
|
|
895
|
+
className: "shrink-0 grid place-items-center rounded bg-slate-800 text-[9px] text-slate-400 ring-1 ring-slate-700",
|
|
896
|
+
style: { width: thumbnailSize, height: thumbnailSize },
|
|
897
|
+
children: layerBadge(layer)
|
|
898
|
+
}
|
|
899
|
+
),
|
|
900
|
+
layer.mask && /* @__PURE__ */ jsx5(
|
|
901
|
+
Thumbnail,
|
|
902
|
+
{
|
|
903
|
+
pixels: maskThumb(layer),
|
|
904
|
+
size: thumbnailSize,
|
|
905
|
+
label: `${row.label} mask${layer.mask.enabled === false ? " (disabled)" : ""}`,
|
|
906
|
+
testId: "layer-mask-thumbnail",
|
|
907
|
+
selected: row.active && state.activeTarget === "mask",
|
|
908
|
+
onClick: (e) => {
|
|
909
|
+
e.stopPropagation();
|
|
910
|
+
if (e.shiftKey) attempt(() => controller.setLayerMaskProps(layer.id, { enabled: layer.mask.enabled === false }));
|
|
911
|
+
else attempt(() => controller.setActiveLayer(layer.id, "mask"));
|
|
912
|
+
}
|
|
913
|
+
}
|
|
914
|
+
),
|
|
915
|
+
renaming === layer.id ? /* @__PURE__ */ jsx5(
|
|
916
|
+
"input",
|
|
917
|
+
{
|
|
918
|
+
autoFocus: true,
|
|
919
|
+
"aria-label": "Layer name",
|
|
920
|
+
defaultValue: layer.name || row.label,
|
|
921
|
+
className: "flex-1 min-w-0 bg-slate-900 border border-slate-600 rounded px-1",
|
|
922
|
+
onClick: (e) => e.stopPropagation(),
|
|
923
|
+
onBlur: (e) => {
|
|
924
|
+
setRenaming(null);
|
|
925
|
+
if (e.target.value.trim() && e.target.value !== layer.name) attempt(() => controller.setLayerProperties(layer.id, { name: e.target.value }));
|
|
926
|
+
},
|
|
927
|
+
onKeyDown: (e) => {
|
|
928
|
+
if (e.key === "Enter") e.target.blur();
|
|
929
|
+
if (e.key === "Escape") setRenaming(null);
|
|
930
|
+
}
|
|
931
|
+
}
|
|
932
|
+
) : /* @__PURE__ */ jsx5(
|
|
933
|
+
"span",
|
|
934
|
+
{
|
|
935
|
+
className: `flex-1 min-w-0 truncate ${layer.visible ? "" : "text-slate-500"}`,
|
|
936
|
+
onDoubleClick: (e) => {
|
|
937
|
+
e.stopPropagation();
|
|
938
|
+
setRenaming(layer.id);
|
|
939
|
+
},
|
|
940
|
+
children: row.label
|
|
941
|
+
}
|
|
942
|
+
),
|
|
943
|
+
locked(layer) && /* @__PURE__ */ jsx5("span", { "aria-label": "Locked", className: "text-slate-400", children: "\u{1F512}" }),
|
|
944
|
+
layer.blend !== "normal" && layer.blend !== "pass-through" && /* @__PURE__ */ jsx5("span", { className: "text-[9px] text-slate-500", children: layer.blend })
|
|
945
|
+
]
|
|
946
|
+
},
|
|
947
|
+
layer.id
|
|
948
|
+
);
|
|
949
|
+
}) }),
|
|
950
|
+
error && /* @__PURE__ */ jsx5("p", { role: "alert", className: "text-rose-400", children: error }),
|
|
951
|
+
menu && /* @__PURE__ */ jsx5(
|
|
952
|
+
"div",
|
|
953
|
+
{
|
|
954
|
+
role: "menu",
|
|
955
|
+
"aria-label": "Layer menu",
|
|
956
|
+
className: "fixed z-50 min-w-44 rounded border border-slate-700 bg-slate-900 py-1 shadow-xl",
|
|
957
|
+
style: { left: menu.x, top: menu.y },
|
|
958
|
+
onClick: (e) => e.stopPropagation(),
|
|
959
|
+
onKeyDown: (e) => e.key === "Escape" && setMenu(null),
|
|
960
|
+
children: layerMenu(controller).map((item) => /* @__PURE__ */ jsx5(
|
|
961
|
+
"button",
|
|
962
|
+
{
|
|
963
|
+
type: "button",
|
|
964
|
+
role: "menuitem",
|
|
965
|
+
disabled: !item.enabled,
|
|
966
|
+
className: "block w-full px-3 py-1 text-left hover:bg-slate-800 disabled:opacity-40",
|
|
967
|
+
onClick: () => run(item.action),
|
|
968
|
+
children: item.label
|
|
969
|
+
},
|
|
970
|
+
item.action
|
|
971
|
+
))
|
|
972
|
+
}
|
|
973
|
+
)
|
|
974
|
+
] });
|
|
975
|
+
};
|
|
976
|
+
|
|
977
|
+
// src/image-editor/BrushOptionsBar.tsx
|
|
978
|
+
import { useEffect as useEffect5, useState as useState6 } from "react";
|
|
979
|
+
import { BRUSH_OPTION_CONTROLS, PAINT_TOOLS } from "@pixerate/editor/image-editor";
|
|
980
|
+
import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
981
|
+
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"}`;
|
|
982
|
+
var BrushOptionsBar = ({ controller, className }) => {
|
|
983
|
+
const [, setTick] = useState6(0);
|
|
984
|
+
useEffect5(() => controller.subscribe(() => setTick((n) => n + 1)), [controller]);
|
|
985
|
+
const [error, setError] = useState6(null);
|
|
986
|
+
const state = controller.getState();
|
|
987
|
+
const { brush, foreground, background } = state.paint;
|
|
988
|
+
const attempt = (fn) => {
|
|
989
|
+
try {
|
|
990
|
+
setError(null);
|
|
991
|
+
fn();
|
|
992
|
+
} catch (err) {
|
|
993
|
+
setError(err instanceof Error ? err.message : String(err));
|
|
994
|
+
}
|
|
995
|
+
};
|
|
996
|
+
return /* @__PURE__ */ jsxs6("div", { className: `flex flex-col gap-2 text-xs text-slate-200 ${className ?? ""}`, "data-testid": "brush-options", children: [
|
|
997
|
+
/* @__PURE__ */ jsx6("div", { className: "flex gap-1", role: "toolbar", "aria-label": "Paint tools", children: PAINT_TOOLS.map((t) => /* @__PURE__ */ jsx6(
|
|
998
|
+
"button",
|
|
999
|
+
{
|
|
1000
|
+
type: "button",
|
|
1001
|
+
"aria-pressed": state.activeTool === t.id,
|
|
1002
|
+
title: `${t.label} (${t.shortcut})`,
|
|
1003
|
+
className: chip(state.activeTool === t.id),
|
|
1004
|
+
onClick: () => controller.setTool(t.id),
|
|
1005
|
+
children: t.label
|
|
1006
|
+
},
|
|
1007
|
+
t.id
|
|
1008
|
+
)) }),
|
|
1009
|
+
/* @__PURE__ */ jsxs6("div", { className: "flex items-center gap-2", role: "group", "aria-label": "Colours", children: [
|
|
1010
|
+
/* @__PURE__ */ jsxs6("label", { className: "flex items-center gap-1", children: [
|
|
1011
|
+
/* @__PURE__ */ jsx6(
|
|
1012
|
+
"input",
|
|
1013
|
+
{
|
|
1014
|
+
type: "color",
|
|
1015
|
+
"aria-label": "Foreground colour",
|
|
1016
|
+
value: foreground,
|
|
1017
|
+
onChange: (e) => controller.setPaintOptions({ foreground: e.target.value }),
|
|
1018
|
+
className: "h-6 w-8 rounded border border-slate-700 bg-transparent"
|
|
1019
|
+
}
|
|
1020
|
+
),
|
|
1021
|
+
/* @__PURE__ */ jsx6("span", { className: "text-slate-400", children: "FG" })
|
|
1022
|
+
] }),
|
|
1023
|
+
/* @__PURE__ */ jsxs6("label", { className: "flex items-center gap-1", children: [
|
|
1024
|
+
/* @__PURE__ */ jsx6(
|
|
1025
|
+
"input",
|
|
1026
|
+
{
|
|
1027
|
+
type: "color",
|
|
1028
|
+
"aria-label": "Background colour",
|
|
1029
|
+
value: background,
|
|
1030
|
+
onChange: (e) => controller.setPaintOptions({ background: e.target.value }),
|
|
1031
|
+
className: "h-6 w-8 rounded border border-slate-700 bg-transparent"
|
|
1032
|
+
}
|
|
1033
|
+
),
|
|
1034
|
+
/* @__PURE__ */ jsx6("span", { className: "text-slate-400", children: "BG" })
|
|
1035
|
+
] }),
|
|
1036
|
+
/* @__PURE__ */ jsx6("button", { type: "button", title: "Swap colours (X)", className: chip(false), onClick: () => controller.swapPaintColors(), children: "Swap" }),
|
|
1037
|
+
/* @__PURE__ */ jsx6("button", { type: "button", title: "Default colours (D)", className: chip(false), onClick: () => controller.resetPaintColors(), children: "Default" })
|
|
1038
|
+
] }),
|
|
1039
|
+
BRUSH_OPTION_CONTROLS.map((spec) => /* @__PURE__ */ jsx6(
|
|
1040
|
+
ControlSlider,
|
|
1041
|
+
{
|
|
1042
|
+
spec,
|
|
1043
|
+
value: brush[spec.id],
|
|
1044
|
+
onChange: (v) => controller.setPaintOptions({ brush: { [spec.id]: v } })
|
|
1045
|
+
},
|
|
1046
|
+
spec.id
|
|
1047
|
+
)),
|
|
1048
|
+
/* @__PURE__ */ jsxs6("div", { className: "flex flex-wrap gap-3 text-slate-300", role: "group", "aria-label": "Pressure", children: [
|
|
1049
|
+
/* @__PURE__ */ jsxs6("label", { className: "flex items-center gap-1.5", children: [
|
|
1050
|
+
/* @__PURE__ */ jsx6(
|
|
1051
|
+
"input",
|
|
1052
|
+
{
|
|
1053
|
+
type: "checkbox",
|
|
1054
|
+
"aria-label": "Pressure controls size",
|
|
1055
|
+
checked: brush.pressureSize,
|
|
1056
|
+
onChange: (e) => controller.setPaintOptions({ brush: { pressureSize: e.target.checked } }),
|
|
1057
|
+
className: "accent-indigo-500"
|
|
1058
|
+
}
|
|
1059
|
+
),
|
|
1060
|
+
"Pressure size"
|
|
1061
|
+
] }),
|
|
1062
|
+
/* @__PURE__ */ jsxs6("label", { className: "flex items-center gap-1.5", children: [
|
|
1063
|
+
/* @__PURE__ */ jsx6(
|
|
1064
|
+
"input",
|
|
1065
|
+
{
|
|
1066
|
+
type: "checkbox",
|
|
1067
|
+
"aria-label": "Pressure controls opacity",
|
|
1068
|
+
checked: brush.pressureOpacity,
|
|
1069
|
+
onChange: (e) => controller.setPaintOptions({ brush: { pressureOpacity: e.target.checked } }),
|
|
1070
|
+
className: "accent-indigo-500"
|
|
1071
|
+
}
|
|
1072
|
+
),
|
|
1073
|
+
"Pressure opacity"
|
|
1074
|
+
] })
|
|
1075
|
+
] }),
|
|
1076
|
+
/* @__PURE__ */ jsxs6("div", { className: "flex gap-1", children: [
|
|
1077
|
+
/* @__PURE__ */ jsx6("button", { type: "button", className: chip(false), onClick: () => attempt(() => controller.fillPixels()), children: "Fill" }),
|
|
1078
|
+
/* @__PURE__ */ jsx6("button", { type: "button", className: chip(false), onClick: () => attempt(() => controller.clearPixels()), children: "Clear" })
|
|
1079
|
+
] }),
|
|
1080
|
+
error && /* @__PURE__ */ jsx6("p", { role: "alert", className: "text-rose-400", children: error })
|
|
1081
|
+
] });
|
|
1082
|
+
};
|
|
1083
|
+
|
|
625
1084
|
// src/image-editor/SelectionOptionsBar.tsx
|
|
626
|
-
import { useState as
|
|
1085
|
+
import { useState as useState7 } from "react";
|
|
627
1086
|
import {
|
|
628
1087
|
SELECTION_MODES,
|
|
629
1088
|
SELECTION_MODE_LABELS,
|
|
630
1089
|
SELECTION_TOOLS,
|
|
631
1090
|
describeSelection
|
|
632
1091
|
} from "@pixerate/editor/image-editor";
|
|
633
|
-
import { jsx as
|
|
1092
|
+
import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
634
1093
|
var MODIFY_OPS = [
|
|
635
1094
|
{ op: "grow", label: "Grow" },
|
|
636
1095
|
{ op: "shrink", label: "Shrink" },
|
|
@@ -647,20 +1106,20 @@ var SelectionOptionsBar = ({
|
|
|
647
1106
|
quickMask,
|
|
648
1107
|
className = ""
|
|
649
1108
|
}) => {
|
|
650
|
-
const [radius, setRadius] =
|
|
1109
|
+
const [radius, setRadius] = useState7(2);
|
|
651
1110
|
const set = (patch) => onOptionsChange({ ...options, ...patch });
|
|
652
1111
|
const sel = selection === void 0 ? controller.getSelection() : selection;
|
|
653
1112
|
const qm = quickMask ?? Boolean(controller.getState().quickMask);
|
|
654
|
-
return /* @__PURE__ */
|
|
655
|
-
/* @__PURE__ */
|
|
656
|
-
/* @__PURE__ */
|
|
657
|
-
options.tool === "wand" && /* @__PURE__ */
|
|
658
|
-
/* @__PURE__ */
|
|
659
|
-
/* @__PURE__ */
|
|
660
|
-
/* @__PURE__ */
|
|
661
|
-
/* @__PURE__ */
|
|
1113
|
+
return /* @__PURE__ */ jsxs7("div", { "data-testid": "selection-options-bar", className: `flex flex-col gap-3 text-xs ${className}`, children: [
|
|
1114
|
+
/* @__PURE__ */ jsx7("div", { role: "group", "aria-label": "Selection tool", className: "flex flex-wrap gap-1", children: SELECTION_TOOLS.map((t) => /* @__PURE__ */ jsx7("button", { type: "button", "aria-pressed": options.tool === t.id, className: btn(options.tool === t.id), onClick: () => set({ tool: t.id }), children: t.label }, t.id)) }),
|
|
1115
|
+
/* @__PURE__ */ jsx7("div", { role: "group", "aria-label": "Selection mode", className: "flex flex-wrap gap-1", children: SELECTION_MODES.map((m) => /* @__PURE__ */ jsx7("button", { type: "button", "aria-pressed": options.mode === m, className: btn(options.mode === m), onClick: () => set({ mode: m }), children: SELECTION_MODE_LABELS[m] }, m)) }),
|
|
1116
|
+
options.tool === "wand" && /* @__PURE__ */ jsxs7("div", { className: "flex flex-col gap-2", children: [
|
|
1117
|
+
/* @__PURE__ */ jsxs7("label", { className: "flex flex-col gap-1", children: [
|
|
1118
|
+
/* @__PURE__ */ jsxs7("span", { className: "flex justify-between text-slate-400", children: [
|
|
1119
|
+
/* @__PURE__ */ jsx7("span", { children: "Tolerance" }),
|
|
1120
|
+
/* @__PURE__ */ jsx7("span", { className: "font-mono", children: options.tolerance })
|
|
662
1121
|
] }),
|
|
663
|
-
/* @__PURE__ */
|
|
1122
|
+
/* @__PURE__ */ jsx7(
|
|
664
1123
|
"input",
|
|
665
1124
|
{
|
|
666
1125
|
type: "range",
|
|
@@ -674,29 +1133,29 @@ var SelectionOptionsBar = ({
|
|
|
674
1133
|
}
|
|
675
1134
|
)
|
|
676
1135
|
] }),
|
|
677
|
-
/* @__PURE__ */
|
|
678
|
-
/* @__PURE__ */
|
|
1136
|
+
/* @__PURE__ */ jsxs7("label", { className: "flex items-center gap-2 text-slate-300", children: [
|
|
1137
|
+
/* @__PURE__ */ jsx7("input", { type: "checkbox", checked: options.contiguous, onChange: (e) => set({ contiguous: e.target.checked }) }),
|
|
679
1138
|
"Contiguous"
|
|
680
1139
|
] }),
|
|
681
|
-
/* @__PURE__ */
|
|
682
|
-
/* @__PURE__ */
|
|
1140
|
+
/* @__PURE__ */ jsxs7("label", { className: "flex items-center gap-2 text-slate-300", children: [
|
|
1141
|
+
/* @__PURE__ */ jsx7("input", { type: "checkbox", checked: options.sampleMerged, onChange: (e) => set({ sampleMerged: e.target.checked }) }),
|
|
683
1142
|
"Sample adjusted image"
|
|
684
1143
|
] })
|
|
685
1144
|
] }),
|
|
686
|
-
/* @__PURE__ */
|
|
687
|
-
/* @__PURE__ */
|
|
1145
|
+
/* @__PURE__ */ jsxs7("label", { className: "flex items-center gap-2 text-slate-300", children: [
|
|
1146
|
+
/* @__PURE__ */ jsx7("input", { type: "checkbox", checked: options.antiAlias, onChange: (e) => set({ antiAlias: e.target.checked }) }),
|
|
688
1147
|
"Anti-alias"
|
|
689
1148
|
] }),
|
|
690
|
-
/* @__PURE__ */
|
|
691
|
-
/* @__PURE__ */
|
|
692
|
-
/* @__PURE__ */
|
|
693
|
-
/* @__PURE__ */
|
|
694
|
-
/* @__PURE__ */
|
|
1149
|
+
/* @__PURE__ */ jsxs7("div", { className: "flex flex-wrap gap-1", children: [
|
|
1150
|
+
/* @__PURE__ */ jsx7("button", { type: "button", className: btn(false), onClick: () => controller.selectAll(), children: "Select All" }),
|
|
1151
|
+
/* @__PURE__ */ jsx7("button", { type: "button", className: btn(false), disabled: !sel, onClick: () => controller.deselect(), children: "Deselect" }),
|
|
1152
|
+
/* @__PURE__ */ jsx7("button", { type: "button", className: btn(false), onClick: () => controller.invertSelection(), children: "Inverse" }),
|
|
1153
|
+
/* @__PURE__ */ jsx7("button", { type: "button", "aria-pressed": qm, className: btn(qm), onClick: () => controller.setQuickMask(!qm), children: "Quick Mask" })
|
|
695
1154
|
] }),
|
|
696
|
-
/* @__PURE__ */
|
|
697
|
-
/* @__PURE__ */
|
|
1155
|
+
/* @__PURE__ */ jsxs7("div", { className: "flex flex-wrap items-center gap-1", children: [
|
|
1156
|
+
/* @__PURE__ */ jsxs7("label", { className: "flex items-center gap-1 text-slate-400", children: [
|
|
698
1157
|
"Radius",
|
|
699
|
-
/* @__PURE__ */
|
|
1158
|
+
/* @__PURE__ */ jsx7(
|
|
700
1159
|
"input",
|
|
701
1160
|
{
|
|
702
1161
|
type: "number",
|
|
@@ -710,29 +1169,29 @@ var SelectionOptionsBar = ({
|
|
|
710
1169
|
}
|
|
711
1170
|
)
|
|
712
1171
|
] }),
|
|
713
|
-
MODIFY_OPS.map(({ op, label }) => /* @__PURE__ */
|
|
1172
|
+
MODIFY_OPS.map(({ op, label }) => /* @__PURE__ */ jsx7("button", { type: "button", className: btn(false), disabled: !sel, onClick: () => controller.modifySelection(op, radius), children: label }, op))
|
|
714
1173
|
] }),
|
|
715
|
-
/* @__PURE__ */
|
|
1174
|
+
/* @__PURE__ */ jsx7("p", { "data-testid": "selection-status", className: "text-slate-400", children: describeSelection(sel) })
|
|
716
1175
|
] });
|
|
717
1176
|
};
|
|
718
1177
|
|
|
719
1178
|
// src/image-editor/RefineEdgePanel.tsx
|
|
720
|
-
import { useState as
|
|
1179
|
+
import { useState as useState8 } from "react";
|
|
721
1180
|
import {
|
|
722
1181
|
ADJUSTMENT_KINDS as ADJUSTMENT_KINDS2,
|
|
723
1182
|
REFINE_EDGE_CONTROLS,
|
|
724
1183
|
adjustmentKindLabel as adjustmentKindLabel2,
|
|
725
1184
|
layerMaskFromSelection
|
|
726
1185
|
} from "@pixerate/editor/image-editor";
|
|
727
|
-
import { Fragment, jsx as
|
|
1186
|
+
import { Fragment, jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
728
1187
|
var DEFAULT_PARAMS = { radius: 8, smartRadius: false, smooth: 0, feather: 0, contrast: 0, shiftEdge: 0 };
|
|
729
1188
|
var button = "px-2 py-1 rounded-md border border-slate-800 bg-slate-950 text-[11px] text-slate-200 hover:text-white disabled:opacity-40";
|
|
730
1189
|
var RefineEdgePanel = ({ controller, selection, layers, className = "" }) => {
|
|
731
|
-
const [params, setParams] =
|
|
732
|
-
const [busy, setBusy] =
|
|
733
|
-
const [error, setError] =
|
|
734
|
-
const [layerId, setLayerId] =
|
|
735
|
-
const [newKind, setNewKind] =
|
|
1190
|
+
const [params, setParams] = useState8(DEFAULT_PARAMS);
|
|
1191
|
+
const [busy, setBusy] = useState8(false);
|
|
1192
|
+
const [error, setError] = useState8(null);
|
|
1193
|
+
const [layerId, setLayerId] = useState8("");
|
|
1194
|
+
const [newKind, setNewKind] = useState8("hueSaturation");
|
|
736
1195
|
const sel = selection === void 0 ? controller.getSelection() : selection;
|
|
737
1196
|
const list = layers ?? controller.getAdjustmentLayers();
|
|
738
1197
|
const target = list.find((l) => l.id === layerId) ?? list[list.length - 1];
|
|
@@ -747,17 +1206,17 @@ var RefineEdgePanel = ({ controller, selection, layers, className = "" }) => {
|
|
|
747
1206
|
setBusy(false);
|
|
748
1207
|
}
|
|
749
1208
|
};
|
|
750
|
-
return /* @__PURE__ */
|
|
751
|
-
/* @__PURE__ */
|
|
752
|
-
REFINE_EDGE_CONTROLS.map((c) => /* @__PURE__ */
|
|
753
|
-
/* @__PURE__ */
|
|
754
|
-
/* @__PURE__ */
|
|
755
|
-
/* @__PURE__ */
|
|
1209
|
+
return /* @__PURE__ */ jsxs8("div", { "data-testid": "refine-edge-panel", className: `flex flex-col gap-3 text-xs ${className}`, children: [
|
|
1210
|
+
/* @__PURE__ */ jsx8("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Refine Edge" }),
|
|
1211
|
+
REFINE_EDGE_CONTROLS.map((c) => /* @__PURE__ */ jsxs8("label", { className: "flex flex-col gap-1", children: [
|
|
1212
|
+
/* @__PURE__ */ jsxs8("span", { className: "flex justify-between text-slate-400", children: [
|
|
1213
|
+
/* @__PURE__ */ jsx8("span", { children: c.label }),
|
|
1214
|
+
/* @__PURE__ */ jsxs8("span", { className: "font-mono", children: [
|
|
756
1215
|
params[c.id],
|
|
757
1216
|
c.unit
|
|
758
1217
|
] })
|
|
759
1218
|
] }),
|
|
760
|
-
/* @__PURE__ */
|
|
1219
|
+
/* @__PURE__ */ jsx8(
|
|
761
1220
|
"input",
|
|
762
1221
|
{
|
|
763
1222
|
type: "range",
|
|
@@ -771,28 +1230,28 @@ var RefineEdgePanel = ({ controller, selection, layers, className = "" }) => {
|
|
|
771
1230
|
}
|
|
772
1231
|
)
|
|
773
1232
|
] }, c.id)),
|
|
774
|
-
/* @__PURE__ */
|
|
775
|
-
/* @__PURE__ */
|
|
1233
|
+
/* @__PURE__ */ jsxs8("label", { className: "flex items-center gap-2 text-slate-300", children: [
|
|
1234
|
+
/* @__PURE__ */ jsx8("input", { type: "checkbox", checked: params.smartRadius, onChange: (e) => setParams({ ...params, smartRadius: e.target.checked }) }),
|
|
776
1235
|
"Smart Radius"
|
|
777
1236
|
] }),
|
|
778
|
-
/* @__PURE__ */
|
|
779
|
-
/* @__PURE__ */
|
|
780
|
-
/* @__PURE__ */
|
|
1237
|
+
/* @__PURE__ */ jsxs8("div", { className: "flex gap-1", children: [
|
|
1238
|
+
/* @__PURE__ */ jsx8("button", { type: "button", className: button, disabled: !sel || busy, onClick: refine, children: "Refine Edge" }),
|
|
1239
|
+
/* @__PURE__ */ jsx8("button", { type: "button", className: button, onClick: () => setParams(DEFAULT_PARAMS), children: "Reset" })
|
|
781
1240
|
] }),
|
|
782
|
-
error && /* @__PURE__ */
|
|
783
|
-
/* @__PURE__ */
|
|
784
|
-
/* @__PURE__ */
|
|
785
|
-
/* @__PURE__ */
|
|
1241
|
+
error && /* @__PURE__ */ jsx8("p", { role: "alert", className: "text-rose-400", children: error }),
|
|
1242
|
+
/* @__PURE__ */ jsx8("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px] mt-2", children: "Layer Mask" }),
|
|
1243
|
+
/* @__PURE__ */ jsxs8("div", { className: "flex gap-1 items-center", children: [
|
|
1244
|
+
/* @__PURE__ */ jsx8(
|
|
786
1245
|
"select",
|
|
787
1246
|
{
|
|
788
1247
|
"aria-label": "New masked layer kind",
|
|
789
1248
|
value: newKind,
|
|
790
1249
|
onChange: (e) => setNewKind(e.target.value),
|
|
791
1250
|
className: "flex-1 bg-slate-950 border border-slate-800 rounded px-1 py-1 text-slate-200",
|
|
792
|
-
children: ADJUSTMENT_KINDS2.map((k) => /* @__PURE__ */
|
|
1251
|
+
children: ADJUSTMENT_KINDS2.map((k) => /* @__PURE__ */ jsx8("option", { value: k.kind, children: k.label }, k.kind))
|
|
793
1252
|
}
|
|
794
1253
|
),
|
|
795
|
-
/* @__PURE__ */
|
|
1254
|
+
/* @__PURE__ */ jsx8(
|
|
796
1255
|
"button",
|
|
797
1256
|
{
|
|
798
1257
|
type: "button",
|
|
@@ -803,35 +1262,35 @@ var RefineEdgePanel = ({ controller, selection, layers, className = "" }) => {
|
|
|
803
1262
|
}
|
|
804
1263
|
)
|
|
805
1264
|
] }),
|
|
806
|
-
list.length > 0 && target && /* @__PURE__ */
|
|
807
|
-
/* @__PURE__ */
|
|
1265
|
+
list.length > 0 && target && /* @__PURE__ */ jsxs8("div", { className: "flex flex-col gap-2", children: [
|
|
1266
|
+
/* @__PURE__ */ jsx8(
|
|
808
1267
|
"select",
|
|
809
1268
|
{
|
|
810
1269
|
"aria-label": "Mask layer",
|
|
811
1270
|
value: target.id,
|
|
812
1271
|
onChange: (e) => setLayerId(e.target.value),
|
|
813
1272
|
className: "bg-slate-950 border border-slate-800 rounded px-1 py-1 text-slate-200",
|
|
814
|
-
children: list.map((l) => /* @__PURE__ */
|
|
1273
|
+
children: list.map((l) => /* @__PURE__ */ jsxs8("option", { value: l.id, children: [
|
|
815
1274
|
l.name || adjustmentKindLabel2(l.kind),
|
|
816
1275
|
l.mask ? " (masked)" : ""
|
|
817
1276
|
] }, l.id))
|
|
818
1277
|
}
|
|
819
1278
|
),
|
|
820
|
-
/* @__PURE__ */
|
|
821
|
-
/* @__PURE__ */
|
|
822
|
-
/* @__PURE__ */
|
|
823
|
-
/* @__PURE__ */
|
|
1279
|
+
/* @__PURE__ */ jsxs8("div", { className: "flex flex-wrap gap-1", children: [
|
|
1280
|
+
/* @__PURE__ */ jsx8("button", { type: "button", className: button, disabled: !sel, onClick: () => controller.applySelectionAsLayerMask(target.id), children: "Mask with Selection" }),
|
|
1281
|
+
/* @__PURE__ */ jsx8("button", { type: "button", className: button, disabled: !target.mask, onClick: () => controller.selectionFromLayerMask(target.id), children: "Load as Selection" }),
|
|
1282
|
+
/* @__PURE__ */ jsx8("button", { type: "button", className: button, disabled: !target.mask, onClick: () => controller.setAdjustmentLayerMask(target.id, null), children: "Remove Mask" })
|
|
824
1283
|
] }),
|
|
825
|
-
target.mask && /* @__PURE__ */
|
|
826
|
-
/* @__PURE__ */
|
|
827
|
-
/* @__PURE__ */
|
|
828
|
-
/* @__PURE__ */
|
|
829
|
-
/* @__PURE__ */
|
|
1284
|
+
target.mask && /* @__PURE__ */ jsxs8(Fragment, { children: [
|
|
1285
|
+
/* @__PURE__ */ jsxs8("label", { className: "flex flex-col gap-1", children: [
|
|
1286
|
+
/* @__PURE__ */ jsxs8("span", { className: "flex justify-between text-slate-400", children: [
|
|
1287
|
+
/* @__PURE__ */ jsx8("span", { children: "Density" }),
|
|
1288
|
+
/* @__PURE__ */ jsxs8("span", { className: "font-mono", children: [
|
|
830
1289
|
Math.round((target.mask.density ?? 1) * 100),
|
|
831
1290
|
"%"
|
|
832
1291
|
] })
|
|
833
1292
|
] }),
|
|
834
|
-
/* @__PURE__ */
|
|
1293
|
+
/* @__PURE__ */ jsx8(
|
|
835
1294
|
"input",
|
|
836
1295
|
{
|
|
837
1296
|
type: "range",
|
|
@@ -845,15 +1304,15 @@ var RefineEdgePanel = ({ controller, selection, layers, className = "" }) => {
|
|
|
845
1304
|
}
|
|
846
1305
|
)
|
|
847
1306
|
] }),
|
|
848
|
-
/* @__PURE__ */
|
|
849
|
-
/* @__PURE__ */
|
|
850
|
-
/* @__PURE__ */
|
|
851
|
-
/* @__PURE__ */
|
|
1307
|
+
/* @__PURE__ */ jsxs8("label", { className: "flex flex-col gap-1", children: [
|
|
1308
|
+
/* @__PURE__ */ jsxs8("span", { className: "flex justify-between text-slate-400", children: [
|
|
1309
|
+
/* @__PURE__ */ jsx8("span", { children: "Mask Feather" }),
|
|
1310
|
+
/* @__PURE__ */ jsxs8("span", { className: "font-mono", children: [
|
|
852
1311
|
target.mask.feather ?? 0,
|
|
853
1312
|
"px"
|
|
854
1313
|
] })
|
|
855
1314
|
] }),
|
|
856
|
-
/* @__PURE__ */
|
|
1315
|
+
/* @__PURE__ */ jsx8(
|
|
857
1316
|
"input",
|
|
858
1317
|
{
|
|
859
1318
|
type: "range",
|
|
@@ -873,7 +1332,7 @@ var RefineEdgePanel = ({ controller, selection, layers, className = "" }) => {
|
|
|
873
1332
|
};
|
|
874
1333
|
|
|
875
1334
|
// src/image-editor/ImageEditor.tsx
|
|
876
|
-
import { Fragment as Fragment2, jsx as
|
|
1335
|
+
import { Fragment as Fragment2, jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
877
1336
|
var ImageEditor = ({
|
|
878
1337
|
editor: externalEditor,
|
|
879
1338
|
src,
|
|
@@ -889,25 +1348,33 @@ var ImageEditor = ({
|
|
|
889
1348
|
const editor = externalEditor || internalEditor;
|
|
890
1349
|
const { state, controller } = editor;
|
|
891
1350
|
const legacyProcess = normalizeProcessVersion(state.adjustments.process) < LATEST_PROCESS_VERSION;
|
|
892
|
-
const canvasRef =
|
|
893
|
-
const containerRef =
|
|
894
|
-
const [activeTab, setActiveTab] =
|
|
895
|
-
const [activeCropRatio, setActiveCropRatio] =
|
|
896
|
-
const [selectionOptions, setSelectionOptions] =
|
|
897
|
-
const selectionOptionsRef =
|
|
1351
|
+
const canvasRef = useRef4(null);
|
|
1352
|
+
const containerRef = useRef4(null);
|
|
1353
|
+
const [activeTab, setActiveTab] = useState9("crop");
|
|
1354
|
+
const [activeCropRatio, setActiveCropRatio] = useState9(null);
|
|
1355
|
+
const [selectionOptions, setSelectionOptions] = useState9(DEFAULT_SELECTION_TOOL_OPTIONS);
|
|
1356
|
+
const selectionOptionsRef = useRef4(selectionOptions);
|
|
898
1357
|
selectionOptionsRef.current = selectionOptions;
|
|
899
|
-
const selectionGestureRef =
|
|
1358
|
+
const selectionGestureRef = useRef4(null);
|
|
900
1359
|
if (!selectionGestureRef.current || selectionGestureRef.current.controller !== controller) {
|
|
901
1360
|
selectionGestureRef.current = new SelectionGesture(controller, () => selectionOptionsRef.current);
|
|
902
1361
|
}
|
|
903
|
-
const selectionDraftRef =
|
|
904
|
-
const [selectionError, setSelectionError] =
|
|
905
|
-
const
|
|
906
|
-
const
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
1362
|
+
const selectionDraftRef = useRef4(null);
|
|
1363
|
+
const [selectionError, setSelectionError] = useState9(null);
|
|
1364
|
+
const paintGestureRef = useRef4(null);
|
|
1365
|
+
const paintControllerRef = useRef4(controller);
|
|
1366
|
+
if (!paintGestureRef.current || paintControllerRef.current !== controller) {
|
|
1367
|
+
paintGestureRef.current = new PaintGesture(controller);
|
|
1368
|
+
paintControllerRef.current = controller;
|
|
1369
|
+
}
|
|
1370
|
+
const brushCursorRef = useRef4(null);
|
|
1371
|
+
const [paintError, setPaintError] = useState9(null);
|
|
1372
|
+
const [inpaintPrompt, setInpaintPrompt] = useState9("");
|
|
1373
|
+
const [inpaintMaskMode, setInpaintMaskMode] = useState9("annotations");
|
|
1374
|
+
const [isRemovingBg, setIsRemovingBg] = useState9(false);
|
|
1375
|
+
const [isInpainting, setIsInpainting] = useState9(false);
|
|
1376
|
+
const [aiError, setAiError] = useState9(null);
|
|
1377
|
+
const [aiSuccess, setAiSuccess] = useState9(null);
|
|
911
1378
|
const effectiveOnInpaint = onInpaint || aiHooks?.onInpaint;
|
|
912
1379
|
const effectiveOnRemoveBg = onRemoveBackground || aiHooks?.onRemoveBackground;
|
|
913
1380
|
const handleRemoveBackground = async () => {
|
|
@@ -969,17 +1436,17 @@ var ImageEditor = ({
|
|
|
969
1436
|
setIsInpainting(false);
|
|
970
1437
|
}
|
|
971
1438
|
};
|
|
972
|
-
const [activeColor, setActiveColor] =
|
|
973
|
-
const [strokeWidth, setStrokeWidth] =
|
|
974
|
-
const [fontSize, setFontSize] =
|
|
975
|
-
const [textInput, setTextInput] =
|
|
976
|
-
const draftAnnotationRef =
|
|
977
|
-
const draftCropRef =
|
|
978
|
-
const cropDragInfoRef =
|
|
979
|
-
const annotationDragInfoRef =
|
|
980
|
-
const isDrawingRef =
|
|
981
|
-
const currentPathRef =
|
|
982
|
-
const dragStartPointRef =
|
|
1439
|
+
const [activeColor, setActiveColor] = useState9("#ef4444");
|
|
1440
|
+
const [strokeWidth, setStrokeWidth] = useState9(3);
|
|
1441
|
+
const [fontSize, setFontSize] = useState9(24);
|
|
1442
|
+
const [textInput, setTextInput] = useState9("");
|
|
1443
|
+
const draftAnnotationRef = useRef4(null);
|
|
1444
|
+
const draftCropRef = useRef4(null);
|
|
1445
|
+
const cropDragInfoRef = useRef4(null);
|
|
1446
|
+
const annotationDragInfoRef = useRef4(null);
|
|
1447
|
+
const isDrawingRef = useRef4(false);
|
|
1448
|
+
const currentPathRef = useRef4([]);
|
|
1449
|
+
const dragStartPointRef = useRef4(null);
|
|
983
1450
|
const renderCanvas = useCallback2(
|
|
984
1451
|
async (draftAnnotation, draftCrop) => {
|
|
985
1452
|
const canvas = canvasRef.current;
|
|
@@ -995,12 +1462,13 @@ var ImageEditor = ({
|
|
|
995
1462
|
interactive: true,
|
|
996
1463
|
draftAnnotation: draftAnnotation !== void 0 ? draftAnnotation : draftAnnotationRef.current,
|
|
997
1464
|
draftCrop: draftCrop !== void 0 ? draftCrop : draftCropRef.current,
|
|
998
|
-
draftSelectionPath: selectionDraftRef.current
|
|
1465
|
+
draftSelectionPath: selectionDraftRef.current,
|
|
1466
|
+
brushCursor: activeTab === "paint" && brushCursorRef.current && controller.getState().activeTool !== "move" ? { ...brushCursorRef.current, diameter: controller.getState().paint.brush.size } : null
|
|
999
1467
|
});
|
|
1000
1468
|
},
|
|
1001
1469
|
[controller, activeTab]
|
|
1002
1470
|
);
|
|
1003
|
-
|
|
1471
|
+
useEffect6(() => {
|
|
1004
1472
|
const raf = typeof requestAnimationFrame !== "undefined" ? requestAnimationFrame : (cb) => setTimeout(cb, 0);
|
|
1005
1473
|
const caf = typeof cancelAnimationFrame !== "undefined" ? cancelAnimationFrame : (id) => clearTimeout(id);
|
|
1006
1474
|
const animId = raf(() => {
|
|
@@ -1008,12 +1476,12 @@ var ImageEditor = ({
|
|
|
1008
1476
|
});
|
|
1009
1477
|
return () => caf(animId);
|
|
1010
1478
|
}, [renderCanvas, state]);
|
|
1011
|
-
|
|
1479
|
+
useEffect6(() => {
|
|
1012
1480
|
if (activeTab === "crop" && editor.state.activeTool !== "crop") {
|
|
1013
1481
|
editor.setTool("crop");
|
|
1014
1482
|
}
|
|
1015
1483
|
}, [activeTab, editor]);
|
|
1016
|
-
|
|
1484
|
+
useEffect6(() => {
|
|
1017
1485
|
const container = containerRef.current;
|
|
1018
1486
|
if (!container) return;
|
|
1019
1487
|
if (typeof ResizeObserver !== "undefined") {
|
|
@@ -1039,6 +1507,53 @@ var ImageEditor = ({
|
|
|
1039
1507
|
y: (e.clientY - rect.top) * scaleY
|
|
1040
1508
|
};
|
|
1041
1509
|
};
|
|
1510
|
+
const paintSamples = (e) => {
|
|
1511
|
+
const canvas = canvasRef.current;
|
|
1512
|
+
const native = e.nativeEvent;
|
|
1513
|
+
const events = typeof native.getCoalescedEvents === "function" ? native.getCoalescedEvents() : [];
|
|
1514
|
+
const list = events.length ? events : [native];
|
|
1515
|
+
const rect = canvas.getBoundingClientRect();
|
|
1516
|
+
const sx = rect.width > 0 ? canvas.width / rect.width : 1;
|
|
1517
|
+
const sy = rect.height > 0 ? canvas.height / rect.height : 1;
|
|
1518
|
+
const s = controller.getState();
|
|
1519
|
+
return list.map((ev) => ({
|
|
1520
|
+
...canvasToImagePoint({ x: (ev.clientX - rect.left) * sx, y: (ev.clientY - rect.top) * sy }, s, canvas.width, canvas.height, false),
|
|
1521
|
+
pressure: pointerPressure(ev)
|
|
1522
|
+
}));
|
|
1523
|
+
};
|
|
1524
|
+
const handlePaintPointerDown = (e) => {
|
|
1525
|
+
if (activeTab !== "paint" || e.button !== 0) return;
|
|
1526
|
+
e.preventDefault();
|
|
1527
|
+
try {
|
|
1528
|
+
e.currentTarget.setPointerCapture?.(e.pointerId);
|
|
1529
|
+
} catch {
|
|
1530
|
+
}
|
|
1531
|
+
const [first] = paintSamples(e);
|
|
1532
|
+
setPaintError(paintGestureRef.current.down(first));
|
|
1533
|
+
};
|
|
1534
|
+
const handlePaintPointerMove = (e) => {
|
|
1535
|
+
if (activeTab !== "paint") return;
|
|
1536
|
+
const samples = paintSamples(e);
|
|
1537
|
+
brushCursorRef.current = samples[samples.length - 1] ?? null;
|
|
1538
|
+
if (paintGestureRef.current.active) paintGestureRef.current.moveTo(samples);
|
|
1539
|
+
else void renderCanvas();
|
|
1540
|
+
};
|
|
1541
|
+
const handlePaintPointerUp = () => {
|
|
1542
|
+
if (activeTab !== "paint") return;
|
|
1543
|
+
const g = paintGestureRef.current;
|
|
1544
|
+
if (g.active) {
|
|
1545
|
+
g.up();
|
|
1546
|
+
if (g.error) setPaintError(g.error);
|
|
1547
|
+
}
|
|
1548
|
+
};
|
|
1549
|
+
useEffect6(() => {
|
|
1550
|
+
if (activeTab !== "paint" || typeof window === "undefined") return;
|
|
1551
|
+
const onKey = (e) => {
|
|
1552
|
+
if (handlePaintShortcut(controller, e)) e.preventDefault();
|
|
1553
|
+
};
|
|
1554
|
+
window.addEventListener("keydown", onKey);
|
|
1555
|
+
return () => window.removeEventListener("keydown", onKey);
|
|
1556
|
+
}, [activeTab, controller]);
|
|
1042
1557
|
const handleMouseDown = (e) => {
|
|
1043
1558
|
const canvas = canvasRef.current;
|
|
1044
1559
|
if (!canvas) return;
|
|
@@ -1394,15 +1909,15 @@ var ImageEditor = ({
|
|
|
1394
1909
|
link.click();
|
|
1395
1910
|
}
|
|
1396
1911
|
};
|
|
1397
|
-
return /* @__PURE__ */
|
|
1912
|
+
return /* @__PURE__ */ jsxs9(
|
|
1398
1913
|
"div",
|
|
1399
1914
|
{
|
|
1400
1915
|
className: `flex flex-col bg-slate-900 border border-slate-800 rounded-xl overflow-hidden shadow-2xl text-slate-100 ${className}`,
|
|
1401
1916
|
style,
|
|
1402
1917
|
children: [
|
|
1403
|
-
/* @__PURE__ */
|
|
1404
|
-
/* @__PURE__ */
|
|
1405
|
-
/* @__PURE__ */
|
|
1918
|
+
/* @__PURE__ */ jsxs9("div", { className: "flex flex-wrap items-center justify-between px-4 py-3 bg-slate-950/80 border-b border-slate-800 gap-3", children: [
|
|
1919
|
+
/* @__PURE__ */ jsxs9("div", { className: "flex items-center gap-1 bg-slate-900 p-1 rounded-lg border border-slate-800 text-xs", children: [
|
|
1920
|
+
/* @__PURE__ */ jsx9(
|
|
1406
1921
|
"button",
|
|
1407
1922
|
{
|
|
1408
1923
|
type: "button",
|
|
@@ -1414,7 +1929,7 @@ var ImageEditor = ({
|
|
|
1414
1929
|
children: "Transform & Crop"
|
|
1415
1930
|
}
|
|
1416
1931
|
),
|
|
1417
|
-
/* @__PURE__ */
|
|
1932
|
+
/* @__PURE__ */ jsx9(
|
|
1418
1933
|
"button",
|
|
1419
1934
|
{
|
|
1420
1935
|
type: "button",
|
|
@@ -1426,7 +1941,31 @@ var ImageEditor = ({
|
|
|
1426
1941
|
children: "Adjustments"
|
|
1427
1942
|
}
|
|
1428
1943
|
),
|
|
1429
|
-
/* @__PURE__ */
|
|
1944
|
+
/* @__PURE__ */ jsx9(
|
|
1945
|
+
"button",
|
|
1946
|
+
{
|
|
1947
|
+
type: "button",
|
|
1948
|
+
onClick: () => {
|
|
1949
|
+
setActiveTab("layers");
|
|
1950
|
+
editor.setTool("select");
|
|
1951
|
+
},
|
|
1952
|
+
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"}`,
|
|
1953
|
+
children: "Layers"
|
|
1954
|
+
}
|
|
1955
|
+
),
|
|
1956
|
+
/* @__PURE__ */ jsx9(
|
|
1957
|
+
"button",
|
|
1958
|
+
{
|
|
1959
|
+
type: "button",
|
|
1960
|
+
onClick: () => {
|
|
1961
|
+
setActiveTab("paint");
|
|
1962
|
+
editor.setTool("brush");
|
|
1963
|
+
},
|
|
1964
|
+
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"}`,
|
|
1965
|
+
children: "Paint"
|
|
1966
|
+
}
|
|
1967
|
+
),
|
|
1968
|
+
/* @__PURE__ */ jsx9(
|
|
1430
1969
|
"button",
|
|
1431
1970
|
{
|
|
1432
1971
|
type: "button",
|
|
@@ -1438,7 +1977,7 @@ var ImageEditor = ({
|
|
|
1438
1977
|
children: "Select & Mask"
|
|
1439
1978
|
}
|
|
1440
1979
|
),
|
|
1441
|
-
/* @__PURE__ */
|
|
1980
|
+
/* @__PURE__ */ jsx9(
|
|
1442
1981
|
"button",
|
|
1443
1982
|
{
|
|
1444
1983
|
type: "button",
|
|
@@ -1450,7 +1989,7 @@ var ImageEditor = ({
|
|
|
1450
1989
|
children: "Annotations"
|
|
1451
1990
|
}
|
|
1452
1991
|
),
|
|
1453
|
-
/* @__PURE__ */
|
|
1992
|
+
/* @__PURE__ */ jsx9(
|
|
1454
1993
|
"button",
|
|
1455
1994
|
{
|
|
1456
1995
|
type: "button",
|
|
@@ -1462,7 +2001,7 @@ var ImageEditor = ({
|
|
|
1462
2001
|
children: "Depth Mask"
|
|
1463
2002
|
}
|
|
1464
2003
|
),
|
|
1465
|
-
/* @__PURE__ */
|
|
2004
|
+
/* @__PURE__ */ jsxs9(
|
|
1466
2005
|
"button",
|
|
1467
2006
|
{
|
|
1468
2007
|
type: "button",
|
|
@@ -1472,14 +2011,14 @@ var ImageEditor = ({
|
|
|
1472
2011
|
},
|
|
1473
2012
|
className: `px-3 py-1.5 rounded-md font-medium transition-colors flex items-center gap-1.5 ${activeTab === "ai" ? "bg-indigo-600 text-white shadow" : "text-slate-400 hover:text-slate-200"}`,
|
|
1474
2013
|
children: [
|
|
1475
|
-
/* @__PURE__ */
|
|
2014
|
+
/* @__PURE__ */ jsx9("svg", { className: "w-3.5 h-3.5", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: /* @__PURE__ */ jsx9("path", { d: "M12 2v4M12 18v4M4.93 4.93l2.83 2.83M16.24 16.24l2.83 2.83M2 12h4M18 12h4M4.93 19.07l2.83-2.83M16.24 7.76l2.83-2.83" }) }),
|
|
1476
2015
|
"AI Tools"
|
|
1477
2016
|
]
|
|
1478
2017
|
}
|
|
1479
2018
|
)
|
|
1480
2019
|
] }),
|
|
1481
|
-
/* @__PURE__ */
|
|
1482
|
-
/* @__PURE__ */
|
|
2020
|
+
/* @__PURE__ */ jsxs9("div", { className: "flex items-center gap-2 text-xs", children: [
|
|
2021
|
+
/* @__PURE__ */ jsx9(
|
|
1483
2022
|
"button",
|
|
1484
2023
|
{
|
|
1485
2024
|
type: "button",
|
|
@@ -1490,7 +2029,7 @@ var ImageEditor = ({
|
|
|
1490
2029
|
children: "\u21A9"
|
|
1491
2030
|
}
|
|
1492
2031
|
),
|
|
1493
|
-
/* @__PURE__ */
|
|
2032
|
+
/* @__PURE__ */ jsx9(
|
|
1494
2033
|
"button",
|
|
1495
2034
|
{
|
|
1496
2035
|
type: "button",
|
|
@@ -1501,7 +2040,7 @@ var ImageEditor = ({
|
|
|
1501
2040
|
children: "\u21AA"
|
|
1502
2041
|
}
|
|
1503
2042
|
),
|
|
1504
|
-
/* @__PURE__ */
|
|
2043
|
+
/* @__PURE__ */ jsx9(
|
|
1505
2044
|
"button",
|
|
1506
2045
|
{
|
|
1507
2046
|
type: "button",
|
|
@@ -1510,8 +2049,8 @@ var ImageEditor = ({
|
|
|
1510
2049
|
children: "Reset"
|
|
1511
2050
|
}
|
|
1512
2051
|
),
|
|
1513
|
-
/* @__PURE__ */
|
|
1514
|
-
/* @__PURE__ */
|
|
2052
|
+
/* @__PURE__ */ jsx9("div", { className: "h-4 w-px bg-slate-800 mx-1" }),
|
|
2053
|
+
/* @__PURE__ */ jsx9(
|
|
1515
2054
|
"button",
|
|
1516
2055
|
{
|
|
1517
2056
|
type: "button",
|
|
@@ -1521,11 +2060,11 @@ var ImageEditor = ({
|
|
|
1521
2060
|
children: "-"
|
|
1522
2061
|
}
|
|
1523
2062
|
),
|
|
1524
|
-
/* @__PURE__ */
|
|
2063
|
+
/* @__PURE__ */ jsxs9("span", { className: "font-mono text-slate-400 w-10 text-center", children: [
|
|
1525
2064
|
Math.round(state.zoom * 100),
|
|
1526
2065
|
"%"
|
|
1527
2066
|
] }),
|
|
1528
|
-
/* @__PURE__ */
|
|
2067
|
+
/* @__PURE__ */ jsx9(
|
|
1529
2068
|
"button",
|
|
1530
2069
|
{
|
|
1531
2070
|
type: "button",
|
|
@@ -1535,8 +2074,8 @@ var ImageEditor = ({
|
|
|
1535
2074
|
children: "+"
|
|
1536
2075
|
}
|
|
1537
2076
|
),
|
|
1538
|
-
/* @__PURE__ */
|
|
1539
|
-
/* @__PURE__ */
|
|
2077
|
+
/* @__PURE__ */ jsx9("div", { className: "h-4 w-px bg-slate-800 mx-1" }),
|
|
2078
|
+
/* @__PURE__ */ jsx9(
|
|
1540
2079
|
"button",
|
|
1541
2080
|
{
|
|
1542
2081
|
type: "button",
|
|
@@ -1547,7 +2086,7 @@ var ImageEditor = ({
|
|
|
1547
2086
|
children: "Hold Compare"
|
|
1548
2087
|
}
|
|
1549
2088
|
),
|
|
1550
|
-
/* @__PURE__ */
|
|
2089
|
+
/* @__PURE__ */ jsx9(
|
|
1551
2090
|
"button",
|
|
1552
2091
|
{
|
|
1553
2092
|
type: "button",
|
|
@@ -1558,13 +2097,13 @@ var ImageEditor = ({
|
|
|
1558
2097
|
)
|
|
1559
2098
|
] })
|
|
1560
2099
|
] }),
|
|
1561
|
-
/* @__PURE__ */
|
|
1562
|
-
/* @__PURE__ */
|
|
2100
|
+
/* @__PURE__ */ jsxs9("div", { className: "flex flex-col md:flex-row flex-1 min-h-[520px] md:h-[560px]", children: [
|
|
2101
|
+
/* @__PURE__ */ jsx9(
|
|
1563
2102
|
"div",
|
|
1564
2103
|
{
|
|
1565
2104
|
ref: containerRef,
|
|
1566
2105
|
className: "relative flex-1 h-[360px] md:h-full min-h-[320px] bg-slate-950 overflow-hidden select-none",
|
|
1567
|
-
children: /* @__PURE__ */
|
|
2106
|
+
children: /* @__PURE__ */ jsx9(
|
|
1568
2107
|
"canvas",
|
|
1569
2108
|
{
|
|
1570
2109
|
ref: canvasRef,
|
|
@@ -1572,16 +2111,21 @@ var ImageEditor = ({
|
|
|
1572
2111
|
onMouseMove: handleMouseMove,
|
|
1573
2112
|
onMouseUp: handleMouseUp,
|
|
1574
2113
|
onMouseLeave: handleMouseUp,
|
|
1575
|
-
|
|
2114
|
+
onPointerDown: handlePaintPointerDown,
|
|
2115
|
+
onPointerMove: handlePaintPointerMove,
|
|
2116
|
+
onPointerUp: handlePaintPointerUp,
|
|
2117
|
+
onPointerCancel: () => paintGestureRef.current?.cancel(),
|
|
2118
|
+
"data-testid": "image-editor-canvas",
|
|
2119
|
+
className: `absolute inset-0 w-full h-full block ${activeTab === "paint" ? "touch-none cursor-crosshair" : ""}`
|
|
1576
2120
|
}
|
|
1577
2121
|
)
|
|
1578
2122
|
}
|
|
1579
2123
|
),
|
|
1580
|
-
/* @__PURE__ */
|
|
1581
|
-
activeTab === "crop" && /* @__PURE__ */
|
|
1582
|
-
/* @__PURE__ */
|
|
1583
|
-
/* @__PURE__ */
|
|
1584
|
-
/* @__PURE__ */
|
|
2124
|
+
/* @__PURE__ */ jsxs9("div", { className: "w-full md:w-80 bg-slate-900/90 border-t md:border-t-0 md:border-l border-slate-800 p-4 overflow-y-auto h-auto md:h-full md:max-h-[560px]", children: [
|
|
2125
|
+
activeTab === "crop" && /* @__PURE__ */ jsxs9("div", { className: "flex flex-col gap-4 text-xs", children: [
|
|
2126
|
+
/* @__PURE__ */ jsx9("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Aspect Ratio Presets" }),
|
|
2127
|
+
/* @__PURE__ */ jsxs9("div", { className: "grid grid-cols-3 gap-2", children: [
|
|
2128
|
+
/* @__PURE__ */ jsx9(
|
|
1585
2129
|
"button",
|
|
1586
2130
|
{
|
|
1587
2131
|
type: "button",
|
|
@@ -1593,7 +2137,7 @@ var ImageEditor = ({
|
|
|
1593
2137
|
children: "Freeform"
|
|
1594
2138
|
}
|
|
1595
2139
|
),
|
|
1596
|
-
/* @__PURE__ */
|
|
2140
|
+
/* @__PURE__ */ jsx9(
|
|
1597
2141
|
"button",
|
|
1598
2142
|
{
|
|
1599
2143
|
type: "button",
|
|
@@ -1605,7 +2149,7 @@ var ImageEditor = ({
|
|
|
1605
2149
|
children: "1:1 Square"
|
|
1606
2150
|
}
|
|
1607
2151
|
),
|
|
1608
|
-
/* @__PURE__ */
|
|
2152
|
+
/* @__PURE__ */ jsx9(
|
|
1609
2153
|
"button",
|
|
1610
2154
|
{
|
|
1611
2155
|
type: "button",
|
|
@@ -1617,7 +2161,7 @@ var ImageEditor = ({
|
|
|
1617
2161
|
children: "4:3 Standard"
|
|
1618
2162
|
}
|
|
1619
2163
|
),
|
|
1620
|
-
/* @__PURE__ */
|
|
2164
|
+
/* @__PURE__ */ jsx9(
|
|
1621
2165
|
"button",
|
|
1622
2166
|
{
|
|
1623
2167
|
type: "button",
|
|
@@ -1629,7 +2173,7 @@ var ImageEditor = ({
|
|
|
1629
2173
|
children: "16:9 Wide"
|
|
1630
2174
|
}
|
|
1631
2175
|
),
|
|
1632
|
-
/* @__PURE__ */
|
|
2176
|
+
/* @__PURE__ */ jsx9(
|
|
1633
2177
|
"button",
|
|
1634
2178
|
{
|
|
1635
2179
|
type: "button",
|
|
@@ -1641,7 +2185,7 @@ var ImageEditor = ({
|
|
|
1641
2185
|
children: "3:2 Photo"
|
|
1642
2186
|
}
|
|
1643
2187
|
),
|
|
1644
|
-
/* @__PURE__ */
|
|
2188
|
+
/* @__PURE__ */ jsx9(
|
|
1645
2189
|
"button",
|
|
1646
2190
|
{
|
|
1647
2191
|
type: "button",
|
|
@@ -1654,10 +2198,10 @@ var ImageEditor = ({
|
|
|
1654
2198
|
}
|
|
1655
2199
|
)
|
|
1656
2200
|
] }),
|
|
1657
|
-
/* @__PURE__ */
|
|
1658
|
-
/* @__PURE__ */
|
|
1659
|
-
/* @__PURE__ */
|
|
1660
|
-
/* @__PURE__ */
|
|
2201
|
+
/* @__PURE__ */ jsx9("div", { className: "h-px bg-slate-800 my-1" }),
|
|
2202
|
+
/* @__PURE__ */ jsx9("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Orientation & Flips" }),
|
|
2203
|
+
/* @__PURE__ */ jsxs9("div", { className: "grid grid-cols-2 gap-2", children: [
|
|
2204
|
+
/* @__PURE__ */ jsx9(
|
|
1661
2205
|
"button",
|
|
1662
2206
|
{
|
|
1663
2207
|
type: "button",
|
|
@@ -1666,7 +2210,7 @@ var ImageEditor = ({
|
|
|
1666
2210
|
children: "Rotate -90\xB0"
|
|
1667
2211
|
}
|
|
1668
2212
|
),
|
|
1669
|
-
/* @__PURE__ */
|
|
2213
|
+
/* @__PURE__ */ jsx9(
|
|
1670
2214
|
"button",
|
|
1671
2215
|
{
|
|
1672
2216
|
type: "button",
|
|
@@ -1675,7 +2219,7 @@ var ImageEditor = ({
|
|
|
1675
2219
|
children: "Rotate +90\xB0"
|
|
1676
2220
|
}
|
|
1677
2221
|
),
|
|
1678
|
-
/* @__PURE__ */
|
|
2222
|
+
/* @__PURE__ */ jsx9(
|
|
1679
2223
|
"button",
|
|
1680
2224
|
{
|
|
1681
2225
|
type: "button",
|
|
@@ -1684,7 +2228,7 @@ var ImageEditor = ({
|
|
|
1684
2228
|
children: "Flip Horizontal"
|
|
1685
2229
|
}
|
|
1686
2230
|
),
|
|
1687
|
-
/* @__PURE__ */
|
|
2231
|
+
/* @__PURE__ */ jsx9(
|
|
1688
2232
|
"button",
|
|
1689
2233
|
{
|
|
1690
2234
|
type: "button",
|
|
@@ -1695,10 +2239,10 @@ var ImageEditor = ({
|
|
|
1695
2239
|
)
|
|
1696
2240
|
] })
|
|
1697
2241
|
] }),
|
|
1698
|
-
activeTab === "adjust" && /* @__PURE__ */
|
|
1699
|
-
/* @__PURE__ */
|
|
1700
|
-
/* @__PURE__ */
|
|
1701
|
-
/* @__PURE__ */
|
|
2242
|
+
activeTab === "adjust" && /* @__PURE__ */ jsxs9("div", { className: "flex flex-col gap-3 text-xs", children: [
|
|
2243
|
+
/* @__PURE__ */ jsxs9("div", { className: "flex items-center justify-between", children: [
|
|
2244
|
+
/* @__PURE__ */ jsx9("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Color Adjustments" }),
|
|
2245
|
+
/* @__PURE__ */ jsx9(
|
|
1702
2246
|
"button",
|
|
1703
2247
|
{
|
|
1704
2248
|
type: "button",
|
|
@@ -1708,9 +2252,9 @@ var ImageEditor = ({
|
|
|
1708
2252
|
}
|
|
1709
2253
|
)
|
|
1710
2254
|
] }),
|
|
1711
|
-
/* @__PURE__ */
|
|
1712
|
-
IMAGE_ADJUSTMENT_CONTROLS.map((spec) => /* @__PURE__ */
|
|
1713
|
-
/* @__PURE__ */
|
|
2255
|
+
/* @__PURE__ */ jsx9(Histogram, { histogram: editor.histogram }),
|
|
2256
|
+
IMAGE_ADJUSTMENT_CONTROLS.map((spec) => /* @__PURE__ */ jsxs9(React8.Fragment, { children: [
|
|
2257
|
+
/* @__PURE__ */ jsx9(
|
|
1714
2258
|
ControlSlider,
|
|
1715
2259
|
{
|
|
1716
2260
|
spec,
|
|
@@ -1719,14 +2263,14 @@ var ImageEditor = ({
|
|
|
1719
2263
|
onChange: (v) => editor.setAdjustments({ [spec.id]: v })
|
|
1720
2264
|
}
|
|
1721
2265
|
),
|
|
1722
|
-
spec.id === "tint" && legacyProcess && /* @__PURE__ */
|
|
2266
|
+
spec.id === "tint" && legacyProcess && /* @__PURE__ */ jsxs9(
|
|
1723
2267
|
"div",
|
|
1724
2268
|
{
|
|
1725
2269
|
className: "flex items-center justify-between gap-2 rounded border border-amber-500/30 bg-amber-500/10 px-2 py-1.5 text-[10px] text-amber-200",
|
|
1726
2270
|
"data-testid": "image-editor-process-notice",
|
|
1727
2271
|
children: [
|
|
1728
|
-
/* @__PURE__ */
|
|
1729
|
-
/* @__PURE__ */
|
|
2272
|
+
/* @__PURE__ */ jsx9("span", { children: "Legacy rendering (process 1). Tint needs the current process." }),
|
|
2273
|
+
/* @__PURE__ */ jsx9(
|
|
1730
2274
|
"button",
|
|
1731
2275
|
{
|
|
1732
2276
|
type: "button",
|
|
@@ -1739,7 +2283,7 @@ var ImageEditor = ({
|
|
|
1739
2283
|
}
|
|
1740
2284
|
)
|
|
1741
2285
|
] }, spec.id)),
|
|
1742
|
-
/* @__PURE__ */
|
|
2286
|
+
/* @__PURE__ */ jsx9("div", { className: "border-t border-slate-700/60 pt-3", children: /* @__PURE__ */ jsx9(
|
|
1743
2287
|
AdjustmentLayersPanel,
|
|
1744
2288
|
{
|
|
1745
2289
|
controller,
|
|
@@ -1748,9 +2292,19 @@ var ImageEditor = ({
|
|
|
1748
2292
|
}
|
|
1749
2293
|
) })
|
|
1750
2294
|
] }),
|
|
1751
|
-
activeTab === "
|
|
1752
|
-
/* @__PURE__ */
|
|
1753
|
-
/* @__PURE__ */
|
|
2295
|
+
activeTab === "layers" && /* @__PURE__ */ jsxs9("div", { className: "flex flex-col gap-3 text-xs", children: [
|
|
2296
|
+
/* @__PURE__ */ jsx9("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Layers" }),
|
|
2297
|
+
/* @__PURE__ */ jsx9(LayersPanel, { controller })
|
|
2298
|
+
] }),
|
|
2299
|
+
activeTab === "paint" && /* @__PURE__ */ jsxs9("div", { className: "flex flex-col gap-3 text-xs", children: [
|
|
2300
|
+
/* @__PURE__ */ jsx9("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Paint" }),
|
|
2301
|
+
/* @__PURE__ */ jsx9(BrushOptionsBar, { controller }),
|
|
2302
|
+
paintError && /* @__PURE__ */ jsx9("p", { role: "alert", className: "text-rose-400", "data-testid": "paint-error", children: paintError }),
|
|
2303
|
+
/* @__PURE__ */ jsx9("div", { className: "border-t border-slate-700/60 pt-3", children: /* @__PURE__ */ jsx9(LayersPanel, { controller }) })
|
|
2304
|
+
] }),
|
|
2305
|
+
activeTab === "annotate" && /* @__PURE__ */ jsxs9("div", { className: "flex flex-col gap-3 text-xs", children: [
|
|
2306
|
+
/* @__PURE__ */ jsx9("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Draw & Annotate" }),
|
|
2307
|
+
/* @__PURE__ */ jsx9("div", { className: "grid grid-cols-3 gap-1.5", children: ["pen", "rect", "circle", "arrow", "line"].map((t) => /* @__PURE__ */ jsx9(
|
|
1754
2308
|
"button",
|
|
1755
2309
|
{
|
|
1756
2310
|
type: "button",
|
|
@@ -1760,9 +2314,9 @@ var ImageEditor = ({
|
|
|
1760
2314
|
},
|
|
1761
2315
|
t
|
|
1762
2316
|
)) }),
|
|
1763
|
-
/* @__PURE__ */
|
|
1764
|
-
/* @__PURE__ */
|
|
1765
|
-
/* @__PURE__ */
|
|
2317
|
+
/* @__PURE__ */ jsxs9("div", { className: "flex items-center gap-2 mt-2", children: [
|
|
2318
|
+
/* @__PURE__ */ jsx9("label", { className: "text-slate-400", children: "Color:" }),
|
|
2319
|
+
/* @__PURE__ */ jsx9(
|
|
1766
2320
|
"input",
|
|
1767
2321
|
{
|
|
1768
2322
|
type: "color",
|
|
@@ -1771,15 +2325,15 @@ var ImageEditor = ({
|
|
|
1771
2325
|
className: "w-8 h-8 rounded border border-slate-700 cursor-pointer bg-transparent"
|
|
1772
2326
|
}
|
|
1773
2327
|
),
|
|
1774
|
-
/* @__PURE__ */
|
|
1775
|
-
/* @__PURE__ */
|
|
1776
|
-
/* @__PURE__ */
|
|
1777
|
-
/* @__PURE__ */
|
|
2328
|
+
/* @__PURE__ */ jsxs9("div", { className: "flex-1 ml-2", children: [
|
|
2329
|
+
/* @__PURE__ */ jsxs9("div", { className: "flex justify-between text-slate-400 mb-0.5", children: [
|
|
2330
|
+
/* @__PURE__ */ jsx9("span", { children: "Width:" }),
|
|
2331
|
+
/* @__PURE__ */ jsxs9("span", { className: "font-mono", children: [
|
|
1778
2332
|
strokeWidth,
|
|
1779
2333
|
"px"
|
|
1780
2334
|
] })
|
|
1781
2335
|
] }),
|
|
1782
|
-
/* @__PURE__ */
|
|
2336
|
+
/* @__PURE__ */ jsx9(
|
|
1783
2337
|
"input",
|
|
1784
2338
|
{
|
|
1785
2339
|
type: "range",
|
|
@@ -1792,10 +2346,10 @@ var ImageEditor = ({
|
|
|
1792
2346
|
)
|
|
1793
2347
|
] })
|
|
1794
2348
|
] }),
|
|
1795
|
-
/* @__PURE__ */
|
|
1796
|
-
/* @__PURE__ */
|
|
1797
|
-
/* @__PURE__ */
|
|
1798
|
-
/* @__PURE__ */
|
|
2349
|
+
/* @__PURE__ */ jsx9("div", { className: "h-px bg-slate-800 my-1" }),
|
|
2350
|
+
/* @__PURE__ */ jsx9("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Add Text" }),
|
|
2351
|
+
/* @__PURE__ */ jsxs9("div", { className: "flex gap-2", children: [
|
|
2352
|
+
/* @__PURE__ */ jsx9(
|
|
1799
2353
|
"input",
|
|
1800
2354
|
{
|
|
1801
2355
|
type: "text",
|
|
@@ -1806,7 +2360,7 @@ var ImageEditor = ({
|
|
|
1806
2360
|
className: "flex-1 bg-slate-950 border border-slate-700 rounded-lg px-2.5 py-1.5 text-xs text-slate-100 placeholder-slate-500 focus:outline-none focus:border-indigo-500"
|
|
1807
2361
|
}
|
|
1808
2362
|
),
|
|
1809
|
-
/* @__PURE__ */
|
|
2363
|
+
/* @__PURE__ */ jsx9(
|
|
1810
2364
|
"button",
|
|
1811
2365
|
{
|
|
1812
2366
|
type: "button",
|
|
@@ -1816,14 +2370,14 @@ var ImageEditor = ({
|
|
|
1816
2370
|
}
|
|
1817
2371
|
)
|
|
1818
2372
|
] }),
|
|
1819
|
-
/* @__PURE__ */
|
|
1820
|
-
/* @__PURE__ */
|
|
1821
|
-
/* @__PURE__ */
|
|
2373
|
+
/* @__PURE__ */ jsxs9("div", { className: "flex items-center justify-between text-slate-400", children: [
|
|
2374
|
+
/* @__PURE__ */ jsx9("span", { children: "Font Size:" }),
|
|
2375
|
+
/* @__PURE__ */ jsxs9("span", { className: "font-mono", children: [
|
|
1822
2376
|
fontSize,
|
|
1823
2377
|
"px"
|
|
1824
2378
|
] })
|
|
1825
2379
|
] }),
|
|
1826
|
-
/* @__PURE__ */
|
|
2380
|
+
/* @__PURE__ */ jsx9(
|
|
1827
2381
|
"input",
|
|
1828
2382
|
{
|
|
1829
2383
|
type: "range",
|
|
@@ -1834,14 +2388,14 @@ var ImageEditor = ({
|
|
|
1834
2388
|
className: "w-full accent-indigo-500"
|
|
1835
2389
|
}
|
|
1836
2390
|
),
|
|
1837
|
-
/* @__PURE__ */
|
|
1838
|
-
/* @__PURE__ */
|
|
1839
|
-
/* @__PURE__ */
|
|
2391
|
+
/* @__PURE__ */ jsx9("div", { className: "h-px bg-slate-800 my-1" }),
|
|
2392
|
+
/* @__PURE__ */ jsxs9("div", { className: "flex items-center justify-between", children: [
|
|
2393
|
+
/* @__PURE__ */ jsxs9("span", { className: "text-slate-400", children: [
|
|
1840
2394
|
"Layers (",
|
|
1841
2395
|
state.annotations.length,
|
|
1842
2396
|
")"
|
|
1843
2397
|
] }),
|
|
1844
|
-
state.selectedAnnotationId && /* @__PURE__ */
|
|
2398
|
+
state.selectedAnnotationId && /* @__PURE__ */ jsx9(
|
|
1845
2399
|
"button",
|
|
1846
2400
|
{
|
|
1847
2401
|
type: "button",
|
|
@@ -1850,7 +2404,7 @@ var ImageEditor = ({
|
|
|
1850
2404
|
children: "Delete Selected"
|
|
1851
2405
|
}
|
|
1852
2406
|
),
|
|
1853
|
-
state.annotations.length > 0 && /* @__PURE__ */
|
|
2407
|
+
state.annotations.length > 0 && /* @__PURE__ */ jsx9(
|
|
1854
2408
|
"button",
|
|
1855
2409
|
{
|
|
1856
2410
|
type: "button",
|
|
@@ -1861,10 +2415,10 @@ var ImageEditor = ({
|
|
|
1861
2415
|
)
|
|
1862
2416
|
] })
|
|
1863
2417
|
] }),
|
|
1864
|
-
activeTab === "depth" && /* @__PURE__ */
|
|
1865
|
-
/* @__PURE__ */
|
|
1866
|
-
/* @__PURE__ */
|
|
1867
|
-
/* @__PURE__ */
|
|
2418
|
+
activeTab === "depth" && /* @__PURE__ */ jsxs9("div", { className: "flex flex-col gap-3 text-xs", children: [
|
|
2419
|
+
/* @__PURE__ */ jsxs9("div", { className: "flex items-center justify-between", children: [
|
|
2420
|
+
/* @__PURE__ */ jsx9("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Depth Masking" }),
|
|
2421
|
+
/* @__PURE__ */ jsx9(
|
|
1868
2422
|
"button",
|
|
1869
2423
|
{
|
|
1870
2424
|
type: "button",
|
|
@@ -1874,8 +2428,8 @@ var ImageEditor = ({
|
|
|
1874
2428
|
}
|
|
1875
2429
|
)
|
|
1876
2430
|
] }),
|
|
1877
|
-
/* @__PURE__ */
|
|
1878
|
-
/* @__PURE__ */
|
|
2431
|
+
/* @__PURE__ */ jsxs9("label", { className: "flex items-center gap-2 cursor-pointer text-slate-300", children: [
|
|
2432
|
+
/* @__PURE__ */ jsx9(
|
|
1879
2433
|
"input",
|
|
1880
2434
|
{
|
|
1881
2435
|
type: "checkbox",
|
|
@@ -1884,12 +2438,12 @@ var ImageEditor = ({
|
|
|
1884
2438
|
className: "rounded accent-indigo-500"
|
|
1885
2439
|
}
|
|
1886
2440
|
),
|
|
1887
|
-
/* @__PURE__ */
|
|
2441
|
+
/* @__PURE__ */ jsx9("span", { children: "Enable Depth / Alpha Mask" })
|
|
1888
2442
|
] }),
|
|
1889
|
-
state.depthMask.enabled && /* @__PURE__ */
|
|
1890
|
-
/* @__PURE__ */
|
|
1891
|
-
/* @__PURE__ */
|
|
1892
|
-
/* @__PURE__ */
|
|
2443
|
+
state.depthMask.enabled && /* @__PURE__ */ jsxs9(Fragment2, { children: [
|
|
2444
|
+
/* @__PURE__ */ jsxs9("div", { children: [
|
|
2445
|
+
/* @__PURE__ */ jsx9("label", { className: "text-slate-400 block mb-1", children: "Mask Image URL:" }),
|
|
2446
|
+
/* @__PURE__ */ jsx9(
|
|
1893
2447
|
"input",
|
|
1894
2448
|
{
|
|
1895
2449
|
type: "text",
|
|
@@ -1900,8 +2454,8 @@ var ImageEditor = ({
|
|
|
1900
2454
|
}
|
|
1901
2455
|
)
|
|
1902
2456
|
] }),
|
|
1903
|
-
/* @__PURE__ */
|
|
1904
|
-
/* @__PURE__ */
|
|
2457
|
+
/* @__PURE__ */ jsxs9("div", { className: "flex gap-2", children: [
|
|
2458
|
+
/* @__PURE__ */ jsx9(
|
|
1905
2459
|
"button",
|
|
1906
2460
|
{
|
|
1907
2461
|
type: "button",
|
|
@@ -1910,7 +2464,7 @@ var ImageEditor = ({
|
|
|
1910
2464
|
children: "Threshold Mode"
|
|
1911
2465
|
}
|
|
1912
2466
|
),
|
|
1913
|
-
/* @__PURE__ */
|
|
2467
|
+
/* @__PURE__ */ jsx9(
|
|
1914
2468
|
"button",
|
|
1915
2469
|
{
|
|
1916
2470
|
type: "button",
|
|
@@ -1920,12 +2474,12 @@ var ImageEditor = ({
|
|
|
1920
2474
|
}
|
|
1921
2475
|
)
|
|
1922
2476
|
] }),
|
|
1923
|
-
/* @__PURE__ */
|
|
1924
|
-
/* @__PURE__ */
|
|
1925
|
-
/* @__PURE__ */
|
|
1926
|
-
/* @__PURE__ */
|
|
2477
|
+
/* @__PURE__ */ jsxs9("div", { children: [
|
|
2478
|
+
/* @__PURE__ */ jsxs9("div", { className: "flex justify-between text-slate-400 mb-1", children: [
|
|
2479
|
+
/* @__PURE__ */ jsx9("span", { children: "Depth Start (Lower):" }),
|
|
2480
|
+
/* @__PURE__ */ jsx9("span", { className: "font-mono", children: state.depthMask.depthRange[0] })
|
|
1927
2481
|
] }),
|
|
1928
|
-
/* @__PURE__ */
|
|
2482
|
+
/* @__PURE__ */ jsx9(
|
|
1929
2483
|
"input",
|
|
1930
2484
|
{
|
|
1931
2485
|
type: "range",
|
|
@@ -1939,12 +2493,12 @@ var ImageEditor = ({
|
|
|
1939
2493
|
}
|
|
1940
2494
|
)
|
|
1941
2495
|
] }),
|
|
1942
|
-
/* @__PURE__ */
|
|
1943
|
-
/* @__PURE__ */
|
|
1944
|
-
/* @__PURE__ */
|
|
1945
|
-
/* @__PURE__ */
|
|
2496
|
+
/* @__PURE__ */ jsxs9("div", { children: [
|
|
2497
|
+
/* @__PURE__ */ jsxs9("div", { className: "flex justify-between text-slate-400 mb-1", children: [
|
|
2498
|
+
/* @__PURE__ */ jsx9("span", { children: "Depth Stop (Upper):" }),
|
|
2499
|
+
/* @__PURE__ */ jsx9("span", { className: "font-mono", children: state.depthMask.depthRange[1] })
|
|
1946
2500
|
] }),
|
|
1947
|
-
/* @__PURE__ */
|
|
2501
|
+
/* @__PURE__ */ jsx9(
|
|
1948
2502
|
"input",
|
|
1949
2503
|
{
|
|
1950
2504
|
type: "range",
|
|
@@ -1958,12 +2512,12 @@ var ImageEditor = ({
|
|
|
1958
2512
|
}
|
|
1959
2513
|
)
|
|
1960
2514
|
] }),
|
|
1961
|
-
/* @__PURE__ */
|
|
1962
|
-
/* @__PURE__ */
|
|
1963
|
-
/* @__PURE__ */
|
|
1964
|
-
/* @__PURE__ */
|
|
2515
|
+
/* @__PURE__ */ jsxs9("div", { children: [
|
|
2516
|
+
/* @__PURE__ */ jsxs9("div", { className: "flex justify-between text-slate-400 mb-1", children: [
|
|
2517
|
+
/* @__PURE__ */ jsx9("span", { children: "Softness:" }),
|
|
2518
|
+
/* @__PURE__ */ jsx9("span", { className: "font-mono", children: state.depthMask.softness })
|
|
1965
2519
|
] }),
|
|
1966
|
-
/* @__PURE__ */
|
|
2520
|
+
/* @__PURE__ */ jsx9(
|
|
1967
2521
|
"input",
|
|
1968
2522
|
{
|
|
1969
2523
|
type: "range",
|
|
@@ -1975,8 +2529,8 @@ var ImageEditor = ({
|
|
|
1975
2529
|
}
|
|
1976
2530
|
)
|
|
1977
2531
|
] }),
|
|
1978
|
-
/* @__PURE__ */
|
|
1979
|
-
/* @__PURE__ */
|
|
2532
|
+
/* @__PURE__ */ jsxs9("label", { className: "flex items-center gap-2 cursor-pointer text-slate-300 mt-1", children: [
|
|
2533
|
+
/* @__PURE__ */ jsx9(
|
|
1980
2534
|
"input",
|
|
1981
2535
|
{
|
|
1982
2536
|
type: "checkbox",
|
|
@@ -1985,12 +2539,12 @@ var ImageEditor = ({
|
|
|
1985
2539
|
className: "rounded accent-indigo-500"
|
|
1986
2540
|
}
|
|
1987
2541
|
),
|
|
1988
|
-
/* @__PURE__ */
|
|
2542
|
+
/* @__PURE__ */ jsx9("span", { children: "Invert Mask" })
|
|
1989
2543
|
] })
|
|
1990
2544
|
] })
|
|
1991
2545
|
] }),
|
|
1992
|
-
activeTab === "select" && /* @__PURE__ */
|
|
1993
|
-
/* @__PURE__ */
|
|
2546
|
+
activeTab === "select" && /* @__PURE__ */ jsxs9("div", { className: "flex flex-col gap-4 text-xs", children: [
|
|
2547
|
+
/* @__PURE__ */ jsx9(
|
|
1994
2548
|
SelectionOptionsBar,
|
|
1995
2549
|
{
|
|
1996
2550
|
controller,
|
|
@@ -2000,51 +2554,51 @@ var ImageEditor = ({
|
|
|
2000
2554
|
quickMask: Boolean(state.quickMask)
|
|
2001
2555
|
}
|
|
2002
2556
|
),
|
|
2003
|
-
selectionError && /* @__PURE__ */
|
|
2004
|
-
/* @__PURE__ */
|
|
2557
|
+
selectionError && /* @__PURE__ */ jsx9("p", { role: "alert", className: "text-rose-400", children: selectionError }),
|
|
2558
|
+
/* @__PURE__ */ jsx9(RefineEdgePanel, { controller, selection: state.selection ?? null, layers: state.adjustments.layers })
|
|
2005
2559
|
] }),
|
|
2006
|
-
activeTab === "ai" && /* @__PURE__ */
|
|
2007
|
-
/* @__PURE__ */
|
|
2008
|
-
/* @__PURE__ */
|
|
2009
|
-
/* @__PURE__ */
|
|
2010
|
-
/* @__PURE__ */
|
|
2560
|
+
activeTab === "ai" && /* @__PURE__ */ jsxs9("div", { className: "flex flex-col gap-4 text-xs", children: [
|
|
2561
|
+
/* @__PURE__ */ jsxs9("div", { className: "p-3 rounded-lg bg-slate-950/60 border border-slate-800 flex flex-col gap-2.5", children: [
|
|
2562
|
+
/* @__PURE__ */ jsxs9("div", { className: "flex items-center justify-between", children: [
|
|
2563
|
+
/* @__PURE__ */ jsxs9("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px] flex items-center gap-1.5", children: [
|
|
2564
|
+
/* @__PURE__ */ jsx9("svg", { className: "w-3.5 h-3.5 text-indigo-400", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: /* @__PURE__ */ jsx9("path", { d: "M6 3v12M18 9a3 3 0 1 0 0-6 3 3 0 0 0 0 6zM6 21a3 3 0 1 0 0-6 3 3 0 0 0 0 6z" }) }),
|
|
2011
2565
|
"Background Removal"
|
|
2012
2566
|
] }),
|
|
2013
|
-
isRemovingBg && /* @__PURE__ */
|
|
2567
|
+
isRemovingBg && /* @__PURE__ */ jsx9("span", { className: "inline-flex items-center px-1.5 py-0.5 rounded text-[10px] font-medium bg-indigo-900/60 text-indigo-300 animate-pulse", children: "Removing..." })
|
|
2014
2568
|
] }),
|
|
2015
|
-
/* @__PURE__ */
|
|
2016
|
-
/* @__PURE__ */
|
|
2569
|
+
/* @__PURE__ */ jsx9("p", { className: "text-slate-400 text-[11px] leading-relaxed", children: "Isolate foreground subject with AI alpha transparency segmentation." }),
|
|
2570
|
+
/* @__PURE__ */ jsx9(
|
|
2017
2571
|
"button",
|
|
2018
2572
|
{
|
|
2019
2573
|
type: "button",
|
|
2020
2574
|
onClick: handleRemoveBackground,
|
|
2021
2575
|
disabled: isRemovingBg || isInpainting,
|
|
2022
2576
|
className: "mt-1 px-3 py-2 rounded-lg bg-indigo-600 hover:bg-indigo-500 disabled:opacity-50 text-white font-medium shadow transition-colors flex items-center justify-center gap-2",
|
|
2023
|
-
children: isRemovingBg ? /* @__PURE__ */
|
|
2024
|
-
/* @__PURE__ */
|
|
2577
|
+
children: isRemovingBg ? /* @__PURE__ */ jsxs9(Fragment2, { children: [
|
|
2578
|
+
/* @__PURE__ */ jsx9("div", { className: "w-3.5 h-3.5 border-2 border-white/30 border-t-white rounded-full animate-spin" }),
|
|
2025
2579
|
"Removing Background..."
|
|
2026
|
-
] }) : /* @__PURE__ */
|
|
2027
|
-
/* @__PURE__ */
|
|
2580
|
+
] }) : /* @__PURE__ */ jsxs9(Fragment2, { children: [
|
|
2581
|
+
/* @__PURE__ */ jsx9("svg", { className: "w-3.5 h-3.5", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: /* @__PURE__ */ jsx9("path", { d: "M12 2v4M12 18v4M4.93 4.93l2.83 2.83M16.24 16.24l2.83 2.83M2 12h4M18 12h4M4.93 19.07l2.83-2.83M16.24 7.76l2.83-2.83" }) }),
|
|
2028
2582
|
"Remove Background"
|
|
2029
2583
|
] })
|
|
2030
2584
|
}
|
|
2031
2585
|
)
|
|
2032
2586
|
] }),
|
|
2033
|
-
/* @__PURE__ */
|
|
2034
|
-
/* @__PURE__ */
|
|
2035
|
-
/* @__PURE__ */
|
|
2036
|
-
/* @__PURE__ */
|
|
2037
|
-
/* @__PURE__ */
|
|
2038
|
-
/* @__PURE__ */
|
|
2587
|
+
/* @__PURE__ */ jsxs9("div", { className: "p-3 rounded-lg bg-slate-950/60 border border-slate-800 flex flex-col gap-2.5", children: [
|
|
2588
|
+
/* @__PURE__ */ jsxs9("div", { className: "flex items-center justify-between", children: [
|
|
2589
|
+
/* @__PURE__ */ jsxs9("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px] flex items-center gap-1.5", children: [
|
|
2590
|
+
/* @__PURE__ */ jsxs9("svg", { className: "w-3.5 h-3.5 text-purple-400", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
|
|
2591
|
+
/* @__PURE__ */ jsx9("path", { d: "m21.64 3.64-1.28-1.28a1.2 1.2 0 0 0-1.7 0l-9.72 9.72-1.94 4.86 4.86-1.94 9.72-9.72a1.2 1.2 0 0 0 .06-1.64z" }),
|
|
2592
|
+
/* @__PURE__ */ jsx9("path", { d: "m14 7 3 3" })
|
|
2039
2593
|
] }),
|
|
2040
2594
|
"Generative Inpainting"
|
|
2041
2595
|
] }),
|
|
2042
|
-
isInpainting && /* @__PURE__ */
|
|
2596
|
+
isInpainting && /* @__PURE__ */ jsx9("span", { className: "inline-flex items-center px-1.5 py-0.5 rounded text-[10px] font-medium bg-purple-900/60 text-purple-300 animate-pulse", children: "Inpainting..." })
|
|
2043
2597
|
] }),
|
|
2044
|
-
/* @__PURE__ */
|
|
2045
|
-
/* @__PURE__ */
|
|
2046
|
-
/* @__PURE__ */
|
|
2047
|
-
/* @__PURE__ */
|
|
2598
|
+
/* @__PURE__ */ jsx9("p", { className: "text-slate-400 text-[11px] leading-relaxed", children: "Replace, modify, or fill selected regions using prompt-guided generative inpainting." }),
|
|
2599
|
+
/* @__PURE__ */ jsxs9("div", { className: "flex flex-col gap-1", children: [
|
|
2600
|
+
/* @__PURE__ */ jsx9("label", { htmlFor: "ai-inpaint-prompt-react", className: "text-slate-300 text-[11px] font-medium", children: "Prompt" }),
|
|
2601
|
+
/* @__PURE__ */ jsx9(
|
|
2048
2602
|
"textarea",
|
|
2049
2603
|
{
|
|
2050
2604
|
id: "ai-inpaint-prompt-react",
|
|
@@ -2056,10 +2610,10 @@ var ImageEditor = ({
|
|
|
2056
2610
|
}
|
|
2057
2611
|
)
|
|
2058
2612
|
] }),
|
|
2059
|
-
/* @__PURE__ */
|
|
2060
|
-
/* @__PURE__ */
|
|
2061
|
-
/* @__PURE__ */
|
|
2062
|
-
/* @__PURE__ */
|
|
2613
|
+
/* @__PURE__ */ jsxs9("div", { className: "flex flex-col gap-1", children: [
|
|
2614
|
+
/* @__PURE__ */ jsx9("span", { className: "text-slate-300 text-[11px] font-medium", children: "Target Mask Region" }),
|
|
2615
|
+
/* @__PURE__ */ jsxs9("div", { className: "grid grid-cols-2 gap-1.5", children: [
|
|
2616
|
+
/* @__PURE__ */ jsxs9(
|
|
2063
2617
|
"button",
|
|
2064
2618
|
{
|
|
2065
2619
|
type: "button",
|
|
@@ -2072,7 +2626,7 @@ var ImageEditor = ({
|
|
|
2072
2626
|
]
|
|
2073
2627
|
}
|
|
2074
2628
|
),
|
|
2075
|
-
/* @__PURE__ */
|
|
2629
|
+
/* @__PURE__ */ jsxs9(
|
|
2076
2630
|
"button",
|
|
2077
2631
|
{
|
|
2078
2632
|
type: "button",
|
|
@@ -2087,25 +2641,25 @@ var ImageEditor = ({
|
|
|
2087
2641
|
)
|
|
2088
2642
|
] })
|
|
2089
2643
|
] }),
|
|
2090
|
-
/* @__PURE__ */
|
|
2644
|
+
/* @__PURE__ */ jsx9(
|
|
2091
2645
|
"button",
|
|
2092
2646
|
{
|
|
2093
2647
|
type: "button",
|
|
2094
2648
|
onClick: handleInpaint,
|
|
2095
2649
|
disabled: isInpainting || isRemovingBg || !inpaintPrompt.trim(),
|
|
2096
2650
|
className: "mt-1 px-3 py-2 rounded-lg bg-purple-600 hover:bg-purple-500 disabled:opacity-50 text-white font-medium shadow transition-colors flex items-center justify-center gap-2",
|
|
2097
|
-
children: isInpainting ? /* @__PURE__ */
|
|
2098
|
-
/* @__PURE__ */
|
|
2651
|
+
children: isInpainting ? /* @__PURE__ */ jsxs9(Fragment2, { children: [
|
|
2652
|
+
/* @__PURE__ */ jsx9("div", { className: "w-3.5 h-3.5 border-2 border-white/30 border-t-white rounded-full animate-spin" }),
|
|
2099
2653
|
"Generating Inpaint..."
|
|
2100
|
-
] }) : /* @__PURE__ */
|
|
2101
|
-
/* @__PURE__ */
|
|
2654
|
+
] }) : /* @__PURE__ */ jsxs9(Fragment2, { children: [
|
|
2655
|
+
/* @__PURE__ */ jsx9("svg", { className: "w-3.5 h-3.5", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: /* @__PURE__ */ jsx9("path", { d: "M12 2v4M12 18v4M4.93 4.93l2.83 2.83M16.24 16.24l2.83 2.83M2 12h4M18 12h4M4.93 19.07l2.83-2.83M16.24 7.76l2.83-2.83" }) }),
|
|
2102
2656
|
"Apply Inpainting"
|
|
2103
2657
|
] })
|
|
2104
2658
|
}
|
|
2105
2659
|
)
|
|
2106
2660
|
] }),
|
|
2107
|
-
aiError && /* @__PURE__ */
|
|
2108
|
-
aiSuccess && /* @__PURE__ */
|
|
2661
|
+
aiError && /* @__PURE__ */ jsx9("div", { className: "p-2.5 rounded-lg bg-rose-950/60 border border-rose-800 text-rose-300 text-[11px]", children: aiError }),
|
|
2662
|
+
aiSuccess && /* @__PURE__ */ jsx9("div", { className: "p-2.5 rounded-lg bg-emerald-950/60 border border-emerald-800 text-emerald-300 text-[11px]", children: aiSuccess })
|
|
2109
2663
|
] })
|
|
2110
2664
|
] })
|
|
2111
2665
|
] })
|
|
@@ -2121,8 +2675,10 @@ export {
|
|
|
2121
2675
|
useImageHistogram,
|
|
2122
2676
|
CurvesEditor,
|
|
2123
2677
|
AdjustmentLayersPanel,
|
|
2678
|
+
LayersPanel,
|
|
2679
|
+
BrushOptionsBar,
|
|
2124
2680
|
SelectionOptionsBar,
|
|
2125
2681
|
RefineEdgePanel,
|
|
2126
2682
|
ImageEditor
|
|
2127
2683
|
};
|
|
2128
|
-
//# sourceMappingURL=chunk-
|
|
2684
|
+
//# sourceMappingURL=chunk-SYTV33TW.js.map
|