@pixerate/editor-react 0.15.0 → 0.17.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.
Files changed (54) hide show
  1. package/README.md +6 -2
  2. package/dist/canvas/index.cjs +484 -119
  3. package/dist/canvas/index.cjs.map +1 -1
  4. package/dist/canvas/index.d.cts +122 -3
  5. package/dist/canvas/index.d.ts +122 -3
  6. package/dist/canvas/index.js +380 -0
  7. package/dist/canvas/index.js.map +1 -1
  8. package/dist/chunk-I3E4KSA2.js +515 -0
  9. package/dist/chunk-I3E4KSA2.js.map +1 -0
  10. package/dist/chunk-JR5ZBDR7.js +2012 -0
  11. package/dist/chunk-JR5ZBDR7.js.map +1 -0
  12. package/dist/chunk-XWFU2CSG.js +167 -0
  13. package/dist/chunk-XWFU2CSG.js.map +1 -0
  14. package/dist/chunk-ZU54BMDC.js +2128 -0
  15. package/dist/chunk-ZU54BMDC.js.map +1 -0
  16. package/dist/generation/index.cjs +525 -0
  17. package/dist/generation/index.cjs.map +1 -0
  18. package/dist/generation/index.d.cts +124 -0
  19. package/dist/generation/index.d.ts +124 -0
  20. package/dist/generation/index.js +19 -0
  21. package/dist/generation/index.js.map +1 -0
  22. package/dist/image-editor/index.cjs +988 -337
  23. package/dist/image-editor/index.cjs.map +1 -1
  24. package/dist/image-editor/index.d.cts +92 -2
  25. package/dist/image-editor/index.d.ts +92 -2
  26. package/dist/image-editor/index.js +17 -3
  27. package/dist/index.cjs +3628 -754
  28. package/dist/index.cjs.map +1 -1
  29. package/dist/index.d.cts +226 -5
  30. package/dist/index.d.ts +226 -5
  31. package/dist/index.js +868 -77
  32. package/dist/index.js.map +1 -1
  33. package/dist/persistence/index.cjs +191 -0
  34. package/dist/persistence/index.cjs.map +1 -0
  35. package/dist/persistence/index.d.cts +77 -0
  36. package/dist/persistence/index.d.ts +77 -0
  37. package/dist/persistence/index.js +9 -0
  38. package/dist/persistence/index.js.map +1 -0
  39. package/dist/spreadsheet/index.cjs +949 -111
  40. package/dist/spreadsheet/index.cjs.map +1 -1
  41. package/dist/spreadsheet/index.d.cts +121 -7
  42. package/dist/spreadsheet/index.d.ts +121 -7
  43. package/dist/spreadsheet/index.js +11 -1
  44. package/dist/tokens/index.cjs +395 -0
  45. package/dist/tokens/index.cjs.map +1 -0
  46. package/dist/tokens/index.d.cts +54 -0
  47. package/dist/tokens/index.d.ts +54 -0
  48. package/dist/tokens/index.js +375 -0
  49. package/dist/tokens/index.js.map +1 -0
  50. package/package.json +26 -2
  51. package/dist/chunk-LPFZMZ3L.js +0 -1461
  52. package/dist/chunk-LPFZMZ3L.js.map +0 -1
  53. package/dist/chunk-QVQYYHJ5.js +0 -1161
  54. package/dist/chunk-QVQYYHJ5.js.map +0 -1
@@ -1,7 +1,9 @@
1
1
  "use strict";
2
+ var __create = Object.create;
2
3
  var __defProp = Object.defineProperty;
3
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
5
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
6
8
  var __export = (target, all) => {
7
9
  for (var name in all)
@@ -15,13 +17,28 @@ var __copyProps = (to, from, except, desc) => {
15
17
  }
16
18
  return to;
17
19
  };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
18
28
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
29
 
20
30
  // src/image-editor/index.ts
21
31
  var image_editor_exports = {};
22
32
  __export(image_editor_exports, {
33
+ AdjustmentLayersPanel: () => AdjustmentLayersPanel,
34
+ ControlSlider: () => ControlSlider,
35
+ CurvesEditor: () => CurvesEditor,
36
+ Histogram: () => Histogram,
23
37
  ImageEditor: () => ImageEditor,
24
- useImageEditor: () => useImageEditor
38
+ RefineEdgePanel: () => RefineEdgePanel,
39
+ SelectionOptionsBar: () => SelectionOptionsBar,
40
+ useImageEditor: () => useImageEditor,
41
+ useImageHistogram: () => useImageHistogram
25
42
  });
26
43
  module.exports = __toCommonJS(image_editor_exports);
27
44
 
