@pixerate/editor-react 0.57.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.
@@ -159,7 +159,7 @@ function useImageEditor(options = {}) {
159
159
  }
160
160
 
161
161
  // src/image-editor/ImageEditor.tsx
162
- import React6, { useRef as useRef3, useEffect as useEffect4, useState as useState7, useCallback as useCallback2 } from "react";
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 useState5 } from "react";
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 jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
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] = useState5(2);
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__ */ jsxs5("div", { "data-testid": "selection-options-bar", className: `flex flex-col gap-3 text-xs ${className}`, children: [
655
- /* @__PURE__ */ jsx5("div", { role: "group", "aria-label": "Selection tool", className: "flex flex-wrap gap-1", children: SELECTION_TOOLS.map((t) => /* @__PURE__ */ jsx5("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)) }),
656
- /* @__PURE__ */ jsx5("div", { role: "group", "aria-label": "Selection mode", className: "flex flex-wrap gap-1", children: SELECTION_MODES.map((m) => /* @__PURE__ */ jsx5("button", { type: "button", "aria-pressed": options.mode === m, className: btn(options.mode === m), onClick: () => set({ mode: m }), children: SELECTION_MODE_LABELS[m] }, m)) }),
657
- options.tool === "wand" && /* @__PURE__ */ jsxs5("div", { className: "flex flex-col gap-2", children: [
658
- /* @__PURE__ */ jsxs5("label", { className: "flex flex-col gap-1", children: [
659
- /* @__PURE__ */ jsxs5("span", { className: "flex justify-between text-slate-400", children: [
660
- /* @__PURE__ */ jsx5("span", { children: "Tolerance" }),
661
- /* @__PURE__ */ jsx5("span", { className: "font-mono", children: options.tolerance })
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__ */ jsx5(
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__ */ jsxs5("label", { className: "flex items-center gap-2 text-slate-300", children: [
678
- /* @__PURE__ */ jsx5("input", { type: "checkbox", checked: options.contiguous, onChange: (e) => set({ contiguous: e.target.checked }) }),
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__ */ jsxs5("label", { className: "flex items-center gap-2 text-slate-300", children: [
682
- /* @__PURE__ */ jsx5("input", { type: "checkbox", checked: options.sampleMerged, onChange: (e) => set({ sampleMerged: e.target.checked }) }),
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__ */ jsxs5("label", { className: "flex items-center gap-2 text-slate-300", children: [
687
- /* @__PURE__ */ jsx5("input", { type: "checkbox", checked: options.antiAlias, onChange: (e) => set({ antiAlias: e.target.checked }) }),
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__ */ jsxs5("div", { className: "flex flex-wrap gap-1", children: [
691
- /* @__PURE__ */ jsx5("button", { type: "button", className: btn(false), onClick: () => controller.selectAll(), children: "Select All" }),
692
- /* @__PURE__ */ jsx5("button", { type: "button", className: btn(false), disabled: !sel, onClick: () => controller.deselect(), children: "Deselect" }),
693
- /* @__PURE__ */ jsx5("button", { type: "button", className: btn(false), onClick: () => controller.invertSelection(), children: "Inverse" }),
694
- /* @__PURE__ */ jsx5("button", { type: "button", "aria-pressed": qm, className: btn(qm), onClick: () => controller.setQuickMask(!qm), children: "Quick Mask" })
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__ */ jsxs5("div", { className: "flex flex-wrap items-center gap-1", children: [
697
- /* @__PURE__ */ jsxs5("label", { className: "flex items-center gap-1 text-slate-400", children: [
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__ */ jsx5(
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__ */ jsx5("button", { type: "button", className: btn(false), disabled: !sel, onClick: () => controller.modifySelection(op, radius), children: label }, op))
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__ */ jsx5("p", { "data-testid": "selection-status", className: "text-slate-400", children: describeSelection(sel) })
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 useState6 } from "react";
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 jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
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] = useState6(DEFAULT_PARAMS);
732
- const [busy, setBusy] = useState6(false);
733
- const [error, setError] = useState6(null);
734
- const [layerId, setLayerId] = useState6("");
735
- const [newKind, setNewKind] = useState6("hueSaturation");
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__ */ jsxs6("div", { "data-testid": "refine-edge-panel", className: `flex flex-col gap-3 text-xs ${className}`, children: [
751
- /* @__PURE__ */ jsx6("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Refine Edge" }),
752
- REFINE_EDGE_CONTROLS.map((c) => /* @__PURE__ */ jsxs6("label", { className: "flex flex-col gap-1", children: [
753
- /* @__PURE__ */ jsxs6("span", { className: "flex justify-between text-slate-400", children: [
754
- /* @__PURE__ */ jsx6("span", { children: c.label }),
755
- /* @__PURE__ */ jsxs6("span", { className: "font-mono", children: [
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__ */ jsx6(
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__ */ jsxs6("label", { className: "flex items-center gap-2 text-slate-300", children: [
775
- /* @__PURE__ */ jsx6("input", { type: "checkbox", checked: params.smartRadius, onChange: (e) => setParams({ ...params, smartRadius: e.target.checked }) }),
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__ */ jsxs6("div", { className: "flex gap-1", children: [
779
- /* @__PURE__ */ jsx6("button", { type: "button", className: button, disabled: !sel || busy, onClick: refine, children: "Refine Edge" }),
780
- /* @__PURE__ */ jsx6("button", { type: "button", className: button, onClick: () => setParams(DEFAULT_PARAMS), children: "Reset" })
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__ */ jsx6("p", { role: "alert", className: "text-rose-400", children: error }),
783
- /* @__PURE__ */ jsx6("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px] mt-2", children: "Layer Mask" }),
784
- /* @__PURE__ */ jsxs6("div", { className: "flex gap-1 items-center", children: [
785
- /* @__PURE__ */ jsx6(
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__ */ jsx6("option", { value: k.kind, children: k.label }, k.kind))
1251
+ children: ADJUSTMENT_KINDS2.map((k) => /* @__PURE__ */ jsx8("option", { value: k.kind, children: k.label }, k.kind))
793
1252
  }
794
1253
  ),
795
- /* @__PURE__ */ jsx6(
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__ */ jsxs6("div", { className: "flex flex-col gap-2", children: [
807
- /* @__PURE__ */ jsx6(
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__ */ jsxs6("option", { value: l.id, children: [
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__ */ jsxs6("div", { className: "flex flex-wrap gap-1", children: [
821
- /* @__PURE__ */ jsx6("button", { type: "button", className: button, disabled: !sel, onClick: () => controller.applySelectionAsLayerMask(target.id), children: "Mask with Selection" }),
822
- /* @__PURE__ */ jsx6("button", { type: "button", className: button, disabled: !target.mask, onClick: () => controller.selectionFromLayerMask(target.id), children: "Load as Selection" }),
823
- /* @__PURE__ */ jsx6("button", { type: "button", className: button, disabled: !target.mask, onClick: () => controller.setAdjustmentLayerMask(target.id, null), children: "Remove Mask" })
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__ */ jsxs6(Fragment, { children: [
826
- /* @__PURE__ */ jsxs6("label", { className: "flex flex-col gap-1", children: [
827
- /* @__PURE__ */ jsxs6("span", { className: "flex justify-between text-slate-400", children: [
828
- /* @__PURE__ */ jsx6("span", { children: "Density" }),
829
- /* @__PURE__ */ jsxs6("span", { className: "font-mono", children: [
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__ */ jsx6(
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__ */ jsxs6("label", { className: "flex flex-col gap-1", children: [
849
- /* @__PURE__ */ jsxs6("span", { className: "flex justify-between text-slate-400", children: [
850
- /* @__PURE__ */ jsx6("span", { children: "Mask Feather" }),
851
- /* @__PURE__ */ jsxs6("span", { className: "font-mono", children: [
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__ */ jsx6(
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 jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
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 = useRef3(null);
893
- const containerRef = useRef3(null);
894
- const [activeTab, setActiveTab] = useState7("crop");
895
- const [activeCropRatio, setActiveCropRatio] = useState7(null);
896
- const [selectionOptions, setSelectionOptions] = useState7(DEFAULT_SELECTION_TOOL_OPTIONS);
897
- const selectionOptionsRef = useRef3(selectionOptions);
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 = useRef3(null);
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 = useRef3(null);
904
- const [selectionError, setSelectionError] = useState7(null);
905
- const [inpaintPrompt, setInpaintPrompt] = useState7("");
906
- const [inpaintMaskMode, setInpaintMaskMode] = useState7("annotations");
907
- const [isRemovingBg, setIsRemovingBg] = useState7(false);
908
- const [isInpainting, setIsInpainting] = useState7(false);
909
- const [aiError, setAiError] = useState7(null);
910
- const [aiSuccess, setAiSuccess] = useState7(null);
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] = useState7("#ef4444");
973
- const [strokeWidth, setStrokeWidth] = useState7(3);
974
- const [fontSize, setFontSize] = useState7(24);
975
- const [textInput, setTextInput] = useState7("");
976
- const draftAnnotationRef = useRef3(null);
977
- const draftCropRef = useRef3(null);
978
- const cropDragInfoRef = useRef3(null);
979
- const annotationDragInfoRef = useRef3(null);
980
- const isDrawingRef = useRef3(false);
981
- const currentPathRef = useRef3([]);
982
- const dragStartPointRef = useRef3(null);
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
- useEffect4(() => {
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
- useEffect4(() => {
1479
+ useEffect6(() => {
1012
1480
  if (activeTab === "crop" && editor.state.activeTool !== "crop") {
1013
1481
  editor.setTool("crop");
1014
1482
  }
1015
1483
  }, [activeTab, editor]);
1016
- useEffect4(() => {
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__ */ jsxs7(
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__ */ jsxs7("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: [
1404
- /* @__PURE__ */ jsxs7("div", { className: "flex items-center gap-1 bg-slate-900 p-1 rounded-lg border border-slate-800 text-xs", children: [
1405
- /* @__PURE__ */ jsx7(
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__ */ jsx7(
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__ */ jsx7(
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__ */ jsx7(
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__ */ jsx7(
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__ */ jsxs7(
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__ */ jsx7("svg", { className: "w-3.5 h-3.5", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: /* @__PURE__ */ jsx7("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" }) }),
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__ */ jsxs7("div", { className: "flex items-center gap-2 text-xs", children: [
1482
- /* @__PURE__ */ jsx7(
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__ */ jsx7(
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__ */ jsx7(
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__ */ jsx7("div", { className: "h-4 w-px bg-slate-800 mx-1" }),
1514
- /* @__PURE__ */ jsx7(
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__ */ jsxs7("span", { className: "font-mono text-slate-400 w-10 text-center", children: [
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__ */ jsx7(
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__ */ jsx7("div", { className: "h-4 w-px bg-slate-800 mx-1" }),
1539
- /* @__PURE__ */ jsx7(
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__ */ jsx7(
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__ */ jsxs7("div", { className: "flex flex-col md:flex-row flex-1 min-h-[520px] md:h-[560px]", children: [
1562
- /* @__PURE__ */ jsx7(
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__ */ jsx7(
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
- className: "absolute inset-0 w-full h-full block"
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__ */ jsxs7("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: [
1581
- activeTab === "crop" && /* @__PURE__ */ jsxs7("div", { className: "flex flex-col gap-4 text-xs", children: [
1582
- /* @__PURE__ */ jsx7("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Aspect Ratio Presets" }),
1583
- /* @__PURE__ */ jsxs7("div", { className: "grid grid-cols-3 gap-2", children: [
1584
- /* @__PURE__ */ jsx7(
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__ */ jsx7(
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__ */ jsx7(
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__ */ jsx7(
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__ */ jsx7(
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__ */ jsx7(
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__ */ jsx7("div", { className: "h-px bg-slate-800 my-1" }),
1658
- /* @__PURE__ */ jsx7("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Orientation & Flips" }),
1659
- /* @__PURE__ */ jsxs7("div", { className: "grid grid-cols-2 gap-2", children: [
1660
- /* @__PURE__ */ jsx7(
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__ */ jsx7(
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__ */ jsx7(
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__ */ jsx7(
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__ */ jsxs7("div", { className: "flex flex-col gap-3 text-xs", children: [
1699
- /* @__PURE__ */ jsxs7("div", { className: "flex items-center justify-between", children: [
1700
- /* @__PURE__ */ jsx7("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Color Adjustments" }),
1701
- /* @__PURE__ */ jsx7(
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__ */ jsx7(Histogram, { histogram: editor.histogram }),
1712
- IMAGE_ADJUSTMENT_CONTROLS.map((spec) => /* @__PURE__ */ jsxs7(React6.Fragment, { children: [
1713
- /* @__PURE__ */ jsx7(
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__ */ jsxs7(
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__ */ jsx7("span", { children: "Legacy rendering (process 1). Tint needs the current process." }),
1729
- /* @__PURE__ */ jsx7(
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__ */ jsx7("div", { className: "border-t border-slate-700/60 pt-3", children: /* @__PURE__ */ jsx7(
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 === "annotate" && /* @__PURE__ */ jsxs7("div", { className: "flex flex-col gap-3 text-xs", children: [
1752
- /* @__PURE__ */ jsx7("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Draw & Annotate" }),
1753
- /* @__PURE__ */ jsx7("div", { className: "grid grid-cols-3 gap-1.5", children: ["pen", "rect", "circle", "arrow", "line"].map((t) => /* @__PURE__ */ jsx7(
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__ */ jsxs7("div", { className: "flex items-center gap-2 mt-2", children: [
1764
- /* @__PURE__ */ jsx7("label", { className: "text-slate-400", children: "Color:" }),
1765
- /* @__PURE__ */ jsx7(
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__ */ jsxs7("div", { className: "flex-1 ml-2", children: [
1775
- /* @__PURE__ */ jsxs7("div", { className: "flex justify-between text-slate-400 mb-0.5", children: [
1776
- /* @__PURE__ */ jsx7("span", { children: "Width:" }),
1777
- /* @__PURE__ */ jsxs7("span", { className: "font-mono", children: [
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__ */ jsx7(
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__ */ jsx7("div", { className: "h-px bg-slate-800 my-1" }),
1796
- /* @__PURE__ */ jsx7("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Add Text" }),
1797
- /* @__PURE__ */ jsxs7("div", { className: "flex gap-2", children: [
1798
- /* @__PURE__ */ jsx7(
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__ */ jsx7(
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__ */ jsxs7("div", { className: "flex items-center justify-between text-slate-400", children: [
1820
- /* @__PURE__ */ jsx7("span", { children: "Font Size:" }),
1821
- /* @__PURE__ */ jsxs7("span", { className: "font-mono", children: [
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__ */ jsx7(
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__ */ jsx7("div", { className: "h-px bg-slate-800 my-1" }),
1838
- /* @__PURE__ */ jsxs7("div", { className: "flex items-center justify-between", children: [
1839
- /* @__PURE__ */ jsxs7("span", { className: "text-slate-400", children: [
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__ */ jsx7(
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__ */ jsx7(
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__ */ jsxs7("div", { className: "flex flex-col gap-3 text-xs", children: [
1865
- /* @__PURE__ */ jsxs7("div", { className: "flex items-center justify-between", children: [
1866
- /* @__PURE__ */ jsx7("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Depth Masking" }),
1867
- /* @__PURE__ */ jsx7(
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__ */ jsxs7("label", { className: "flex items-center gap-2 cursor-pointer text-slate-300", children: [
1878
- /* @__PURE__ */ jsx7(
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__ */ jsx7("span", { children: "Enable Depth / Alpha Mask" })
2441
+ /* @__PURE__ */ jsx9("span", { children: "Enable Depth / Alpha Mask" })
1888
2442
  ] }),
1889
- state.depthMask.enabled && /* @__PURE__ */ jsxs7(Fragment2, { children: [
1890
- /* @__PURE__ */ jsxs7("div", { children: [
1891
- /* @__PURE__ */ jsx7("label", { className: "text-slate-400 block mb-1", children: "Mask Image URL:" }),
1892
- /* @__PURE__ */ jsx7(
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__ */ jsxs7("div", { className: "flex gap-2", children: [
1904
- /* @__PURE__ */ jsx7(
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__ */ jsx7(
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__ */ jsxs7("div", { children: [
1924
- /* @__PURE__ */ jsxs7("div", { className: "flex justify-between text-slate-400 mb-1", children: [
1925
- /* @__PURE__ */ jsx7("span", { children: "Depth Start (Lower):" }),
1926
- /* @__PURE__ */ jsx7("span", { className: "font-mono", children: state.depthMask.depthRange[0] })
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__ */ jsx7(
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__ */ jsxs7("div", { children: [
1943
- /* @__PURE__ */ jsxs7("div", { className: "flex justify-between text-slate-400 mb-1", children: [
1944
- /* @__PURE__ */ jsx7("span", { children: "Depth Stop (Upper):" }),
1945
- /* @__PURE__ */ jsx7("span", { className: "font-mono", children: state.depthMask.depthRange[1] })
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__ */ jsx7(
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__ */ jsxs7("div", { children: [
1962
- /* @__PURE__ */ jsxs7("div", { className: "flex justify-between text-slate-400 mb-1", children: [
1963
- /* @__PURE__ */ jsx7("span", { children: "Softness:" }),
1964
- /* @__PURE__ */ jsx7("span", { className: "font-mono", children: state.depthMask.softness })
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__ */ jsx7(
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__ */ jsxs7("label", { className: "flex items-center gap-2 cursor-pointer text-slate-300 mt-1", children: [
1979
- /* @__PURE__ */ jsx7(
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__ */ jsx7("span", { children: "Invert Mask" })
2542
+ /* @__PURE__ */ jsx9("span", { children: "Invert Mask" })
1989
2543
  ] })
1990
2544
  ] })
1991
2545
  ] }),
1992
- activeTab === "select" && /* @__PURE__ */ jsxs7("div", { className: "flex flex-col gap-4 text-xs", children: [
1993
- /* @__PURE__ */ jsx7(
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__ */ jsx7("p", { role: "alert", className: "text-rose-400", children: selectionError }),
2004
- /* @__PURE__ */ jsx7(RefineEdgePanel, { controller, selection: state.selection ?? null, layers: state.adjustments.layers })
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__ */ jsxs7("div", { className: "flex flex-col gap-4 text-xs", children: [
2007
- /* @__PURE__ */ jsxs7("div", { className: "p-3 rounded-lg bg-slate-950/60 border border-slate-800 flex flex-col gap-2.5", children: [
2008
- /* @__PURE__ */ jsxs7("div", { className: "flex items-center justify-between", children: [
2009
- /* @__PURE__ */ jsxs7("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px] flex items-center gap-1.5", children: [
2010
- /* @__PURE__ */ jsx7("svg", { className: "w-3.5 h-3.5 text-indigo-400", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: /* @__PURE__ */ jsx7("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" }) }),
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__ */ jsx7("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..." })
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__ */ jsx7("p", { className: "text-slate-400 text-[11px] leading-relaxed", children: "Isolate foreground subject with AI alpha transparency segmentation." }),
2016
- /* @__PURE__ */ jsx7(
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__ */ jsxs7(Fragment2, { children: [
2024
- /* @__PURE__ */ jsx7("div", { className: "w-3.5 h-3.5 border-2 border-white/30 border-t-white rounded-full animate-spin" }),
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__ */ jsxs7(Fragment2, { children: [
2027
- /* @__PURE__ */ jsx7("svg", { className: "w-3.5 h-3.5", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: /* @__PURE__ */ jsx7("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" }) }),
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__ */ jsxs7("div", { className: "p-3 rounded-lg bg-slate-950/60 border border-slate-800 flex flex-col gap-2.5", children: [
2034
- /* @__PURE__ */ jsxs7("div", { className: "flex items-center justify-between", children: [
2035
- /* @__PURE__ */ jsxs7("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px] flex items-center gap-1.5", children: [
2036
- /* @__PURE__ */ jsxs7("svg", { className: "w-3.5 h-3.5 text-purple-400", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
2037
- /* @__PURE__ */ jsx7("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" }),
2038
- /* @__PURE__ */ jsx7("path", { d: "m14 7 3 3" })
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__ */ jsx7("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..." })
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__ */ jsx7("p", { className: "text-slate-400 text-[11px] leading-relaxed", children: "Replace, modify, or fill selected regions using prompt-guided generative inpainting." }),
2045
- /* @__PURE__ */ jsxs7("div", { className: "flex flex-col gap-1", children: [
2046
- /* @__PURE__ */ jsx7("label", { htmlFor: "ai-inpaint-prompt-react", className: "text-slate-300 text-[11px] font-medium", children: "Prompt" }),
2047
- /* @__PURE__ */ jsx7(
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__ */ jsxs7("div", { className: "flex flex-col gap-1", children: [
2060
- /* @__PURE__ */ jsx7("span", { className: "text-slate-300 text-[11px] font-medium", children: "Target Mask Region" }),
2061
- /* @__PURE__ */ jsxs7("div", { className: "grid grid-cols-2 gap-1.5", children: [
2062
- /* @__PURE__ */ jsxs7(
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__ */ jsxs7(
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__ */ jsx7(
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__ */ jsxs7(Fragment2, { children: [
2098
- /* @__PURE__ */ jsx7("div", { className: "w-3.5 h-3.5 border-2 border-white/30 border-t-white rounded-full animate-spin" }),
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__ */ jsxs7(Fragment2, { children: [
2101
- /* @__PURE__ */ jsx7("svg", { className: "w-3.5 h-3.5", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: /* @__PURE__ */ jsx7("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" }) }),
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__ */ jsx7("div", { className: "p-2.5 rounded-lg bg-rose-950/60 border border-rose-800 text-rose-300 text-[11px]", children: aiError }),
2108
- aiSuccess && /* @__PURE__ */ jsx7("div", { className: "p-2.5 rounded-lg bg-emerald-950/60 border border-emerald-800 text-emerald-300 text-[11px]", children: aiSuccess })
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-ZU54BMDC.js.map
2684
+ //# sourceMappingURL=chunk-SYTV33TW.js.map