@@ -36,6 +53,8 @@ function useImageEditor(options = {}) {
36
53
  const controller = controllerRef.current;
37
54
  const [state, setState] = (0, import_react.useState)(() => controller.getState());
38
55
  const [historyTicks, setHistoryTicks] = (0, import_react.useState)(0);
56
+ const [histogram, setHistogram] = (0, import_react.useState)(() => controller.getHistogram());
57
+ (0, import_react.useEffect)(() => controller.subscribeHistogram(setHistogram), [controller]);
39
58
  (0, import_react.useEffect)(() => {
40
59
  const unsub = controller.subscribe((next) => {
41
60
  setState(next);
@@ -68,6 +87,20 @@ function useImageEditor(options = {}) {
68
87
  );
69
88
  const resetAdjustments = (0, import_react.useCallback)(() => controller.resetAdjustments(), [controller]);
70
89
  const upgradeProcess = (0, import_react.useCallback)(() => controller.upgradeProcess(), [controller]);
90
+ const addAdjustmentLayer = (0, import_react.useCallback)(
91
+ (init, index) => controller.addAdjustmentLayer(init, index),
92
+ [controller]
93
+ );
94
+ const updateAdjustmentLayer = (0, import_react.useCallback)(
95
+ (id, update, commitToHistory = true) => controller.updateAdjustmentLayer(id, update, commitToHistory),
96
+ [controller]
97
+ );
98
+ const removeAdjustmentLayer = (0, import_react.useCallback)((id) => controller.removeAdjustmentLayer(id), [controller]);
99
+ const moveAdjustmentLayer = (0, import_react.useCallback)((id, to) => controller.moveAdjustmentLayer(id, to), [controller]);
100
+ const toggleAdjustmentLayer = (0, import_react.useCallback)(
101
+ (id, enabled) => controller.toggleAdjustmentLayer(id, enabled),
102
+ [controller]
103
+ );
71
104
  const setDepthMask = (0, import_react.useCallback)((partial) => controller.setDepthMask(partial), [controller]);
72
105
  const resetDepthMask = (0, import_react.useCallback)(() => controller.resetDepthMask(), [controller]);
73
106
  const addAnnotation = (0, import_react.useCallback)((ann) => controller.addAnnotation(ann), [controller]);
@@ -129,6 +162,12 @@ function useImageEditor(options = {}) {
129
162
  setAdjustments,
130
163
  resetAdjustments,
131
164
  upgradeProcess,
165
+ addAdjustmentLayer,
166
+ updateAdjustmentLayer,
167
+ removeAdjustmentLayer,
168
+ moveAdjustmentLayer,
169
+ toggleAdjustmentLayer,
170
+ histogram,
132
171
  setDepthMask,
133
172
  resetDepthMask,
134
173
  addAnnotation,
@@ -157,14 +196,685 @@ function useImageEditor(options = {}) {
157
196
  applyBackgroundRemovedImage
158
197
  }),
159
198
  // eslint-disable-next-line react-hooks/exhaustive-deps
160
- [controller, state, historyTicks]
199
+ [controller, state, historyTicks, histogram]
161
200
  );
162
201
  }
163
202
 
164
203
  // src/image-editor/ImageEditor.tsx
165
- var import_react2 = require("react");
204
+ var import_react7 = __toESM(require("react"), 1);
205
+ var import_image_editor8 = require("@pixerate/editor/image-editor");
206
+
207
+ // src/image-editor/ControlSlider.tsx
166
208
  var import_image_editor2 = require("@pixerate/editor/image-editor");
167
209
  var import_jsx_runtime = require("react/jsx-runtime");
210
+ var ControlSlider = ({ spec, value, onChange, disabled, label }) => {
211
+ const name = label ?? spec.label;
212
+ const track = (0, import_image_editor2.controlTrackBackground)(spec);
213
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { "data-control-id": spec.id, children: [
214
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex justify-between text-slate-400 mb-1", children: [
215
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: name }),
216
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "font-mono", children: (0, import_image_editor2.formatControlValue)(spec, value) })
217
+ ] }),
218
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
219
+ "input",
220
+ {
221
+ type: "range",
222
+ min: spec.min,
223
+ max: spec.max,
224
+ step: spec.step,
225
+ "aria-label": name,
226
+ disabled,
227
+ value,
228
+ onChange: (e) => onChange(Number(e.target.value)),
229
+ className: "w-full accent-indigo-500 disabled:opacity-40"
230
+ }
231
+ ),
232
+ track && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "h-0.5 rounded-full opacity-70", style: { background: track } })
233
+ ] });
234
+ };
235
+
236
+ // src/image-editor/Histogram.tsx
237
+ var import_react2 = require("react");
238
+ var import_image_editor3 = require("@pixerate/editor/image-editor");
239
+ var import_jsx_runtime2 = require("react/jsx-runtime");
240
+ var FILL = {
241
+ luminance: "rgba(148, 163, 184, 0.45)",
242
+ red: "rgba(239, 68, 68, 0.45)",
243
+ green: "rgba(34, 197, 94, 0.45)",
244
+ blue: "rgba(59, 130, 246, 0.45)"
245
+ };
246
+ var W = 256;
247
+ var Histogram = ({ histogram, channel = "rgb", height = 64, className = "", style }) => {
248
+ const channels = channel === "rgb" ? ["luminance", "red", "green", "blue"] : [channel];
249
+ const paths = (0, import_react2.useMemo)(
250
+ () => histogram && histogram.samples > 0 ? channels.map((c) => ({ c, d: (0, import_image_editor3.histogramPath)(histogram, c, W, height) })) : [],
251
+ // eslint-disable-next-line react-hooks/exhaustive-deps
252
+ [histogram, channel, height]
253
+ );
254
+ const clipLow = histogram && histogram.samples ? Math.max(...histogram.shadows) / histogram.samples : 0;
255
+ const clipHigh = histogram && histogram.samples ? Math.max(...histogram.highlights) / histogram.samples : 0;
256
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
257
+ "svg",
258
+ {
259
+ viewBox: `0 0 ${W} ${height}`,
260
+ preserveAspectRatio: "none",
261
+ role: "img",
262
+ "aria-label": histogram ? `Histogram of ${histogram.samples} pixels` : "Histogram (no data)",
263
+ "data-testid": "image-editor-histogram",
264
+ className: `w-full rounded bg-slate-950/60 ${className}`,
265
+ style: { height, ...style },
266
+ children: [
267
+ paths.map(({ c, d }) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d, fill: FILL[c], stroke: "none" }, c)),
268
+ clipLow > 5e-3 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("rect", { x: 0, y: 0, width: 3, height, fill: "#3b82f6", "data-testid": "histogram-clip-shadows" }),
269
+ clipHigh > 5e-3 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("rect", { x: W - 3, y: 0, width: 3, height, fill: "#ef4444", "data-testid": "histogram-clip-highlights" })
270
+ ]
271
+ }
272
+ );
273
+ };
274
+ function useImageHistogram(controller) {
275
+ const [histogram, setHistogram] = (0, import_react2.useState)(() => controller.getHistogram());
276
+ (0, import_react2.useEffect)(() => controller.subscribeHistogram(setHistogram), [controller]);
277
+ return histogram;
278
+ }
279
+
280
+ // src/image-editor/AdjustmentLayersPanel.tsx
281
+ var import_react4 = require("react");
282
+ var import_image_editor5 = require("@pixerate/editor/image-editor");
283
+
284
+ // src/image-editor/CurvesEditor.tsx
285
+ var import_react3 = require("react");
286
+ var import_image_editor4 = require("@pixerate/editor/image-editor");
287
+ var import_jsx_runtime3 = require("react/jsx-runtime");
288
+ var BOX = 256;
289
+ var STROKE = { master: "#e2e8f0", red: "#ef4444", green: "#22c55e", blue: "#3b82f6" };
290
+ var CurvesEditor = ({ points, onChange, histogram, channel = "master", size = 200 }) => {
291
+ const svgRef = (0, import_react3.useRef)(null);
292
+ const [drag, setDrag] = (0, import_react3.useState)(null);
293
+ const toBox = (e) => {
294
+ const rect = svgRef.current?.getBoundingClientRect();
295
+ if (!rect || !rect.width || !rect.height) return { x: 0, y: 0 };
296
+ return { x: (e.clientX - rect.left) / rect.width * BOX, y: (e.clientY - rect.top) / rect.height * BOX };
297
+ };
298
+ const onPointerDown = (e) => {
299
+ const { x, y } = toBox(e);
300
+ let index = (0, import_image_editor4.hitTestCurvePoint)(points, x, y, BOX, BOX, 10);
301
+ if (index === -1) {
302
+ const inserted = (0, import_image_editor4.insertCurvePoint)(points, (0, import_image_editor4.boxToCurvePoint)(x, y, BOX, BOX));
303
+ onChange(inserted.points, true);
304
+ index = inserted.index;
305
+ } else {
306
+ onChange(points, true);
307
+ }
308
+ setDrag(index);
309
+ e.target.setPointerCapture?.(e.pointerId);
310
+ };
311
+ const onPointerMove = (e) => {
312
+ if (drag === null) return;
313
+ onChange((0, import_image_editor4.moveCurvePoint)(points, drag, (0, import_image_editor4.boxToCurvePoint)(toBox(e).x, toBox(e).y, BOX, BOX)), false);
314
+ };
315
+ const onDoubleClick = (e) => {
316
+ const { x, y } = toBox(e);
317
+ const index = (0, import_image_editor4.hitTestCurvePoint)(points, x, y, BOX, BOX, 10);
318
+ if (index !== -1 && points.length > 2) onChange((0, import_image_editor4.removeCurvePoint)(points, index), true);
319
+ };
320
+ const histChannel = channel === "master" ? "luminance" : channel;
321
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
322
+ "svg",
323
+ {
324
+ ref: svgRef,
325
+ viewBox: `0 0 ${BOX} ${BOX}`,
326
+ width: size,
327
+ height: size,
328
+ role: "application",
329
+ "aria-label": `${channel} curve`,
330
+ "data-testid": "curves-editor",
331
+ className: "rounded bg-slate-950/70 touch-none select-none cursor-crosshair",
332
+ onPointerDown,
333
+ onPointerMove,
334
+ onPointerUp: () => setDrag(null),
335
+ onPointerCancel: () => setDrag(null),
336
+ onDoubleClick,
337
+ children: [
338
+ histogram && histogram.samples > 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("path", { d: (0, import_image_editor4.histogramPath)(histogram, histChannel, BOX, BOX), fill: "rgba(148, 163, 184, 0.25)" }),
339
+ [64, 128, 192].map((v) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("g", { stroke: "rgba(148, 163, 184, 0.2)", strokeWidth: 1, children: [
340
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("line", { x1: v, y1: 0, x2: v, y2: BOX }),
341
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("line", { x1: 0, y1: v, x2: BOX, y2: v })
342
+ ] }, v)),
343
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("line", { x1: 0, y1: BOX, x2: BOX, y2: 0, stroke: "rgba(148, 163, 184, 0.35)", strokeDasharray: "4 4" }),
344
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("path", { d: (0, import_image_editor4.curveSvgPath)(points, BOX, BOX), fill: "none", stroke: STROKE[channel], strokeWidth: 2 }),
345
+ points.map((p, i) => {
346
+ const b = (0, import_image_editor4.curvePointToBox)(p, BOX, BOX);
347
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
348
+ "circle",
349
+ {
350
+ cx: b.x,
351
+ cy: b.y,
352
+ r: 5,
353
+ fill: drag === i ? STROKE[channel] : "#0f172a",
354
+ stroke: STROKE[channel],
355
+ strokeWidth: 2,
356
+ "aria-label": `Point ${p.input} to ${p.output}`
357
+ },
358
+ i
359
+ );
360
+ })
361
+ ]
362
+ }
363
+ );
364
+ };
365
+
366
+ // src/image-editor/AdjustmentLayersPanel.tsx
367
+ var import_jsx_runtime4 = require("react/jsx-runtime");
368
+ var CHANNELS = [
369
+ { id: "master", label: "RGB" },
370
+ { id: "red", label: "Red" },
371
+ { id: "green", label: "Green" },
372
+ { id: "blue", label: "Blue" }
373
+ ];
374
+ var TONES = [
375
+ { id: "shadows", label: "Shadows" },
376
+ { id: "midtones", label: "Midtones" },
377
+ { id: "highlights", label: "Highlights" }
378
+ ];
379
+ var selectClass = "bg-slate-800 border border-slate-700 rounded px-1.5 py-1 text-slate-200";
380
+ var chipClass = (on) => `px-2 py-0.5 rounded text-[10px] ${on ? "bg-indigo-600 text-white" : "bg-slate-800 text-slate-400 hover:text-slate-200"}`;
381
+ var HueRangeStrip = ({ bounds }) => {
382
+ const pts = Array.from({ length: 73 }, (_, i) => `${i / 72 * 360},${20 - (0, import_image_editor5.hueRangeWeight)({ bounds }, i * 5) * 18}`).join(" ");
383
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("svg", { viewBox: "0 0 360 20", preserveAspectRatio: "none", className: "w-full h-4 rounded", "data-testid": "hue-range-strip", children: [
384
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("defs", { children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("linearGradient", { id: "pixerate-hue-strip", children: [0, 60, 120, 180, 240, 300, 360].map((h) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("stop", { offset: `${h / 360 * 100}%`, stopColor: `hsl(${h}, 90%, 50%)` }, h)) }) }),
385
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("rect", { width: 360, height: 20, fill: "url(#pixerate-hue-strip)", opacity: 0.6 }),
386
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("polyline", { points: pts, fill: "none", stroke: "#fff", strokeWidth: 1.5 })
387
+ ] });
388
+ };
389
+ var AdjustmentLayersPanel = ({ controller, layers: layersProp, histogram }) => {
390
+ const follow = layersProp === void 0;
391
+ const [, setTick] = (0, import_react4.useState)(0);
392
+ (0, import_react4.useEffect)(() => follow ? controller.subscribe(() => setTick((n) => n + 1)) : void 0, [controller, follow]);
393
+ const layers = layersProp ?? controller.getAdjustmentLayers();
394
+ const [newKind, setNewKind] = (0, import_react4.useState)("curves");
395
+ const [selectedId, setSelectedId] = (0, import_react4.useState)(null);
396
+ const [channel, setChannel] = (0, import_react4.useState)("master");
397
+ const [range, setRange] = (0, import_react4.useState)(-1);
398
+ const [tone, setTone] = (0, import_react4.useState)("midtones");
399
+ const selected = layers.find((l) => l.id === selectedId) ?? null;
400
+ const topFirst = [...layers].reverse();
401
+ const setParam = (layer, path, value, commit = true) => controller.updateAdjustmentLayer(layer.id, { params: (0, import_image_editor5.setControlPath)(layer.params, path, value) }, commit);
402
+ const slider = (layer, spec, label) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
403
+ ControlSlider,
404
+ {
405
+ spec,
406
+ label,
407
+ value: Number((0, import_image_editor5.getControlPath)(layer.params, spec.id) ?? spec.default),
408
+ onChange: (v) => setParam(layer, spec.id, v)
409
+ },
410
+ spec.id
411
+ );
412
+ const controlsOf = (layer, filter = () => true) => import_image_editor5.ADJUSTMENT_LAYER_CONTROLS[layer.kind].filter(filter);
413
+ const checkbox = (layer, path, label) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { className: "flex items-center gap-2 text-slate-300", children: [
414
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
415
+ "input",
416
+ {
417
+ type: "checkbox",
418
+ checked: Boolean((0, import_image_editor5.getControlPath)(layer.params, path)),
419
+ onChange: (e) => setParam(layer, path, e.target.checked),
420
+ className: "accent-indigo-500"
421
+ }
422
+ ),
423
+ label
424
+ ] });
425
+ const editor = (layer) => {
426
+ switch (layer.kind) {
427
+ case "curves":
428
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex flex-col gap-2", children: [
429
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "flex gap-1", role: "group", "aria-label": "Curve channel", children: CHANNELS.map((c) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { type: "button", className: chipClass(channel === c.id), onClick: () => setChannel(c.id), children: c.label }, c.id)) }),
430
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
431
+ CurvesEditor,
432
+ {
433
+ points: layer.params[channel],
434
+ channel,
435
+ histogram,
436
+ onChange: (points, commit) => setParam(layer, channel, points, commit)
437
+ }
438
+ )
439
+ ] });
440
+ case "levels":
441
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex flex-col gap-2", children: [
442
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "flex gap-1", role: "group", "aria-label": "Levels channel", children: CHANNELS.map((c) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { type: "button", className: chipClass(channel === c.id), onClick: () => setChannel(c.id), children: c.label }, c.id)) }),
443
+ controlsOf(layer, (s) => s.id.startsWith(`${channel}.`)).map((s) => slider(layer, s))
444
+ ] });
445
+ case "hueSaturation":
446
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex flex-col gap-2", children: [
447
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex flex-wrap gap-1", role: "group", "aria-label": "Hue range", children: [
448
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { type: "button", className: chipClass(range === -1), onClick: () => setRange(-1), children: "Master" }),
449
+ import_image_editor5.HUE_RANGE_LABELS.map((name, i) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { type: "button", className: chipClass(range === i), onClick: () => setRange(i), disabled: layer.params.colorize, children: name }, name))
450
+ ] }),
451
+ range >= 0 && !layer.params.colorize && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(HueRangeStrip, { bounds: layer.params.ranges[range].bounds }),
452
+ controlsOf(layer, (s) => range === -1 || layer.params.colorize ? !s.id.startsWith("ranges.") : s.id.startsWith(`ranges.${range}.`)).map(
453
+ (s) => slider(layer, s)
454
+ ),
455
+ range === -1 && checkbox(layer, "colorize", "Colorize")
456
+ ] });
457
+ case "colorBalance":
458
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex flex-col gap-2", children: [
459
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "flex gap-1", role: "group", "aria-label": "Tone", children: TONES.map((t) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { type: "button", className: chipClass(tone === t.id), onClick: () => setTone(t.id), children: t.label }, t.id)) }),
460
+ controlsOf(layer, (s) => s.id.startsWith(`${tone}.`)).map((s) => slider(layer, s)),
461
+ checkbox(layer, "preserveLuminosity", "Preserve luminosity")
462
+ ] });
463
+ case "blackWhite":
464
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex flex-col gap-2", children: [
465
+ controlsOf(layer).map((s) => slider(layer, s)),
466
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { className: "flex items-center gap-2 text-slate-300", children: [
467
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
468
+ "input",
469
+ {
470
+ type: "checkbox",
471
+ checked: layer.params.tint !== null,
472
+ onChange: (e) => setParam(layer, "tint", e.target.checked ? "#e1d3b3" : null),
473
+ className: "accent-indigo-500"
474
+ }
475
+ ),
476
+ "Tint",
477
+ layer.params.tint && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
478
+ "input",
479
+ {
480
+ type: "color",
481
+ "aria-label": "Tint colour",
482
+ value: layer.params.tint,
483
+ onChange: (e) => setParam(layer, "tint", e.target.value),
484
+ className: "h-5 w-8 bg-transparent"
485
+ }
486
+ )
487
+ ] })
488
+ ] });
489
+ case "photoFilter":
490
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex flex-col gap-2", children: [
491
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { className: "flex items-center gap-2 text-slate-300", children: [
492
+ "Filter colour",
493
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
494
+ "input",
495
+ {
496
+ type: "color",
497
+ "aria-label": "Filter colour",
498
+ value: layer.params.color,
499
+ onChange: (e) => setParam(layer, "color", e.target.value),
500
+ className: "h-5 w-8 bg-transparent"
501
+ }
502
+ )
503
+ ] }),
504
+ controlsOf(layer).map((s) => slider(layer, s)),
505
+ checkbox(layer, "preserveLuminosity", "Preserve luminosity")
506
+ ] });
507
+ case "invert":
508
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "text-slate-500 text-[11px]", children: "Invert has no settings." });
509
+ default:
510
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "flex flex-col gap-2", children: controlsOf(layer).map((s) => slider(layer, s)) });
511
+ }
512
+ };
513
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex flex-col gap-2 text-xs", "data-testid": "adjustment-layers-panel", children: [
514
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "flex items-center justify-between", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Adjustment Layers" }) }),
515
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex gap-1.5", children: [
516
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
517
+ "select",
518
+ {
519
+ "aria-label": "New adjustment layer kind",
520
+ className: `${selectClass} flex-1`,
521
+ value: newKind,
522
+ onChange: (e) => setNewKind(e.target.value),
523
+ children: import_image_editor5.ADJUSTMENT_KINDS.map((k) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("option", { value: k.kind, children: k.label }, k.kind))
524
+ }
525
+ ),
526
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
527
+ "button",
528
+ {
529
+ type: "button",
530
+ className: "px-2 py-1 rounded bg-indigo-600 text-white hover:bg-indigo-500",
531
+ onClick: () => {
532
+ setSelectedId(controller.addAdjustmentLayer(newKind));
533
+ setChannel("master");
534
+ setRange(-1);
535
+ },
536
+ children: "Add Layer"
537
+ }
538
+ )
539
+ ] }),
540
+ topFirst.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("ul", { className: "flex flex-col gap-1", "aria-label": "Adjustment layers", children: topFirst.map((layer) => {
541
+ const index = layers.indexOf(layer);
542
+ const label = layer.name || (0, import_image_editor5.adjustmentKindLabel)(layer.kind);
543
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
544
+ "li",
545
+ {
546
+ className: `flex items-center gap-1.5 rounded px-1.5 py-1 ${layer.id === selectedId ? "bg-indigo-600/20 ring-1 ring-indigo-500/50" : "bg-slate-800/60"}`,
547
+ children: [
548
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
549
+ "input",
550
+ {
551
+ type: "checkbox",
552
+ "aria-label": `Show ${label}`,
553
+ checked: layer.enabled,
554
+ onChange: () => controller.toggleAdjustmentLayer(layer.id),
555
+ className: "accent-indigo-500"
556
+ }
557
+ ),
558
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
559
+ "button",
560
+ {
561
+ type: "button",
562
+ className: `flex-1 text-left truncate ${layer.enabled ? "text-slate-200" : "text-slate-500 line-through"}`,
563
+ onClick: () => setSelectedId(layer.id === selectedId ? null : layer.id),
564
+ "aria-pressed": layer.id === selectedId,
565
+ children: label
566
+ }
567
+ ),
568
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
569
+ "button",
570
+ {
571
+ type: "button",
572
+ "aria-label": `Move ${label} up`,
573
+ disabled: index === layers.length - 1,
574
+ onClick: () => controller.moveAdjustmentLayer(layer.id, index + 1),
575
+ className: "px-1 text-slate-400 hover:text-slate-200 disabled:opacity-30",
576
+ children: "\u2191"
577
+ }
578
+ ),
579
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
580
+ "button",
581
+ {
582
+ type: "button",
583
+ "aria-label": `Move ${label} down`,
584
+ disabled: index === 0,
585
+ onClick: () => controller.moveAdjustmentLayer(layer.id, index - 1),
586
+ className: "px-1 text-slate-400 hover:text-slate-200 disabled:opacity-30",
587
+ children: "\u2193"
588
+ }
589
+ ),
590
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
591
+ "button",
592
+ {
593
+ type: "button",
594
+ "aria-label": `Remove ${label}`,
595
+ onClick: () => {
596
+ controller.removeAdjustmentLayer(layer.id);
597
+ if (selectedId === layer.id) setSelectedId(null);
598
+ },
599
+ className: "px-1 text-rose-400 hover:text-rose-300",
600
+ children: "\u2715"
601
+ }
602
+ )
603
+ ]
604
+ },
605
+ layer.id
606
+ );
607
+ }) }),
608
+ selected && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex flex-col gap-2 rounded border border-slate-700/60 p-2", "data-testid": "adjustment-layer-editor", children: [
609
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex items-center gap-2", children: [
610
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "text-slate-400", children: "Blend" }),
611
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
612
+ "select",
613
+ {
614
+ "aria-label": "Blend mode",
615
+ className: `${selectClass} flex-1`,
616
+ value: selected.blend,
617
+ onChange: (e) => controller.updateAdjustmentLayer(selected.id, { blend: e.target.value }),
618
+ children: import_image_editor5.BLEND_MODES.map((m) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("option", { value: m.id, children: m.label }, m.id))
619
+ }
620
+ )
621
+ ] }),
622
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
623
+ ControlSlider,
624
+ {
625
+ spec: import_image_editor5.ADJUSTMENT_LAYER_OPACITY_CONTROL,
626
+ label: "Layer opacity",
627
+ value: selected.opacity,
628
+ onChange: (v) => controller.updateAdjustmentLayer(selected.id, { opacity: v })
629
+ }
630
+ ),
631
+ editor(selected)
632
+ ] })
633
+ ] });
634
+ };
635
+
636
+ // src/image-editor/SelectionOptionsBar.tsx
637
+ var import_react5 = require("react");
638
+ var import_image_editor6 = require("@pixerate/editor/image-editor");
639
+ var import_jsx_runtime5 = require("react/jsx-runtime");
640
+ var MODIFY_OPS = [
641
+ { op: "grow", label: "Grow" },
642
+ { op: "shrink", label: "Shrink" },
643
+ { op: "feather", label: "Feather" },
644
+ { op: "border", label: "Border" },
645
+ { op: "smooth", label: "Smooth" }
646
+ ];
647
+ var btn = (on) => `px-2 py-1 rounded-md border text-[11px] transition-colors ${on ? "bg-indigo-600 border-indigo-500 text-white" : "bg-slate-950 border-slate-800 text-slate-300 hover:text-white"}`;
648
+ var SelectionOptionsBar = ({
649
+ controller,
650
+ options,
651
+ onOptionsChange,
652
+ selection,
653
+ quickMask,
654
+ className = ""
655
+ }) => {
656
+ const [radius, setRadius] = (0, import_react5.useState)(2);
657
+ const set = (patch) => onOptionsChange({ ...options, ...patch });
658
+ const sel = selection === void 0 ? controller.getSelection() : selection;
659
+ const qm = quickMask ?? Boolean(controller.getState().quickMask);
660
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { "data-testid": "selection-options-bar", className: `flex flex-col gap-3 text-xs ${className}`, children: [
661
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { role: "group", "aria-label": "Selection tool", className: "flex flex-wrap gap-1", children: import_image_editor6.SELECTION_TOOLS.map((t) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { type: "button", "aria-pressed": options.tool === t.id, className: btn(options.tool === t.id), onClick: () => set({ tool: t.id }), children: t.label }, t.id)) }),
662
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { role: "group", "aria-label": "Selection mode", className: "flex flex-wrap gap-1", children: import_image_editor6.SELECTION_MODES.map((m) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { type: "button", "aria-pressed": options.mode === m, className: btn(options.mode === m), onClick: () => set({ mode: m }), children: import_image_editor6.SELECTION_MODE_LABELS[m] }, m)) }),
663
+ options.tool === "wand" && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex flex-col gap-2", children: [
664
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("label", { className: "flex flex-col gap-1", children: [
665
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { className: "flex justify-between text-slate-400", children: [
666
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: "Tolerance" }),
667
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "font-mono", children: options.tolerance })
668
+ ] }),
669
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
670
+ "input",
671
+ {
672
+ type: "range",
673
+ min: 0,
674
+ max: 255,
675
+ step: 1,
676
+ "aria-label": "Tolerance",
677
+ value: options.tolerance,
678
+ onChange: (e) => set({ tolerance: Number(e.target.value) }),
679
+ className: "w-full accent-indigo-500"
680
+ }
681
+ )
682
+ ] }),
683
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("label", { className: "flex items-center gap-2 text-slate-300", children: [
684
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("input", { type: "checkbox", checked: options.contiguous, onChange: (e) => set({ contiguous: e.target.checked }) }),
685
+ "Contiguous"
686
+ ] }),
687
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("label", { className: "flex items-center gap-2 text-slate-300", children: [
688
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("input", { type: "checkbox", checked: options.sampleMerged, onChange: (e) => set({ sampleMerged: e.target.checked }) }),
689
+ "Sample adjusted image"
690
+ ] })
691
+ ] }),
692
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("label", { className: "flex items-center gap-2 text-slate-300", children: [
693
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("input", { type: "checkbox", checked: options.antiAlias, onChange: (e) => set({ antiAlias: e.target.checked }) }),
694
+ "Anti-alias"
695
+ ] }),
696
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex flex-wrap gap-1", children: [
697
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { type: "button", className: btn(false), onClick: () => controller.selectAll(), children: "Select All" }),
698
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { type: "button", className: btn(false), disabled: !sel, onClick: () => controller.deselect(), children: "Deselect" }),
699
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { type: "button", className: btn(false), onClick: () => controller.invertSelection(), children: "Inverse" }),
700
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { type: "button", "aria-pressed": qm, className: btn(qm), onClick: () => controller.setQuickMask(!qm), children: "Quick Mask" })
701
+ ] }),
702
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex flex-wrap items-center gap-1", children: [
703
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("label", { className: "flex items-center gap-1 text-slate-400", children: [
704
+ "Radius",
705
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
706
+ "input",
707
+ {
708
+ type: "number",
709
+ min: 0,
710
+ max: 500,
711
+ step: 1,
712
+ "aria-label": "Modify radius",
713
+ value: radius,
714
+ onChange: (e) => setRadius(Math.max(0, Number(e.target.value) || 0)),
715
+ className: "w-14 bg-slate-950 border border-slate-800 rounded px-1 py-0.5 text-slate-200"
716
+ }
717
+ )
718
+ ] }),
719
+ MODIFY_OPS.map(({ op, label }) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { type: "button", className: btn(false), disabled: !sel, onClick: () => controller.modifySelection(op, radius), children: label }, op))
720
+ ] }),
721
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { "data-testid": "selection-status", className: "text-slate-400", children: (0, import_image_editor6.describeSelection)(sel) })
722
+ ] });
723
+ };
724
+
725
+ // src/image-editor/RefineEdgePanel.tsx
726
+ var import_react6 = require("react");
727
+ var import_image_editor7 = require("@pixerate/editor/image-editor");
728
+ var import_jsx_runtime6 = require("react/jsx-runtime");
729
+ var DEFAULT_PARAMS = { radius: 8, smartRadius: false, smooth: 0, feather: 0, contrast: 0, shiftEdge: 0 };
730
+ var button = "px-2 py-1 rounded-md border border-slate-800 bg-slate-950 text-[11px] text-slate-200 hover:text-white disabled:opacity-40";
731
+ var RefineEdgePanel = ({ controller, selection, layers, className = "" }) => {
732
+ const [params, setParams] = (0, import_react6.useState)(DEFAULT_PARAMS);
733
+ const [busy, setBusy] = (0, import_react6.useState)(false);
734
+ const [error, setError] = (0, import_react6.useState)(null);
735
+ const [layerId, setLayerId] = (0, import_react6.useState)("");
736
+ const [newKind, setNewKind] = (0, import_react6.useState)("hueSaturation");
737
+ const sel = selection === void 0 ? controller.getSelection() : selection;
738
+ const list = layers ?? controller.getAdjustmentLayers();
739
+ const target = list.find((l) => l.id === layerId) ?? list[list.length - 1];
740
+ const refine = async () => {
741
+ setBusy(true);
742
+ setError(null);
743
+ try {
744
+ await controller.refineSelection(params);
745
+ } catch (err) {
746
+ setError(err instanceof Error ? err.message : String(err));
747
+ } finally {
748
+ setBusy(false);
749
+ }
750
+ };
751
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { "data-testid": "refine-edge-panel", className: `flex flex-col gap-3 text-xs ${className}`, children: [
752
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Refine Edge" }),
753
+ import_image_editor7.REFINE_EDGE_CONTROLS.map((c) => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("label", { className: "flex flex-col gap-1", children: [
754
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "flex justify-between text-slate-400", children: [
755
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { children: c.label }),
756
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "font-mono", children: [
757
+ params[c.id],
758
+ c.unit
759
+ ] })
760
+ ] }),
761
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
762
+ "input",
763
+ {
764
+ type: "range",
765
+ min: c.min,
766
+ max: c.max,
767
+ step: c.step,
768
+ "aria-label": c.label,
769
+ value: params[c.id],
770
+ onChange: (e) => setParams({ ...params, [c.id]: Number(e.target.value) }),
771
+ className: "w-full accent-indigo-500"
772
+ }
773
+ )
774
+ ] }, c.id)),
775
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("label", { className: "flex items-center gap-2 text-slate-300", children: [
776
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("input", { type: "checkbox", checked: params.smartRadius, onChange: (e) => setParams({ ...params, smartRadius: e.target.checked }) }),
777
+ "Smart Radius"
778
+ ] }),
779
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex gap-1", children: [
780
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", className: button, disabled: !sel || busy, onClick: refine, children: "Refine Edge" }),
781
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", className: button, onClick: () => setParams(DEFAULT_PARAMS), children: "Reset" })
782
+ ] }),
783
+ error && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { role: "alert", className: "text-rose-400", children: error }),
784
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px] mt-2", children: "Layer Mask" }),
785
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex gap-1 items-center", children: [
786
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
787
+ "select",
788
+ {
789
+ "aria-label": "New masked layer kind",
790
+ value: newKind,
791
+ onChange: (e) => setNewKind(e.target.value),
792
+ className: "flex-1 bg-slate-950 border border-slate-800 rounded px-1 py-1 text-slate-200",
793
+ children: import_image_editor7.ADJUSTMENT_KINDS.map((k) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("option", { value: k.kind, children: k.label }, k.kind))
794
+ }
795
+ ),
796
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
797
+ "button",
798
+ {
799
+ type: "button",
800
+ className: button,
801
+ disabled: !sel,
802
+ onClick: () => sel && setLayerId(controller.addAdjustmentLayer({ kind: newKind, mask: (0, import_image_editor7.layerMaskFromSelection)(sel) })),
803
+ children: "New Masked Layer"
804
+ }
805
+ )
806
+ ] }),
807
+ list.length > 0 && target && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex flex-col gap-2", children: [
808
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
809
+ "select",
810
+ {
811
+ "aria-label": "Mask layer",
812
+ value: target.id,
813
+ onChange: (e) => setLayerId(e.target.value),
814
+ className: "bg-slate-950 border border-slate-800 rounded px-1 py-1 text-slate-200",
815
+ children: list.map((l) => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("option", { value: l.id, children: [
816
+ l.name || (0, import_image_editor7.adjustmentKindLabel)(l.kind),
817
+ l.mask ? " (masked)" : ""
818
+ ] }, l.id))
819
+ }
820
+ ),
821
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex flex-wrap gap-1", children: [
822
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", className: button, disabled: !sel, onClick: () => controller.applySelectionAsLayerMask(target.id), children: "Mask with Selection" }),
823
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", className: button, disabled: !target.mask, onClick: () => controller.selectionFromLayerMask(target.id), children: "Load as Selection" }),
824
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", className: button, disabled: !target.mask, onClick: () => controller.setAdjustmentLayerMask(target.id, null), children: "Remove Mask" })
825
+ ] }),
826
+ target.mask && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
827
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("label", { className: "flex flex-col gap-1", children: [
828
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "flex justify-between text-slate-400", children: [
829
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { children: "Density" }),
830
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "font-mono", children: [
831
+ Math.round((target.mask.density ?? 1) * 100),
832
+ "%"
833
+ ] })
834
+ ] }),
835
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
836
+ "input",
837
+ {
838
+ type: "range",
839
+ min: 0,
840
+ max: 100,
841
+ step: 1,
842
+ "aria-label": "Mask density",
843
+ value: Math.round((target.mask.density ?? 1) * 100),
844
+ onChange: (e) => controller.setAdjustmentLayerMaskProps(target.id, { density: Number(e.target.value) / 100 }),
845
+ className: "w-full accent-indigo-500"
846
+ }
847
+ )
848
+ ] }),
849
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("label", { className: "flex flex-col gap-1", children: [
850
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "flex justify-between text-slate-400", children: [
851
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { children: "Mask Feather" }),
852
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "font-mono", children: [
853
+ target.mask.feather ?? 0,
854
+ "px"
855
+ ] })
856
+ ] }),
857
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
858
+ "input",
859
+ {
860
+ type: "range",
861
+ min: 0,
862
+ max: 50,
863
+ step: 0.5,
864
+ "aria-label": "Mask feather",
865
+ value: target.mask.feather ?? 0,
866
+ onChange: (e) => controller.setAdjustmentLayerMaskProps(target.id, { feather: Number(e.target.value) }),
867
+ className: "w-full accent-indigo-500"
868
+ }
869
+ )
870
+ ] })
871
+ ] })
872
+ ] })
873
+ ] });
874
+ };
875
+
876
+ // src/image-editor/ImageEditor.tsx
877
+ var import_jsx_runtime7 = require("react/jsx-runtime");
168
878
  var ImageEditor = ({
169
879
  editor: externalEditor,
170
880
  src,
@@ -179,17 +889,26 @@ var ImageEditor = ({
179
889
  const internalEditor = useImageEditor({ image: src });
180
890
  const editor = externalEditor || internalEditor;
181
891
  const { state, controller } = editor;
182
- const legacyProcess = (0, import_image_editor2.normalizeProcessVersion)(state.adjustments.process) < import_image_editor2.LATEST_PROCESS_VERSION;
183
- const canvasRef = (0, import_react2.useRef)(null);
184
- const containerRef = (0, import_react2.useRef)(null);
185
- const [activeTab, setActiveTab] = (0, import_react2.useState)("crop");
186
- const [activeCropRatio, setActiveCropRatio] = (0, import_react2.useState)(null);
187
- const [inpaintPrompt, setInpaintPrompt] = (0, import_react2.useState)("");
188
- const [inpaintMaskMode, setInpaintMaskMode] = (0, import_react2.useState)("annotations");
189
- const [isRemovingBg, setIsRemovingBg] = (0, import_react2.useState)(false);
190
- const [isInpainting, setIsInpainting] = (0, import_react2.useState)(false);
191
- const [aiError, setAiError] = (0, import_react2.useState)(null);
192
- const [aiSuccess, setAiSuccess] = (0, import_react2.useState)(null);
892
+ const legacyProcess = (0, import_image_editor8.normalizeProcessVersion)(state.adjustments.process) < import_image_editor8.LATEST_PROCESS_VERSION;
893
+ const canvasRef = (0, import_react7.useRef)(null);
894
+ const containerRef = (0, import_react7.useRef)(null);
895
+ const [activeTab, setActiveTab] = (0, import_react7.useState)("crop");
896
+ const [activeCropRatio, setActiveCropRatio] = (0, import_react7.useState)(null);
897
+ const [selectionOptions, setSelectionOptions] = (0, import_react7.useState)(import_image_editor8.DEFAULT_SELECTION_TOOL_OPTIONS);
898
+ const selectionOptionsRef = (0, import_react7.useRef)(selectionOptions);
899
+ selectionOptionsRef.current = selectionOptions;
900
+ const selectionGestureRef = (0, import_react7.useRef)(null);
901
+ if (!selectionGestureRef.current || selectionGestureRef.current.controller !== controller) {
902
+ selectionGestureRef.current = new import_image_editor8.SelectionGesture(controller, () => selectionOptionsRef.current);
903
+ }
904
+ const selectionDraftRef = (0, import_react7.useRef)(null);
905
+ const [selectionError, setSelectionError] = (0, import_react7.useState)(null);
906
+ const [inpaintPrompt, setInpaintPrompt] = (0, import_react7.useState)("");
907
+ const [inpaintMaskMode, setInpaintMaskMode] = (0, import_react7.useState)("annotations");
908
+ const [isRemovingBg, setIsRemovingBg] = (0, import_react7.useState)(false);
909
+ const [isInpainting, setIsInpainting] = (0, import_react7.useState)(false);
910
+ const [aiError, setAiError] = (0, import_react7.useState)(null);
911
+ const [aiSuccess, setAiSuccess] = (0, import_react7.useState)(null);
193
912
  const effectiveOnInpaint = onInpaint || aiHooks?.onInpaint;
194
913
  const effectiveOnRemoveBg = onRemoveBackground || aiHooks?.onRemoveBackground;
195
914
  const handleRemoveBackground = async () => {
@@ -251,18 +970,18 @@ var ImageEditor = ({
251
970
  setIsInpainting(false);
252
971
  }
253
972
  };
254
- const [activeColor, setActiveColor] = (0, import_react2.useState)("#ef4444");
255
- const [strokeWidth, setStrokeWidth] = (0, import_react2.useState)(3);
256
- const [fontSize, setFontSize] = (0, import_react2.useState)(24);
257
- const [textInput, setTextInput] = (0, import_react2.useState)("");
258
- const draftAnnotationRef = (0, import_react2.useRef)(null);
259
- const draftCropRef = (0, import_react2.useRef)(null);
260
- const cropDragInfoRef = (0, import_react2.useRef)(null);
261
- const annotationDragInfoRef = (0, import_react2.useRef)(null);
262
- const isDrawingRef = (0, import_react2.useRef)(false);
263
- const currentPathRef = (0, import_react2.useRef)([]);
264
- const dragStartPointRef = (0, import_react2.useRef)(null);
265
- const renderCanvas = (0, import_react2.useCallback)(
973
+ const [activeColor, setActiveColor] = (0, import_react7.useState)("#ef4444");
974
+ const [strokeWidth, setStrokeWidth] = (0, import_react7.useState)(3);
975
+ const [fontSize, setFontSize] = (0, import_react7.useState)(24);
976
+ const [textInput, setTextInput] = (0, import_react7.useState)("");
977
+ const draftAnnotationRef = (0, import_react7.useRef)(null);
978
+ const draftCropRef = (0, import_react7.useRef)(null);
979
+ const cropDragInfoRef = (0, import_react7.useRef)(null);
980
+ const annotationDragInfoRef = (0, import_react7.useRef)(null);
981
+ const isDrawingRef = (0, import_react7.useRef)(false);
982
+ const currentPathRef = (0, import_react7.useRef)([]);
983
+ const dragStartPointRef = (0, import_react7.useRef)(null);
984
+ const renderCanvas = (0, import_react7.useCallback)(
266
985
  async (draftAnnotation, draftCrop) => {
267
986
  const canvas = canvasRef.current;
268
987
  const container = containerRef.current;
@@ -276,12 +995,13 @@ var ImageEditor = ({
276
995
  showSelectionOverlay: true,
277
996
  interactive: true,
278
997
  draftAnnotation: draftAnnotation !== void 0 ? draftAnnotation : draftAnnotationRef.current,
279
- draftCrop: draftCrop !== void 0 ? draftCrop : draftCropRef.current
998
+ draftCrop: draftCrop !== void 0 ? draftCrop : draftCropRef.current,
999
+ draftSelectionPath: selectionDraftRef.current
280
1000
  });
281
1001
  },
282
1002
  [controller, activeTab]
283
1003
  );
284
- (0, import_react2.useEffect)(() => {
1004
+ (0, import_react7.useEffect)(() => {
285
1005
  const raf = typeof requestAnimationFrame !== "undefined" ? requestAnimationFrame : (cb) => setTimeout(cb, 0);
286
1006
  const caf = typeof cancelAnimationFrame !== "undefined" ? cancelAnimationFrame : (id) => clearTimeout(id);
287
1007
  const animId = raf(() => {
@@ -289,12 +1009,12 @@ var ImageEditor = ({
289
1009
  });
290
1010
  return () => caf(animId);
291
1011
  }, [renderCanvas, state]);
292
- (0, import_react2.useEffect)(() => {
1012
+ (0, import_react7.useEffect)(() => {
293
1013
  if (activeTab === "crop" && editor.state.activeTool !== "crop") {
294
1014
  editor.setTool("crop");
295
1015
  }
296
1016
  }, [activeTab, editor]);
297
- (0, import_react2.useEffect)(() => {
1017
+ (0, import_react7.useEffect)(() => {
298
1018
  const container = containerRef.current;
299
1019
  if (!container) return;
300
1020
  if (typeof ResizeObserver !== "undefined") {
@@ -324,6 +1044,12 @@ var ImageEditor = ({
324
1044
  const canvas = canvasRef.current;
325
1045
  if (!canvas) return;
326
1046
  const canvasPt = getCanvasEventPoint(e);
1047
+ if (activeTab === "select") {
1048
+ const gesture = selectionGestureRef.current;
1049
+ const imgPt = (0, import_image_editor8.canvasToImagePoint)(canvasPt, state, canvas.width, canvas.height, false);
1050
+ void gesture.begin(imgPt, e).then(() => setSelectionError(gesture.error));
1051
+ return;
1052
+ }
327
1053
  if (activeTab === "crop") {
328
1054
  const activeCrop = draftCropRef.current || state.crop || {
329
1055
  x: 0,
@@ -331,8 +1057,8 @@ var ImageEditor = ({
331
1057
  width: state.imageDimensions.width,
332
1058
  height: state.imageDimensions.height
333
1059
  };
334
- const handle = (0, import_image_editor2.hitTestCrop)(canvasPt, state, canvas.width, canvas.height, 14, activeCrop);
335
- const startImgPt = (0, import_image_editor2.canvasToImagePoint)(canvasPt, state, canvas.width, canvas.height, true);
1060
+ const handle = (0, import_image_editor8.hitTestCrop)(canvasPt, state, canvas.width, canvas.height, 14, activeCrop);
1061
+ const startImgPt = (0, import_image_editor8.canvasToImagePoint)(canvasPt, state, canvas.width, canvas.height, true);
336
1062
  if (handle) {
337
1063
  cropDragInfoRef.current = {
338
1064
  handle,
@@ -351,12 +1077,12 @@ var ImageEditor = ({
351
1077
  return;
352
1078
  }
353
1079
  if (activeTab === "annotate") {
354
- const imgPt = (0, import_image_editor2.canvasToImagePoint)(canvasPt, state, canvas.width, canvas.height, false);
1080
+ const imgPt = (0, import_image_editor8.canvasToImagePoint)(canvasPt, state, canvas.width, canvas.height, false);
355
1081
  dragStartPointRef.current = imgPt;
356
1082
  if (state.activeTool === "select") {
357
1083
  for (let i = state.annotations.length - 1; i >= 0; i--) {
358
1084
  const ann = state.annotations[i];
359
- if ((0, import_image_editor2.hitTestAnnotation)(imgPt, ann)) {
1085
+ if ((0, import_image_editor8.hitTestAnnotation)(imgPt, ann)) {
360
1086
  editor.selectAnnotation(ann.id);
361
1087
  annotationDragInfoRef.current = {
362
1088
  annId: ann.id,
@@ -390,10 +1116,18 @@ var ImageEditor = ({
390
1116
  const canvas = canvasRef.current;
391
1117
  if (!canvas) return;
392
1118
  const canvasPt = getCanvasEventPoint(e);
1119
+ if (activeTab === "select") {
1120
+ const gesture = selectionGestureRef.current;
1121
+ if (gesture.active && gesture.move((0, import_image_editor8.canvasToImagePoint)(canvasPt, state, canvas.width, canvas.height, false))) {
1122
+ selectionDraftRef.current = gesture.draftPath;
1123
+ renderCanvas();
1124
+ }
1125
+ return;
1126
+ }
393
1127
  if (activeTab === "crop") {
394
1128
  const drag = cropDragInfoRef.current;
395
1129
  if (drag) {
396
- const currentImgPt = (0, import_image_editor2.canvasToImagePoint)(canvasPt, state, canvas.width, canvas.height, true);
1130
+ const currentImgPt = (0, import_image_editor8.canvasToImagePoint)(canvasPt, state, canvas.width, canvas.height, true);
397
1131
  let newCrop;
398
1132
  if (drag.handle === "new") {
399
1133
  const minX = Math.max(0, Math.min(drag.startImgPt.x, currentImgPt.x));
@@ -407,7 +1141,7 @@ var ImageEditor = ({
407
1141
  height: Math.round(Math.max(20, maxY - minY))
408
1142
  };
409
1143
  } else {
410
- newCrop = (0, import_image_editor2.calculateCropDrag)(
1144
+ newCrop = (0, import_image_editor8.calculateCropDrag)(
411
1145
  drag.startCrop,
412
1146
  drag.handle,
413
1147
  drag.startImgPt,
@@ -427,15 +1161,15 @@ var ImageEditor = ({
427
1161
  width: state.imageDimensions.width,
428
1162
  height: state.imageDimensions.height
429
1163
  };
430
- const handle = (0, import_image_editor2.hitTestCrop)(canvasPt, state, canvas.width, canvas.height, 14, activeCrop);
431
- canvas.style.cursor = (0, import_image_editor2.getCropCursor)(handle, state.transform);
1164
+ const handle = (0, import_image_editor8.hitTestCrop)(canvasPt, state, canvas.width, canvas.height, 14, activeCrop);
1165
+ canvas.style.cursor = (0, import_image_editor8.getCropCursor)(handle, state.transform);
432
1166
  }
433
1167
  return;
434
1168
  }
435
1169
  if (activeTab === "annotate") {
436
1170
  if (annotationDragInfoRef.current) {
437
1171
  const drag = annotationDragInfoRef.current;
438
- const currentImgPt2 = (0, import_image_editor2.canvasToImagePoint)(canvasPt, state, canvas.width, canvas.height, false);
1172
+ const currentImgPt2 = (0, import_image_editor8.canvasToImagePoint)(canvasPt, state, canvas.width, canvas.height, false);
439
1173
  const dx = currentImgPt2.x - drag.startImgPt.x;
440
1174
  const dy = currentImgPt2.y - drag.startImgPt.y;
441
1175
  editor.updateAnnotation(drag.annId, {
@@ -447,10 +1181,10 @@ var ImageEditor = ({
447
1181
  const startPt = dragStartPointRef.current;
448
1182
  if (!startPt) {
449
1183
  if (state.activeTool === "select") {
450
- const imgPt = (0, import_image_editor2.canvasToImagePoint)(canvasPt, state, canvas.width, canvas.height, false);
1184
+ const imgPt = (0, import_image_editor8.canvasToImagePoint)(canvasPt, state, canvas.width, canvas.height, false);
451
1185
  let hovered = false;
452
1186
  for (let i = state.annotations.length - 1; i >= 0; i--) {
453
- if ((0, import_image_editor2.hitTestAnnotation)(imgPt, state.annotations[i])) {
1187
+ if ((0, import_image_editor8.hitTestAnnotation)(imgPt, state.annotations[i])) {
454
1188
  hovered = true;
455
1189
  break;
456
1190
  }
@@ -461,7 +1195,7 @@ var ImageEditor = ({
461
1195
  }
462
1196
  return;
463
1197
  }
464
- const currentImgPt = (0, import_image_editor2.canvasToImagePoint)(canvasPt, state, canvas.width, canvas.height, false);
1198
+ const currentImgPt = (0, import_image_editor8.canvasToImagePoint)(canvasPt, state, canvas.width, canvas.height, false);
465
1199
  if (state.activeTool === "pen" && isDrawingRef.current) {
466
1200
  currentPathRef.current.push(currentImgPt);
467
1201
  const draft = {
@@ -538,6 +1272,15 @@ var ImageEditor = ({
538
1272
  const canvas = canvasRef.current;
539
1273
  if (!canvas) return;
540
1274
  const canvasPt = getCanvasEventPoint(e);
1275
+ if (activeTab === "select") {
1276
+ const gesture = selectionGestureRef.current;
1277
+ if (gesture.active) {
1278
+ gesture.end((0, import_image_editor8.canvasToImagePoint)(canvasPt, state, canvas.width, canvas.height, false), e);
1279
+ selectionDraftRef.current = null;
1280
+ renderCanvas();
1281
+ }
1282
+ return;
1283
+ }
541
1284
  if (activeTab === "crop") {
542
1285
  if (cropDragInfoRef.current) {
543
1286
  if (draftCropRef.current && draftCropRef.current.width >= 20 && draftCropRef.current.height >= 20) {
@@ -556,7 +1299,7 @@ var ImageEditor = ({
556
1299
  }
557
1300
  const startPt = dragStartPointRef.current;
558
1301
  if (startPt) {
559
- const currentImgPt = (0, import_image_editor2.canvasToImagePoint)(canvasPt, state, canvas.width, canvas.height, false);
1302
+ const currentImgPt = (0, import_image_editor8.canvasToImagePoint)(canvasPt, state, canvas.width, canvas.height, false);
560
1303
  if (state.activeTool === "pen" && isDrawingRef.current) {
561
1304
  if (currentPathRef.current.length > 1) {
562
1305
  editor.addAnnotation({
@@ -652,15 +1395,15 @@ var ImageEditor = ({
652
1395
  link.click();
653
1396
  }
654
1397
  };
655
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1398
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
656
1399
  "div",
657
1400
  {
658
1401
  className: `flex flex-col bg-slate-900 border border-slate-800 rounded-xl overflow-hidden shadow-2xl text-slate-100 ${className}`,
659
1402
  style,
660
1403
  children: [
661
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex flex-wrap items-center justify-between px-4 py-3 bg-slate-950/80 border-b border-slate-800 gap-3", children: [
662
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex items-center gap-1 bg-slate-900 p-1 rounded-lg border border-slate-800 text-xs", children: [
663
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1404
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex flex-wrap items-center justify-between px-4 py-3 bg-slate-950/80 border-b border-slate-800 gap-3", children: [
1405
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex items-center gap-1 bg-slate-900 p-1 rounded-lg border border-slate-800 text-xs", children: [
1406
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
664
1407
  "button",
665
1408
  {
666
1409
  type: "button",
@@ -672,7 +1415,7 @@ var ImageEditor = ({
672
1415
  children: "Transform & Crop"
673
1416
  }
674
1417
  ),
675
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1418
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
676
1419
  "button",
677
1420
  {
678
1421
  type: "button",
@@ -684,7 +1427,19 @@ var ImageEditor = ({
684
1427
  children: "Adjustments"
685
1428
  }
686
1429
  ),
687
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1430
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1431
+ "button",
1432
+ {
1433
+ type: "button",
1434
+ onClick: () => {
1435
+ setActiveTab("select");
1436
+ editor.setTool("select");
1437
+ },
1438
+ className: `px-3 py-1.5 rounded-md font-medium transition-colors ${activeTab === "select" ? "bg-indigo-600 text-white shadow" : "text-slate-400 hover:text-slate-200"}`,
1439
+ children: "Select & Mask"
1440
+ }
1441
+ ),
1442
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
688
1443
  "button",
689
1444
  {
690
1445
  type: "button",
@@ -696,7 +1451,7 @@ var ImageEditor = ({
696
1451
  children: "Annotations"
697
1452
  }
698
1453
  ),
699
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1454
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
700
1455
  "button",
701
1456
  {
702
1457
  type: "button",
@@ -708,7 +1463,7 @@ var ImageEditor = ({
708
1463
  children: "Depth Mask"
709
1464
  }
710
1465
  ),
711
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1466
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
712
1467
  "button",
713
1468
  {
714
1469
  type: "button",
@@ -718,14 +1473,14 @@ var ImageEditor = ({
718
1473
  },
719
1474
  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"}`,
720
1475
  children: [
721
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { className: "w-3.5 h-3.5", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M12 2v4M12 18v4M4.93 4.93l2.83 2.83M16.24 16.24l2.83 2.83M2 12h4M18 12h4M4.93 19.07l2.83-2.83M16.24 7.76l2.83-2.83" }) }),
1476
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("svg", { className: "w-3.5 h-3.5", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("path", { d: "M12 2v4M12 18v4M4.93 4.93l2.83 2.83M16.24 16.24l2.83 2.83M2 12h4M18 12h4M4.93 19.07l2.83-2.83M16.24 7.76l2.83-2.83" }) }),
722
1477
  "AI Tools"
723
1478
  ]
724
1479
  }
725
1480
  )
726
1481
  ] }),
727
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex items-center gap-2 text-xs", children: [
728
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1482
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex items-center gap-2 text-xs", children: [
1483
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
729
1484
  "button",
730
1485
  {
731
1486
  type: "button",
@@ -736,7 +1491,7 @@ var ImageEditor = ({
736
1491
  children: "\u21A9"
737
1492
  }
738
1493
  ),
739
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1494
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
740
1495
  "button",
741
1496
  {
742
1497
  type: "button",
@@ -747,7 +1502,7 @@ var ImageEditor = ({
747
1502
  children: "\u21AA"
748
1503
  }
749
1504
  ),
750
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1505
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
751
1506
  "button",
752
1507
  {
753
1508
  type: "button",
@@ -756,8 +1511,8 @@ var ImageEditor = ({
756
1511
  children: "Reset"
757
1512
  }
758
1513
  ),
759
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "h-4 w-px bg-slate-800 mx-1" }),
760
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1514
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "h-4 w-px bg-slate-800 mx-1" }),
1515
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
761
1516
  "button",
762
1517
  {
763
1518
  type: "button",
@@ -767,11 +1522,11 @@ var ImageEditor = ({
767
1522
  children: "-"
768
1523
  }
769
1524
  ),
770
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "font-mono text-slate-400 w-10 text-center", children: [
1525
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { className: "font-mono text-slate-400 w-10 text-center", children: [
771
1526
  Math.round(state.zoom * 100),
772
1527
  "%"
773
1528
  ] }),
774
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1529
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
775
1530
  "button",
776
1531
  {
777
1532
  type: "button",
@@ -781,8 +1536,8 @@ var ImageEditor = ({
781
1536
  children: "+"
782
1537
  }
783
1538
  ),
784
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "h-4 w-px bg-slate-800 mx-1" }),
785
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1539
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "h-4 w-px bg-slate-800 mx-1" }),
1540
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
786
1541
  "button",
787
1542
  {
788
1543
  type: "button",
@@ -793,7 +1548,7 @@ var ImageEditor = ({
793
1548
  children: "Hold Compare"
794
1549
  }
795
1550
  ),
796
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1551
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
797
1552
  "button",
798
1553
  {
799
1554
  type: "button",
@@ -804,13 +1559,13 @@ var ImageEditor = ({
804
1559
  )
805
1560
  ] })
806
1561
  ] }),
807
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex flex-col md:flex-row flex-1 min-h-[520px] md:h-[560px]", children: [
808
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1562
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex flex-col md:flex-row flex-1 min-h-[520px] md:h-[560px]", children: [
1563
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
809
1564
  "div",
810
1565
  {
811
1566
  ref: containerRef,
812
1567
  className: "relative flex-1 h-[360px] md:h-full min-h-[320px] bg-slate-950 overflow-hidden select-none",
813
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1568
+ children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
814
1569
  "canvas",
815
1570
  {
816
1571
  ref: canvasRef,
@@ -823,11 +1578,11 @@ var ImageEditor = ({
823
1578
  )
824
1579
  }
825
1580
  ),
826
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "w-full md:w-80 bg-slate-900/90 border-t md:border-t-0 md:border-l border-slate-800 p-4 overflow-y-auto h-auto md:h-full md:max-h-[560px]", children: [
827
- activeTab === "crop" && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex flex-col gap-4 text-xs", children: [
828
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Aspect Ratio Presets" }),
829
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "grid grid-cols-3 gap-2", children: [
830
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1581
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "w-full md:w-80 bg-slate-900/90 border-t md:border-t-0 md:border-l border-slate-800 p-4 overflow-y-auto h-auto md:h-full md:max-h-[560px]", children: [
1582
+ activeTab === "crop" && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex flex-col gap-4 text-xs", children: [
1583
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Aspect Ratio Presets" }),
1584
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "grid grid-cols-3 gap-2", children: [
1585
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
831
1586
  "button",
832
1587
  {
833
1588
  type: "button",
@@ -839,7 +1594,7 @@ var ImageEditor = ({
839
1594
  children: "Freeform"
840
1595
  }
841
1596
  ),
842
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1597
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
843
1598
  "button",
844
1599
  {
845
1600
  type: "button",
@@ -851,7 +1606,7 @@ var ImageEditor = ({
851
1606
  children: "1:1 Square"
852
1607
  }
853
1608
  ),
854
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1609
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
855
1610
  "button",
856
1611
  {
857
1612
  type: "button",
@@ -863,7 +1618,7 @@ var ImageEditor = ({
863
1618
  children: "4:3 Standard"
864
1619
  }
865
1620
  ),
866
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1621
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
867
1622
  "button",
868
1623
  {
869
1624
  type: "button",
@@ -875,7 +1630,7 @@ var ImageEditor = ({
875
1630
  children: "16:9 Wide"
876
1631
  }
877
1632
  ),
878
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1633
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
879
1634
  "button",
880
1635
  {
881
1636
  type: "button",
@@ -887,7 +1642,7 @@ var ImageEditor = ({
887
1642
  children: "3:2 Photo"
888
1643
  }
889
1644
  ),
890
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1645
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
891
1646
  "button",
892
1647
  {
893
1648
  type: "button",
@@ -900,10 +1655,10 @@ var ImageEditor = ({
900
1655
  }
901
1656
  )
902
1657
  ] }),
903
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "h-px bg-slate-800 my-1" }),
904
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Orientation & Flips" }),
905
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "grid grid-cols-2 gap-2", children: [
906
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1658
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "h-px bg-slate-800 my-1" }),
1659
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Orientation & Flips" }),
1660
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "grid grid-cols-2 gap-2", children: [
1661
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
907
1662
  "button",
908
1663
  {
909
1664
  type: "button",
@@ -912,7 +1667,7 @@ var ImageEditor = ({
912
1667
  children: "Rotate -90\xB0"
913
1668
  }
914
1669
  ),
915
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1670
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
916
1671
  "button",
917
1672
  {
918
1673
  type: "button",
@@ -921,7 +1676,7 @@ var ImageEditor = ({
921
1676
  children: "Rotate +90\xB0"
922
1677
  }
923
1678
  ),
924
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1679
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
925
1680
  "button",
926
1681
  {
927
1682
  type: "button",
@@ -930,7 +1685,7 @@ var ImageEditor = ({
930
1685
  children: "Flip Horizontal"
931
1686
  }
932
1687
  ),
933
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1688
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
934
1689
  "button",
935
1690
  {
936
1691
  type: "button",
@@ -941,10 +1696,10 @@ var ImageEditor = ({
941
1696
  )
942
1697
  ] })
943
1698
  ] }),
944
- activeTab === "adjust" && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex flex-col gap-3 text-xs", children: [
945
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex items-center justify-between", children: [
946
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Color Adjustments" }),
947
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1699
+ activeTab === "adjust" && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex flex-col gap-3 text-xs", children: [
1700
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex items-center justify-between", children: [
1701
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Color Adjustments" }),
1702
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
948
1703
  "button",
949
1704
  {
950
1705
  type: "button",
@@ -954,174 +1709,49 @@ var ImageEditor = ({
954
1709
  }
955
1710
  )
956
1711
  ] }),
957
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
958
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex justify-between text-slate-400 mb-1", children: [
959
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "Brightness" }),
960
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "font-mono", children: state.adjustments.brightness })
961
- ] }),
962
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
963
- "input",
964
- {
965
- type: "range",
966
- min: "-100",
967
- max: "100",
968
- value: state.adjustments.brightness,
969
- onChange: (e) => editor.setAdjustments({ brightness: Number(e.target.value) }),
970
- className: "w-full accent-indigo-500"
971
- }
972
- )
973
- ] }),
974
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
975
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex justify-between text-slate-400 mb-1", children: [
976
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "Contrast" }),
977
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "font-mono", children: state.adjustments.contrast })
978
- ] }),
979
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
980
- "input",
981
- {
982
- type: "range",
983
- min: "-100",
984
- max: "100",
985
- value: state.adjustments.contrast,
986
- onChange: (e) => editor.setAdjustments({ contrast: Number(e.target.value) }),
987
- className: "w-full accent-indigo-500"
988
- }
989
- )
990
- ] }),
991
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
992
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex justify-between text-slate-400 mb-1", children: [
993
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "Saturation" }),
994
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "font-mono", children: state.adjustments.saturation })
995
- ] }),
996
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
997
- "input",
998
- {
999
- type: "range",
1000
- min: "-100",
1001
- max: "100",
1002
- value: state.adjustments.saturation,
1003
- onChange: (e) => editor.setAdjustments({ saturation: Number(e.target.value) }),
1004
- className: "w-full accent-indigo-500"
1005
- }
1006
- )
1007
- ] }),
1008
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
1009
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex justify-between text-slate-400 mb-1", children: [
1010
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "Exposure" }),
1011
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "font-mono", children: state.adjustments.exposure })
1012
- ] }),
1013
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1014
- "input",
1712
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Histogram, { histogram: editor.histogram }),
1713
+ import_image_editor8.IMAGE_ADJUSTMENT_CONTROLS.map((spec) => /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_react7.default.Fragment, { children: [
1714
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1715
+ ControlSlider,
1015
1716
  {
1016
- type: "range",
1017
- min: "-100",
1018
- max: "100",
1019
- value: state.adjustments.exposure,
1020
- onChange: (e) => editor.setAdjustments({ exposure: Number(e.target.value) }),
1021
- className: "w-full accent-indigo-500"
1717
+ spec,
1718
+ value: (0, import_image_editor8.readAdjustmentControls)(state.adjustments)[spec.id],
1719
+ disabled: spec.requiresProcess !== void 0 && legacyProcess,
1720
+ onChange: (v) => editor.setAdjustments({ [spec.id]: v })
1022
1721
  }
1023
- )
1024
- ] }),
1025
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
1026
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex justify-between text-slate-400 mb-1", children: [
1027
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "Warmth / Temperature" }),
1028
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "font-mono", children: state.adjustments.temperature })
1029
- ] }),
1030
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1031
- "input",
1032
- {
1033
- type: "range",
1034
- min: "-100",
1035
- max: "100",
1036
- value: state.adjustments.temperature,
1037
- onChange: (e) => editor.setAdjustments({ temperature: Number(e.target.value) }),
1038
- className: "w-full accent-indigo-500"
1039
- }
1040
- )
1041
- ] }),
1042
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
1043
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex justify-between text-slate-400 mb-1", children: [
1044
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "Tint" }),
1045
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "font-mono", children: state.adjustments.tint ?? 0 })
1046
- ] }),
1047
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1048
- "input",
1722
+ ),
1723
+ spec.id === "tint" && legacyProcess && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
1724
+ "div",
1049
1725
  {
1050
- type: "range",
1051
- min: "-100",
1052
- max: "100",
1053
- "aria-label": "Tint",
1054
- disabled: legacyProcess,
1055
- value: state.adjustments.tint ?? 0,
1056
- onChange: (e) => editor.setAdjustments({ tint: Number(e.target.value) }),
1057
- className: "w-full accent-indigo-500 disabled:opacity-40"
1726
+ className: "flex items-center justify-between gap-2 rounded border border-amber-500/30 bg-amber-500/10 px-2 py-1.5 text-[10px] text-amber-200",
1727
+ "data-testid": "image-editor-process-notice",
1728
+ children: [
1729
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { children: "Legacy rendering (process 1). Tint needs the current process." }),
1730
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1731
+ "button",
1732
+ {
1733
+ type: "button",
1734
+ onClick: () => editor.upgradeProcess(),
1735
+ className: "shrink-0 rounded bg-amber-500/20 px-2 py-0.5 hover:bg-amber-500/30",
1736
+ children: "Update"
1737
+ }
1738
+ )
1739
+ ]
1058
1740
  }
1059
1741
  )
1060
- ] }),
1061
- legacyProcess && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1062
- "div",
1742
+ ] }, spec.id)),
1743
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "border-t border-slate-700/60 pt-3", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1744
+ AdjustmentLayersPanel,
1063
1745
  {
1064
- 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",
1065
- "data-testid": "image-editor-process-notice",
1066
- children: [
1067
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "Legacy rendering (process 1). Tint needs the current process." }),
1068
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1069
- "button",
1070
- {
1071
- type: "button",
1072
- onClick: () => editor.upgradeProcess(),
1073
- className: "shrink-0 rounded bg-amber-500/20 px-2 py-0.5 hover:bg-amber-500/30",
1074
- children: "Update"
1075
- }
1076
- )
1077
- ]
1746
+ controller,
1747
+ layers: state.adjustments.layers,
1748
+ histogram: editor.histogram
1078
1749
  }
1079
- ),
1080
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
1081
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex justify-between text-slate-400 mb-1", children: [
1082
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "Blur" }),
1083
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "font-mono", children: [
1084
- state.adjustments.blur,
1085
- "px"
1086
- ] })
1087
- ] }),
1088
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1089
- "input",
1090
- {
1091
- type: "range",
1092
- min: "0",
1093
- max: "30",
1094
- value: state.adjustments.blur,
1095
- onChange: (e) => editor.setAdjustments({ blur: Number(e.target.value) }),
1096
- className: "w-full accent-indigo-500"
1097
- }
1098
- )
1099
- ] }),
1100
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
1101
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex justify-between text-slate-400 mb-1", children: [
1102
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "Opacity" }),
1103
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "font-mono", children: [
1104
- Math.round(state.adjustments.opacity * 100),
1105
- "%"
1106
- ] })
1107
- ] }),
1108
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1109
- "input",
1110
- {
1111
- type: "range",
1112
- min: "0",
1113
- max: "1",
1114
- step: "0.05",
1115
- value: state.adjustments.opacity,
1116
- onChange: (e) => editor.setAdjustments({ opacity: Number(e.target.value) }),
1117
- className: "w-full accent-indigo-500"
1118
- }
1119
- )
1120
- ] })
1750
+ ) })
1121
1751
  ] }),
1122
- activeTab === "annotate" && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex flex-col gap-3 text-xs", children: [
1123
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Draw & Annotate" }),
1124
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "grid grid-cols-3 gap-1.5", children: ["pen", "rect", "circle", "arrow", "line"].map((t) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1752
+ activeTab === "annotate" && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex flex-col gap-3 text-xs", children: [
1753
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Draw & Annotate" }),
1754
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "grid grid-cols-3 gap-1.5", children: ["pen", "rect", "circle", "arrow", "line"].map((t) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1125
1755
  "button",
1126
1756
  {
1127
1757
  type: "button",
@@ -1131,9 +1761,9 @@ var ImageEditor = ({
1131
1761
  },
1132
1762
  t
1133
1763
  )) }),
1134
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex items-center gap-2 mt-2", children: [
1135
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { className: "text-slate-400", children: "Color:" }),
1136
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1764
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex items-center gap-2 mt-2", children: [
1765
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("label", { className: "text-slate-400", children: "Color:" }),
1766
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1137
1767
  "input",
1138
1768
  {
1139
1769
  type: "color",
@@ -1142,15 +1772,15 @@ var ImageEditor = ({
1142
1772
  className: "w-8 h-8 rounded border border-slate-700 cursor-pointer bg-transparent"
1143
1773
  }
1144
1774
  ),
1145
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex-1 ml-2", children: [
1146
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex justify-between text-slate-400 mb-0.5", children: [
1147
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "Width:" }),
1148
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "font-mono", children: [
1775
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex-1 ml-2", children: [
1776
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex justify-between text-slate-400 mb-0.5", children: [
1777
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { children: "Width:" }),
1778
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { className: "font-mono", children: [
1149
1779
  strokeWidth,
1150
1780
  "px"
1151
1781
  ] })
1152
1782
  ] }),
1153
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1783
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1154
1784
  "input",
1155
1785
  {
1156
1786
  type: "range",
@@ -1163,10 +1793,10 @@ var ImageEditor = ({
1163
1793
  )
1164
1794
  ] })
1165
1795
  ] }),
1166
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "h-px bg-slate-800 my-1" }),
1167
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Add Text" }),
1168
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex gap-2", children: [
1169
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1796
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "h-px bg-slate-800 my-1" }),
1797
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Add Text" }),
1798
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex gap-2", children: [
1799
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1170
1800
  "input",
1171
1801
  {
1172
1802
  type: "text",
@@ -1177,7 +1807,7 @@ var ImageEditor = ({
1177
1807
  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"
1178
1808
  }
1179
1809
  ),
1180
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1810
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1181
1811
  "button",
1182
1812
  {
1183
1813
  type: "button",
@@ -1187,14 +1817,14 @@ var ImageEditor = ({
1187
1817
  }
1188
1818
  )
1189
1819
  ] }),
1190
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex items-center justify-between text-slate-400", children: [
1191
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "Font Size:" }),
1192
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "font-mono", children: [
1820
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex items-center justify-between text-slate-400", children: [
1821
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { children: "Font Size:" }),
1822
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { className: "font-mono", children: [
1193
1823
  fontSize,
1194
1824
  "px"
1195
1825
  ] })
1196
1826
  ] }),
1197
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1827
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1198
1828
  "input",
1199
1829
  {
1200
1830
  type: "range",
@@ -1205,14 +1835,14 @@ var ImageEditor = ({
1205
1835
  className: "w-full accent-indigo-500"
1206
1836
  }
1207
1837
  ),
1208
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "h-px bg-slate-800 my-1" }),
1209
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex items-center justify-between", children: [
1210
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "text-slate-400", children: [
1838
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "h-px bg-slate-800 my-1" }),
1839
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex items-center justify-between", children: [
1840
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { className: "text-slate-400", children: [
1211
1841
  "Layers (",
1212
1842
  state.annotations.length,
1213
1843
  ")"
1214
1844
  ] }),
1215
- state.selectedAnnotationId && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1845
+ state.selectedAnnotationId && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1216
1846
  "button",
1217
1847
  {
1218
1848
  type: "button",
@@ -1221,7 +1851,7 @@ var ImageEditor = ({
1221
1851
  children: "Delete Selected"
1222
1852
  }
1223
1853
  ),
1224
- state.annotations.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1854
+ state.annotations.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1225
1855
  "button",
1226
1856
  {
1227
1857
  type: "button",
@@ -1232,10 +1862,10 @@ var ImageEditor = ({
1232
1862
  )
1233
1863
  ] })
1234
1864
  ] }),
1235
- activeTab === "depth" && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex flex-col gap-3 text-xs", children: [
1236
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex items-center justify-between", children: [
1237
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Depth Masking" }),
1238
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1865
+ activeTab === "depth" && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex flex-col gap-3 text-xs", children: [
1866
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex items-center justify-between", children: [
1867
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Depth Masking" }),
1868
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1239
1869
  "button",
1240
1870
  {
1241
1871
  type: "button",
@@ -1245,8 +1875,8 @@ var ImageEditor = ({
1245
1875
  }
1246
1876
  )
1247
1877
  ] }),
1248
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { className: "flex items-center gap-2 cursor-pointer text-slate-300", children: [
1249
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1878
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("label", { className: "flex items-center gap-2 cursor-pointer text-slate-300", children: [
1879
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1250
1880
  "input",
1251
1881
  {
1252
1882
  type: "checkbox",
@@ -1255,12 +1885,12 @@ var ImageEditor = ({
1255
1885
  className: "rounded accent-indigo-500"
1256
1886
  }
1257
1887
  ),
1258
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "Enable Depth / Alpha Mask" })
1888
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { children: "Enable Depth / Alpha Mask" })
1259
1889
  ] }),
1260
- state.depthMask.enabled && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
1261
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
1262
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { className: "text-slate-400 block mb-1", children: "Mask Image URL:" }),
1263
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1890
+ state.depthMask.enabled && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
1891
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { children: [
1892
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("label", { className: "text-slate-400 block mb-1", children: "Mask Image URL:" }),
1893
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1264
1894
  "input",
1265
1895
  {
1266
1896
  type: "text",
@@ -1271,8 +1901,8 @@ var ImageEditor = ({
1271
1901
  }
1272
1902
  )
1273
1903
  ] }),
1274
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex gap-2", children: [
1275
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1904
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex gap-2", children: [
1905
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1276
1906
  "button",
1277
1907
  {
1278
1908
  type: "button",
@@ -1281,7 +1911,7 @@ var ImageEditor = ({
1281
1911
  children: "Threshold Mode"
1282
1912
  }
1283
1913
  ),
1284
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1914
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1285
1915
  "button",
1286
1916
  {
1287
1917
  type: "button",
@@ -1291,12 +1921,12 @@ var ImageEditor = ({
1291
1921
  }
1292
1922
  )
1293
1923
  ] }),
1294
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
1295
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex justify-between text-slate-400 mb-1", children: [
1296
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "Depth Start (Lower):" }),
1297
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "font-mono", children: state.depthMask.depthRange[0] })
1924
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { children: [
1925
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex justify-between text-slate-400 mb-1", children: [
1926
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { children: "Depth Start (Lower):" }),
1927
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "font-mono", children: state.depthMask.depthRange[0] })
1298
1928
  ] }),
1299
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1929
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1300
1930
  "input",
1301
1931
  {
1302
1932
  type: "range",
@@ -1310,12 +1940,12 @@ var ImageEditor = ({
1310
1940
  }
1311
1941
  )
1312
1942
  ] }),
1313
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
1314
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex justify-between text-slate-400 mb-1", children: [
1315
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "Depth Stop (Upper):" }),
1316
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "font-mono", children: state.depthMask.depthRange[1] })
1943
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { children: [
1944
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex justify-between text-slate-400 mb-1", children: [
1945
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { children: "Depth Stop (Upper):" }),
1946
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "font-mono", children: state.depthMask.depthRange[1] })
1317
1947
  ] }),
1318
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1948
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1319
1949
  "input",
1320
1950
  {
1321
1951
  type: "range",
@@ -1329,12 +1959,12 @@ var ImageEditor = ({
1329
1959
  }
1330
1960
  )
1331
1961
  ] }),
1332
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
1333
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex justify-between text-slate-400 mb-1", children: [
1334
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "Softness:" }),
1335
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "font-mono", children: state.depthMask.softness })
1962
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { children: [
1963
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex justify-between text-slate-400 mb-1", children: [
1964
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { children: "Softness:" }),
1965
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "font-mono", children: state.depthMask.softness })
1336
1966
  ] }),
1337
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1967
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1338
1968
  "input",
1339
1969
  {
1340
1970
  type: "range",
@@ -1346,8 +1976,8 @@ var ImageEditor = ({
1346
1976
  }
1347
1977
  )
1348
1978
  ] }),
1349
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { className: "flex items-center gap-2 cursor-pointer text-slate-300 mt-1", children: [
1350
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1979
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("label", { className: "flex items-center gap-2 cursor-pointer text-slate-300 mt-1", children: [
1980
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1351
1981
  "input",
1352
1982
  {
1353
1983
  type: "checkbox",
@@ -1356,52 +1986,66 @@ var ImageEditor = ({
1356
1986
  className: "rounded accent-indigo-500"
1357
1987
  }
1358
1988
  ),
1359
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "Invert Mask" })
1989
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { children: "Invert Mask" })
1360
1990
  ] })
1361
1991
  ] })
1362
1992
  ] }),
1363
- activeTab === "ai" && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex flex-col gap-4 text-xs", children: [
1364
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "p-3 rounded-lg bg-slate-950/60 border border-slate-800 flex flex-col gap-2.5", children: [
1365
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex items-center justify-between", children: [
1366
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px] flex items-center gap-1.5", children: [
1367
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { className: "w-3.5 h-3.5 text-indigo-400", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M6 3v12M18 9a3 3 0 1 0 0-6 3 3 0 0 0 0 6zM6 21a3 3 0 1 0 0-6 3 3 0 0 0 0 6z" }) }),
1993
+ activeTab === "select" && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex flex-col gap-4 text-xs", children: [
1994
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1995
+ SelectionOptionsBar,
1996
+ {
1997
+ controller,
1998
+ options: selectionOptions,
1999
+ onOptionsChange: setSelectionOptions,
2000
+ selection: state.selection ?? null,
2001
+ quickMask: Boolean(state.quickMask)
2002
+ }
2003
+ ),
2004
+ selectionError && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("p", { role: "alert", className: "text-rose-400", children: selectionError }),
2005
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(RefineEdgePanel, { controller, selection: state.selection ?? null, layers: state.adjustments.layers })
2006
+ ] }),
2007
+ activeTab === "ai" && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex flex-col gap-4 text-xs", children: [
2008
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "p-3 rounded-lg bg-slate-950/60 border border-slate-800 flex flex-col gap-2.5", children: [
2009
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex items-center justify-between", children: [
2010
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px] flex items-center gap-1.5", children: [
2011
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("svg", { className: "w-3.5 h-3.5 text-indigo-400", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("path", { d: "M6 3v12M18 9a3 3 0 1 0 0-6 3 3 0 0 0 0 6zM6 21a3 3 0 1 0 0-6 3 3 0 0 0 0 6z" }) }),
1368
2012
  "Background Removal"
1369
2013
  ] }),
1370
- isRemovingBg && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "inline-flex items-center px-1.5 py-0.5 rounded text-[10px] font-medium bg-indigo-900/60 text-indigo-300 animate-pulse", children: "Removing..." })
2014
+ isRemovingBg && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "inline-flex items-center px-1.5 py-0.5 rounded text-[10px] font-medium bg-indigo-900/60 text-indigo-300 animate-pulse", children: "Removing..." })
1371
2015
  ] }),
1372
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "text-slate-400 text-[11px] leading-relaxed", children: "Isolate foreground subject with AI alpha transparency segmentation." }),
1373
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2016
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("p", { className: "text-slate-400 text-[11px] leading-relaxed", children: "Isolate foreground subject with AI alpha transparency segmentation." }),
2017
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1374
2018
  "button",
1375
2019
  {
1376
2020
  type: "button",
1377
2021
  onClick: handleRemoveBackground,
1378
2022
  disabled: isRemovingBg || isInpainting,
1379
2023
  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",
1380
- children: isRemovingBg ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
1381
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "w-3.5 h-3.5 border-2 border-white/30 border-t-white rounded-full animate-spin" }),
2024
+ children: isRemovingBg ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
2025
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "w-3.5 h-3.5 border-2 border-white/30 border-t-white rounded-full animate-spin" }),
1382
2026
  "Removing Background..."
1383
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
1384
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { className: "w-3.5 h-3.5", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M12 2v4M12 18v4M4.93 4.93l2.83 2.83M16.24 16.24l2.83 2.83M2 12h4M18 12h4M4.93 19.07l2.83-2.83M16.24 7.76l2.83-2.83" }) }),
2027
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
2028
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("svg", { className: "w-3.5 h-3.5", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("path", { d: "M12 2v4M12 18v4M4.93 4.93l2.83 2.83M16.24 16.24l2.83 2.83M2 12h4M18 12h4M4.93 19.07l2.83-2.83M16.24 7.76l2.83-2.83" }) }),
1385
2029
  "Remove Background"
1386
2030
  ] })
1387
2031
  }
1388
2032
  )
1389
2033
  ] }),
1390
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "p-3 rounded-lg bg-slate-950/60 border border-slate-800 flex flex-col gap-2.5", children: [
1391
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex items-center justify-between", children: [
1392
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px] flex items-center gap-1.5", children: [
1393
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { className: "w-3.5 h-3.5 text-purple-400", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
1394
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "m21.64 3.64-1.28-1.28a1.2 1.2 0 0 0-1.7 0l-9.72 9.72-1.94 4.86 4.86-1.94 9.72-9.72a1.2 1.2 0 0 0 .06-1.64z" }),
1395
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "m14 7 3 3" })
2034
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "p-3 rounded-lg bg-slate-950/60 border border-slate-800 flex flex-col gap-2.5", children: [
2035
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex items-center justify-between", children: [
2036
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px] flex items-center gap-1.5", children: [
2037
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("svg", { className: "w-3.5 h-3.5 text-purple-400", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
2038
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("path", { d: "m21.64 3.64-1.28-1.28a1.2 1.2 0 0 0-1.7 0l-9.72 9.72-1.94 4.86 4.86-1.94 9.72-9.72a1.2 1.2 0 0 0 .06-1.64z" }),
2039
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("path", { d: "m14 7 3 3" })
1396
2040
  ] }),
1397
2041
  "Generative Inpainting"
1398
2042
  ] }),
1399
- isInpainting && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "inline-flex items-center px-1.5 py-0.5 rounded text-[10px] font-medium bg-purple-900/60 text-purple-300 animate-pulse", children: "Inpainting..." })
2043
+ isInpainting && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "inline-flex items-center px-1.5 py-0.5 rounded text-[10px] font-medium bg-purple-900/60 text-purple-300 animate-pulse", children: "Inpainting..." })
1400
2044
  ] }),
1401
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "text-slate-400 text-[11px] leading-relaxed", children: "Replace, modify, or fill selected regions using prompt-guided generative inpainting." }),
1402
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex flex-col gap-1", children: [
1403
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { htmlFor: "ai-inpaint-prompt-react", className: "text-slate-300 text-[11px] font-medium", children: "Prompt" }),
1404
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2045
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("p", { className: "text-slate-400 text-[11px] leading-relaxed", children: "Replace, modify, or fill selected regions using prompt-guided generative inpainting." }),
2046
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex flex-col gap-1", children: [
2047
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("label", { htmlFor: "ai-inpaint-prompt-react", className: "text-slate-300 text-[11px] font-medium", children: "Prompt" }),
2048
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1405
2049
  "textarea",
1406
2050
  {
1407
2051
  id: "ai-inpaint-prompt-react",
@@ -1413,10 +2057,10 @@ var ImageEditor = ({
1413
2057
  }
1414
2058
  )
1415
2059
  ] }),
1416
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex flex-col gap-1", children: [
1417
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "text-slate-300 text-[11px] font-medium", children: "Target Mask Region" }),
1418
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "grid grid-cols-2 gap-1.5", children: [
1419
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
2060
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex flex-col gap-1", children: [
2061
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "text-slate-300 text-[11px] font-medium", children: "Target Mask Region" }),
2062
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "grid grid-cols-2 gap-1.5", children: [
2063
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
1420
2064
  "button",
1421
2065
  {
1422
2066
  type: "button",
@@ -1429,7 +2073,7 @@ var ImageEditor = ({
1429
2073
  ]
1430
2074
  }
1431
2075
  ),
1432
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
2076
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
1433
2077
  "button",
1434
2078
  {
1435
2079
  type: "button",
@@ -1444,25 +2088,25 @@ var ImageEditor = ({
1444
2088
  )
1445
2089
  ] })
1446
2090
  ] }),
1447
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2091
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1448
2092
  "button",
1449
2093
  {
1450
2094
  type: "button",
1451
2095
  onClick: handleInpaint,
1452
2096
  disabled: isInpainting || isRemovingBg || !inpaintPrompt.trim(),
1453
2097
  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",
1454
- children: isInpainting ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
1455
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "w-3.5 h-3.5 border-2 border-white/30 border-t-white rounded-full animate-spin" }),
2098
+ children: isInpainting ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
2099
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "w-3.5 h-3.5 border-2 border-white/30 border-t-white rounded-full animate-spin" }),
1456
2100
  "Generating Inpaint..."
1457
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
1458
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { className: "w-3.5 h-3.5", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M12 2v4M12 18v4M4.93 4.93l2.83 2.83M16.24 16.24l2.83 2.83M2 12h4M18 12h4M4.93 19.07l2.83-2.83M16.24 7.76l2.83-2.83" }) }),
2101
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
2102
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("svg", { className: "w-3.5 h-3.5", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("path", { d: "M12 2v4M12 18v4M4.93 4.93l2.83 2.83M16.24 16.24l2.83 2.83M2 12h4M18 12h4M4.93 19.07l2.83-2.83M16.24 7.76l2.83-2.83" }) }),
1459
2103
  "Apply Inpainting"
1460
2104
  ] })
1461
2105
  }
1462
2106
  )
1463
2107
  ] }),
1464
- aiError && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "p-2.5 rounded-lg bg-rose-950/60 border border-rose-800 text-rose-300 text-[11px]", children: aiError }),
1465
- aiSuccess && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "p-2.5 rounded-lg bg-emerald-950/60 border border-emerald-800 text-emerald-300 text-[11px]", children: aiSuccess })
2108
+ aiError && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "p-2.5 rounded-lg bg-rose-950/60 border border-rose-800 text-rose-300 text-[11px]", children: aiError }),
2109
+ aiSuccess && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "p-2.5 rounded-lg bg-emerald-950/60 border border-emerald-800 text-emerald-300 text-[11px]", children: aiSuccess })
1466
2110
  ] })
1467
2111
  ] })
1468
2112
  ] })
@@ -1472,7 +2116,14 @@ var ImageEditor = ({
1472
2116
  };
1473
2117
  // Annotate the CommonJS export names for ESM import in node:
1474
2118
  0 && (module.exports = {
2119
+ AdjustmentLayersPanel,
2120
+ ControlSlider,
2121
+ CurvesEditor,
2122
+ Histogram,
1475
2123
  ImageEditor,
1476
- useImageEditor
2124
+ RefineEdgePanel,
2125
+ SelectionOptionsBar,
2126
+ useImageEditor,
2127
+ useImageHistogram
1477
2128
  });
1478
2129
  //# sourceMappingURL=index.cjs.map