@pixerate/editor-react 0.15.0 → 0.16.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 (46) hide show
  1. package/README.md +4 -0
  2. package/dist/canvas/index.cjs +219 -118
  3. package/dist/canvas/index.cjs.map +1 -1
  4. package/dist/canvas/index.d.cts +59 -3
  5. package/dist/canvas/index.d.ts +59 -3
  6. package/dist/canvas/index.js +103 -0
  7. package/dist/canvas/index.js.map +1 -1
  8. package/dist/chunk-7SPRQZ6H.js +216 -0
  9. package/dist/chunk-7SPRQZ6H.js.map +1 -0
  10. package/dist/{chunk-QVQYYHJ5.js → chunk-FYIPXHAE.js} +21 -13
  11. package/dist/chunk-FYIPXHAE.js.map +1 -0
  12. package/dist/{chunk-LPFZMZ3L.js → chunk-XTFR3IMK.js} +676 -322
  13. package/dist/chunk-XTFR3IMK.js.map +1 -0
  14. package/dist/chunk-XWFU2CSG.js +167 -0
  15. package/dist/chunk-XWFU2CSG.js.map +1 -0
  16. package/dist/generation/index.cjs +235 -0
  17. package/dist/generation/index.cjs.map +1 -0
  18. package/dist/generation/index.d.cts +60 -0
  19. package/dist/generation/index.d.ts +60 -0
  20. package/dist/generation/index.js +13 -0
  21. package/dist/generation/index.js.map +1 -0
  22. package/dist/image-editor/index.cjs +684 -336
  23. package/dist/image-editor/index.cjs.map +1 -1
  24. package/dist/image-editor/index.d.cts +60 -2
  25. package/dist/image-editor/index.d.ts +60 -2
  26. package/dist/image-editor/index.js +13 -3
  27. package/dist/index.cjs +1881 -648
  28. package/dist/index.cjs.map +1 -1
  29. package/dist/index.d.cts +143 -4
  30. package/dist/index.d.ts +143 -4
  31. package/dist/index.js +626 -73
  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 +17 -10
  40. package/dist/spreadsheet/index.cjs.map +1 -1
  41. package/dist/spreadsheet/index.d.cts +2 -0
  42. package/dist/spreadsheet/index.d.ts +2 -0
  43. package/dist/spreadsheet/index.js +1 -1
  44. package/package.json +18 -2
  45. package/dist/chunk-LPFZMZ3L.js.map +0 -1
  46. 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,26 @@ 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
+ useImageEditor: () => useImageEditor,
39
+ useImageHistogram: () => useImageHistogram
25
40
  });
26
41
  module.exports = __toCommonJS(image_editor_exports);
27
42
 
@@ -36,6 +51,8 @@ function useImageEditor(options = {}) {
36
51
  const controller = controllerRef.current;
37
52
  const [state, setState] = (0, import_react.useState)(() => controller.getState());
38
53
  const [historyTicks, setHistoryTicks] = (0, import_react.useState)(0);
54
+ const [histogram, setHistogram] = (0, import_react.useState)(() => controller.getHistogram());
55
+ (0, import_react.useEffect)(() => controller.subscribeHistogram(setHistogram), [controller]);
39
56
  (0, import_react.useEffect)(() => {
40
57
  const unsub = controller.subscribe((next) => {
41
58
  setState(next);
@@ -68,6 +85,20 @@ function useImageEditor(options = {}) {
68
85
  );
69
86
  const resetAdjustments = (0, import_react.useCallback)(() => controller.resetAdjustments(), [controller]);
70
87
  const upgradeProcess = (0, import_react.useCallback)(() => controller.upgradeProcess(), [controller]);
88
+ const addAdjustmentLayer = (0, import_react.useCallback)(
89
+ (init, index) => controller.addAdjustmentLayer(init, index),
90
+ [controller]
91
+ );
92
+ const updateAdjustmentLayer = (0, import_react.useCallback)(
93
+ (id, update, commitToHistory = true) => controller.updateAdjustmentLayer(id, update, commitToHistory),
94
+ [controller]
95
+ );
96
+ const removeAdjustmentLayer = (0, import_react.useCallback)((id) => controller.removeAdjustmentLayer(id), [controller]);
97
+ const moveAdjustmentLayer = (0, import_react.useCallback)((id, to) => controller.moveAdjustmentLayer(id, to), [controller]);
98
+ const toggleAdjustmentLayer = (0, import_react.useCallback)(
99
+ (id, enabled) => controller.toggleAdjustmentLayer(id, enabled),
100
+ [controller]
101
+ );
71
102
  const setDepthMask = (0, import_react.useCallback)((partial) => controller.setDepthMask(partial), [controller]);
72
103
  const resetDepthMask = (0, import_react.useCallback)(() => controller.resetDepthMask(), [controller]);
73
104
  const addAnnotation = (0, import_react.useCallback)((ann) => controller.addAnnotation(ann), [controller]);
@@ -129,6 +160,12 @@ function useImageEditor(options = {}) {
129
160
  setAdjustments,
130
161
  resetAdjustments,
131
162
  upgradeProcess,
163
+ addAdjustmentLayer,
164
+ updateAdjustmentLayer,
165
+ removeAdjustmentLayer,
166
+ moveAdjustmentLayer,
167
+ toggleAdjustmentLayer,
168
+ histogram,
132
169
  setDepthMask,
133
170
  resetDepthMask,
134
171
  addAnnotation,
@@ -157,14 +194,445 @@ function useImageEditor(options = {}) {
157
194
  applyBackgroundRemovedImage
158
195
  }),
159
196
  // eslint-disable-next-line react-hooks/exhaustive-deps
160
- [controller, state, historyTicks]
197
+ [controller, state, historyTicks, histogram]
161
198
  );
162
199
  }
163
200
 
164
201
  // src/image-editor/ImageEditor.tsx
165
- var import_react2 = require("react");
202
+ var import_react5 = __toESM(require("react"), 1);
203
+ var import_image_editor6 = require("@pixerate/editor/image-editor");
204
+
205
+ // src/image-editor/ControlSlider.tsx
166
206
  var import_image_editor2 = require("@pixerate/editor/image-editor");
167
207
  var import_jsx_runtime = require("react/jsx-runtime");
208
+ var ControlSlider = ({ spec, value, onChange, disabled, label }) => {
209
+ const name = label ?? spec.label;
210
+ const track = (0, import_image_editor2.controlTrackBackground)(spec);
211
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { "data-control-id": spec.id, children: [
212
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex justify-between text-slate-400 mb-1", children: [
213
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: name }),
214
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "font-mono", children: (0, import_image_editor2.formatControlValue)(spec, value) })
215
+ ] }),
216
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
217
+ "input",
218
+ {
219
+ type: "range",
220
+ min: spec.min,
221
+ max: spec.max,
222
+ step: spec.step,
223
+ "aria-label": name,
224
+ disabled,
225
+ value,
226
+ onChange: (e) => onChange(Number(e.target.value)),
227
+ className: "w-full accent-indigo-500 disabled:opacity-40"
228
+ }
229
+ ),
230
+ track && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "h-0.5 rounded-full opacity-70", style: { background: track } })
231
+ ] });
232
+ };
233
+
234
+ // src/image-editor/Histogram.tsx
235
+ var import_react2 = require("react");
236
+ var import_image_editor3 = require("@pixerate/editor/image-editor");
237
+ var import_jsx_runtime2 = require("react/jsx-runtime");
238
+ var FILL = {
239
+ luminance: "rgba(148, 163, 184, 0.45)",
240
+ red: "rgba(239, 68, 68, 0.45)",
241
+ green: "rgba(34, 197, 94, 0.45)",
242
+ blue: "rgba(59, 130, 246, 0.45)"
243
+ };
244
+ var W = 256;
245
+ var Histogram = ({ histogram, channel = "rgb", height = 64, className = "", style }) => {
246
+ const channels = channel === "rgb" ? ["luminance", "red", "green", "blue"] : [channel];
247
+ const paths = (0, import_react2.useMemo)(
248
+ () => histogram && histogram.samples > 0 ? channels.map((c) => ({ c, d: (0, import_image_editor3.histogramPath)(histogram, c, W, height) })) : [],
249
+ // eslint-disable-next-line react-hooks/exhaustive-deps
250
+ [histogram, channel, height]
251
+ );
252
+ const clipLow = histogram && histogram.samples ? Math.max(...histogram.shadows) / histogram.samples : 0;
253
+ const clipHigh = histogram && histogram.samples ? Math.max(...histogram.highlights) / histogram.samples : 0;
254
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
255
+ "svg",
256
+ {
257
+ viewBox: `0 0 ${W} ${height}`,
258
+ preserveAspectRatio: "none",
259
+ role: "img",
260
+ "aria-label": histogram ? `Histogram of ${histogram.samples} pixels` : "Histogram (no data)",
261
+ "data-testid": "image-editor-histogram",
262
+ className: `w-full rounded bg-slate-950/60 ${className}`,
263
+ style: { height, ...style },
264
+ children: [
265
+ paths.map(({ c, d }) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d, fill: FILL[c], stroke: "none" }, c)),
266
+ clipLow > 5e-3 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("rect", { x: 0, y: 0, width: 3, height, fill: "#3b82f6", "data-testid": "histogram-clip-shadows" }),
267
+ 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" })
268
+ ]
269
+ }
270
+ );
271
+ };
272
+ function useImageHistogram(controller) {
273
+ const [histogram, setHistogram] = (0, import_react2.useState)(() => controller.getHistogram());
274
+ (0, import_react2.useEffect)(() => controller.subscribeHistogram(setHistogram), [controller]);
275
+ return histogram;
276
+ }
277
+
278
+ // src/image-editor/AdjustmentLayersPanel.tsx
279
+ var import_react4 = require("react");
280
+ var import_image_editor5 = require("@pixerate/editor/image-editor");
281
+
282
+ // src/image-editor/CurvesEditor.tsx
283
+ var import_react3 = require("react");
284
+ var import_image_editor4 = require("@pixerate/editor/image-editor");
285
+ var import_jsx_runtime3 = require("react/jsx-runtime");
286
+ var BOX = 256;
287
+ var STROKE = { master: "#e2e8f0", red: "#ef4444", green: "#22c55e", blue: "#3b82f6" };
288
+ var CurvesEditor = ({ points, onChange, histogram, channel = "master", size = 200 }) => {
289
+ const svgRef = (0, import_react3.useRef)(null);
290
+ const [drag, setDrag] = (0, import_react3.useState)(null);
291
+ const toBox = (e) => {
292
+ const rect = svgRef.current?.getBoundingClientRect();
293
+ if (!rect || !rect.width || !rect.height) return { x: 0, y: 0 };
294
+ return { x: (e.clientX - rect.left) / rect.width * BOX, y: (e.clientY - rect.top) / rect.height * BOX };
295
+ };
296
+ const onPointerDown = (e) => {
297
+ const { x, y } = toBox(e);
298
+ let index = (0, import_image_editor4.hitTestCurvePoint)(points, x, y, BOX, BOX, 10);
299
+ if (index === -1) {
300
+ const inserted = (0, import_image_editor4.insertCurvePoint)(points, (0, import_image_editor4.boxToCurvePoint)(x, y, BOX, BOX));
301
+ onChange(inserted.points, true);
302
+ index = inserted.index;
303
+ } else {
304
+ onChange(points, true);
305
+ }
306
+ setDrag(index);
307
+ e.target.setPointerCapture?.(e.pointerId);
308
+ };
309
+ const onPointerMove = (e) => {
310
+ if (drag === null) return;
311
+ onChange((0, import_image_editor4.moveCurvePoint)(points, drag, (0, import_image_editor4.boxToCurvePoint)(toBox(e).x, toBox(e).y, BOX, BOX)), false);
312
+ };
313
+ const onDoubleClick = (e) => {
314
+ const { x, y } = toBox(e);
315
+ const index = (0, import_image_editor4.hitTestCurvePoint)(points, x, y, BOX, BOX, 10);
316
+ if (index !== -1 && points.length > 2) onChange((0, import_image_editor4.removeCurvePoint)(points, index), true);
317
+ };
318
+ const histChannel = channel === "master" ? "luminance" : channel;
319
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
320
+ "svg",
321
+ {
322
+ ref: svgRef,
323
+ viewBox: `0 0 ${BOX} ${BOX}`,
324
+ width: size,
325
+ height: size,
326
+ role: "application",
327
+ "aria-label": `${channel} curve`,
328
+ "data-testid": "curves-editor",
329
+ className: "rounded bg-slate-950/70 touch-none select-none cursor-crosshair",
330
+ onPointerDown,
331
+ onPointerMove,
332
+ onPointerUp: () => setDrag(null),
333
+ onPointerCancel: () => setDrag(null),
334
+ onDoubleClick,
335
+ children: [
336
+ 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)" }),
337
+ [64, 128, 192].map((v) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("g", { stroke: "rgba(148, 163, 184, 0.2)", strokeWidth: 1, children: [
338
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("line", { x1: v, y1: 0, x2: v, y2: BOX }),
339
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("line", { x1: 0, y1: v, x2: BOX, y2: v })
340
+ ] }, v)),
341
+ /* @__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" }),
342
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("path", { d: (0, import_image_editor4.curveSvgPath)(points, BOX, BOX), fill: "none", stroke: STROKE[channel], strokeWidth: 2 }),
343
+ points.map((p, i) => {
344
+ const b = (0, import_image_editor4.curvePointToBox)(p, BOX, BOX);
345
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
346
+ "circle",
347
+ {
348
+ cx: b.x,
349
+ cy: b.y,
350
+ r: 5,
351
+ fill: drag === i ? STROKE[channel] : "#0f172a",
352
+ stroke: STROKE[channel],
353
+ strokeWidth: 2,
354
+ "aria-label": `Point ${p.input} to ${p.output}`
355
+ },
356
+ i
357
+ );
358
+ })
359
+ ]
360
+ }
361
+ );
362
+ };
363
+
364
+ // src/image-editor/AdjustmentLayersPanel.tsx
365
+ var import_jsx_runtime4 = require("react/jsx-runtime");
366
+ var CHANNELS = [
367
+ { id: "master", label: "RGB" },
368
+ { id: "red", label: "Red" },
369
+ { id: "green", label: "Green" },
370
+ { id: "blue", label: "Blue" }
371
+ ];
372
+ var TONES = [
373
+ { id: "shadows", label: "Shadows" },
374
+ { id: "midtones", label: "Midtones" },
375
+ { id: "highlights", label: "Highlights" }
376
+ ];
377
+ var selectClass = "bg-slate-800 border border-slate-700 rounded px-1.5 py-1 text-slate-200";
378
+ 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"}`;
379
+ var HueRangeStrip = ({ bounds }) => {
380
+ const pts = Array.from({ length: 73 }, (_, i) => `${i / 72 * 360},${20 - (0, import_image_editor5.hueRangeWeight)({ bounds }, i * 5) * 18}`).join(" ");
381
+ 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: [
382
+ /* @__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)) }) }),
383
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("rect", { width: 360, height: 20, fill: "url(#pixerate-hue-strip)", opacity: 0.6 }),
384
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("polyline", { points: pts, fill: "none", stroke: "#fff", strokeWidth: 1.5 })
385
+ ] });
386
+ };
387
+ var AdjustmentLayersPanel = ({ controller, layers: layersProp, histogram }) => {
388
+ const follow = layersProp === void 0;
389
+ const [, setTick] = (0, import_react4.useState)(0);
390
+ (0, import_react4.useEffect)(() => follow ? controller.subscribe(() => setTick((n) => n + 1)) : void 0, [controller, follow]);
391
+ const layers = layersProp ?? controller.getAdjustmentLayers();
392
+ const [newKind, setNewKind] = (0, import_react4.useState)("curves");
393
+ const [selectedId, setSelectedId] = (0, import_react4.useState)(null);
394
+ const [channel, setChannel] = (0, import_react4.useState)("master");
395
+ const [range, setRange] = (0, import_react4.useState)(-1);
396
+ const [tone, setTone] = (0, import_react4.useState)("midtones");
397
+ const selected = layers.find((l) => l.id === selectedId) ?? null;
398
+ const topFirst = [...layers].reverse();
399
+ const setParam = (layer, path, value, commit = true) => controller.updateAdjustmentLayer(layer.id, { params: (0, import_image_editor5.setControlPath)(layer.params, path, value) }, commit);
400
+ const slider = (layer, spec, label) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
401
+ ControlSlider,
402
+ {
403
+ spec,
404
+ label,
405
+ value: Number((0, import_image_editor5.getControlPath)(layer.params, spec.id) ?? spec.default),
406
+ onChange: (v) => setParam(layer, spec.id, v)
407
+ },
408
+ spec.id
409
+ );
410
+ const controlsOf = (layer, filter = () => true) => import_image_editor5.ADJUSTMENT_LAYER_CONTROLS[layer.kind].filter(filter);
411
+ const checkbox = (layer, path, label) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { className: "flex items-center gap-2 text-slate-300", children: [
412
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
413
+ "input",
414
+ {
415
+ type: "checkbox",
416
+ checked: Boolean((0, import_image_editor5.getControlPath)(layer.params, path)),
417
+ onChange: (e) => setParam(layer, path, e.target.checked),
418
+ className: "accent-indigo-500"
419
+ }
420
+ ),
421
+ label
422
+ ] });
423
+ const editor = (layer) => {
424
+ switch (layer.kind) {
425
+ case "curves":
426
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex flex-col gap-2", children: [
427
+ /* @__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)) }),
428
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
429
+ CurvesEditor,
430
+ {
431
+ points: layer.params[channel],
432
+ channel,
433
+ histogram,
434
+ onChange: (points, commit) => setParam(layer, channel, points, commit)
435
+ }
436
+ )
437
+ ] });
438
+ case "levels":
439
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex flex-col gap-2", children: [
440
+ /* @__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)) }),
441
+ controlsOf(layer, (s) => s.id.startsWith(`${channel}.`)).map((s) => slider(layer, s))
442
+ ] });
443
+ case "hueSaturation":
444
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex flex-col gap-2", children: [
445
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex flex-wrap gap-1", role: "group", "aria-label": "Hue range", children: [
446
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { type: "button", className: chipClass(range === -1), onClick: () => setRange(-1), children: "Master" }),
447
+ 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))
448
+ ] }),
449
+ range >= 0 && !layer.params.colorize && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(HueRangeStrip, { bounds: layer.params.ranges[range].bounds }),
450
+ controlsOf(layer, (s) => range === -1 || layer.params.colorize ? !s.id.startsWith("ranges.") : s.id.startsWith(`ranges.${range}.`)).map(
451
+ (s) => slider(layer, s)
452
+ ),
453
+ range === -1 && checkbox(layer, "colorize", "Colorize")
454
+ ] });
455
+ case "colorBalance":
456
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex flex-col gap-2", children: [
457
+ /* @__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)) }),
458
+ controlsOf(layer, (s) => s.id.startsWith(`${tone}.`)).map((s) => slider(layer, s)),
459
+ checkbox(layer, "preserveLuminosity", "Preserve luminosity")
460
+ ] });
461
+ case "blackWhite":
462
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex flex-col gap-2", children: [
463
+ controlsOf(layer).map((s) => slider(layer, s)),
464
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { className: "flex items-center gap-2 text-slate-300", children: [
465
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
466
+ "input",
467
+ {
468
+ type: "checkbox",
469
+ checked: layer.params.tint !== null,
470
+ onChange: (e) => setParam(layer, "tint", e.target.checked ? "#e1d3b3" : null),
471
+ className: "accent-indigo-500"
472
+ }
473
+ ),
474
+ "Tint",
475
+ layer.params.tint && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
476
+ "input",
477
+ {
478
+ type: "color",
479
+ "aria-label": "Tint colour",
480
+ value: layer.params.tint,
481
+ onChange: (e) => setParam(layer, "tint", e.target.value),
482
+ className: "h-5 w-8 bg-transparent"
483
+ }
484
+ )
485
+ ] })
486
+ ] });
487
+ case "photoFilter":
488
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex flex-col gap-2", children: [
489
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { className: "flex items-center gap-2 text-slate-300", children: [
490
+ "Filter colour",
491
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
492
+ "input",
493
+ {
494
+ type: "color",
495
+ "aria-label": "Filter colour",
496
+ value: layer.params.color,
497
+ onChange: (e) => setParam(layer, "color", e.target.value),
498
+ className: "h-5 w-8 bg-transparent"
499
+ }
500
+ )
501
+ ] }),
502
+ controlsOf(layer).map((s) => slider(layer, s)),
503
+ checkbox(layer, "preserveLuminosity", "Preserve luminosity")
504
+ ] });
505
+ case "invert":
506
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "text-slate-500 text-[11px]", children: "Invert has no settings." });
507
+ default:
508
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "flex flex-col gap-2", children: controlsOf(layer).map((s) => slider(layer, s)) });
509
+ }
510
+ };
511
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex flex-col gap-2 text-xs", "data-testid": "adjustment-layers-panel", children: [
512
+ /* @__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" }) }),
513
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex gap-1.5", children: [
514
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
515
+ "select",
516
+ {
517
+ "aria-label": "New adjustment layer kind",
518
+ className: `${selectClass} flex-1`,
519
+ value: newKind,
520
+ onChange: (e) => setNewKind(e.target.value),
521
+ children: import_image_editor5.ADJUSTMENT_KINDS.map((k) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("option", { value: k.kind, children: k.label }, k.kind))
522
+ }
523
+ ),
524
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
525
+ "button",
526
+ {
527
+ type: "button",
528
+ className: "px-2 py-1 rounded bg-indigo-600 text-white hover:bg-indigo-500",
529
+ onClick: () => {
530
+ setSelectedId(controller.addAdjustmentLayer(newKind));
531
+ setChannel("master");
532
+ setRange(-1);
533
+ },
534
+ children: "Add Layer"
535
+ }
536
+ )
537
+ ] }),
538
+ topFirst.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("ul", { className: "flex flex-col gap-1", "aria-label": "Adjustment layers", children: topFirst.map((layer) => {
539
+ const index = layers.indexOf(layer);
540
+ const label = layer.name || (0, import_image_editor5.adjustmentKindLabel)(layer.kind);
541
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
542
+ "li",
543
+ {
544
+ 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"}`,
545
+ children: [
546
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
547
+ "input",
548
+ {
549
+ type: "checkbox",
550
+ "aria-label": `Show ${label}`,
551
+ checked: layer.enabled,
552
+ onChange: () => controller.toggleAdjustmentLayer(layer.id),
553
+ className: "accent-indigo-500"
554
+ }
555
+ ),
556
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
557
+ "button",
558
+ {
559
+ type: "button",
560
+ className: `flex-1 text-left truncate ${layer.enabled ? "text-slate-200" : "text-slate-500 line-through"}`,
561
+ onClick: () => setSelectedId(layer.id === selectedId ? null : layer.id),
562
+ "aria-pressed": layer.id === selectedId,
563
+ children: label
564
+ }
565
+ ),
566
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
567
+ "button",
568
+ {
569
+ type: "button",
570
+ "aria-label": `Move ${label} up`,
571
+ disabled: index === layers.length - 1,
572
+ onClick: () => controller.moveAdjustmentLayer(layer.id, index + 1),
573
+ className: "px-1 text-slate-400 hover:text-slate-200 disabled:opacity-30",
574
+ children: "\u2191"
575
+ }
576
+ ),
577
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
578
+ "button",
579
+ {
580
+ type: "button",
581
+ "aria-label": `Move ${label} down`,
582
+ disabled: index === 0,
583
+ onClick: () => controller.moveAdjustmentLayer(layer.id, index - 1),
584
+ className: "px-1 text-slate-400 hover:text-slate-200 disabled:opacity-30",
585
+ children: "\u2193"
586
+ }
587
+ ),
588
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
589
+ "button",
590
+ {
591
+ type: "button",
592
+ "aria-label": `Remove ${label}`,
593
+ onClick: () => {
594
+ controller.removeAdjustmentLayer(layer.id);
595
+ if (selectedId === layer.id) setSelectedId(null);
596
+ },
597
+ className: "px-1 text-rose-400 hover:text-rose-300",
598
+ children: "\u2715"
599
+ }
600
+ )
601
+ ]
602
+ },
603
+ layer.id
604
+ );
605
+ }) }),
606
+ 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: [
607
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex items-center gap-2", children: [
608
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "text-slate-400", children: "Blend" }),
609
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
610
+ "select",
611
+ {
612
+ "aria-label": "Blend mode",
613
+ className: `${selectClass} flex-1`,
614
+ value: selected.blend,
615
+ onChange: (e) => controller.updateAdjustmentLayer(selected.id, { blend: e.target.value }),
616
+ children: import_image_editor5.BLEND_MODES.map((m) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("option", { value: m.id, children: m.label }, m.id))
617
+ }
618
+ )
619
+ ] }),
620
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
621
+ ControlSlider,
622
+ {
623
+ spec: import_image_editor5.ADJUSTMENT_LAYER_OPACITY_CONTROL,
624
+ label: "Layer opacity",
625
+ value: selected.opacity,
626
+ onChange: (v) => controller.updateAdjustmentLayer(selected.id, { opacity: v })
627
+ }
628
+ ),
629
+ editor(selected)
630
+ ] })
631
+ ] });
632
+ };
633
+
634
+ // src/image-editor/ImageEditor.tsx
635
+ var import_jsx_runtime5 = require("react/jsx-runtime");
168
636
  var ImageEditor = ({
169
637
  editor: externalEditor,
170
638
  src,
@@ -179,17 +647,17 @@ var ImageEditor = ({
179
647
  const internalEditor = useImageEditor({ image: src });
180
648
  const editor = externalEditor || internalEditor;
181
649
  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);
650
+ const legacyProcess = (0, import_image_editor6.normalizeProcessVersion)(state.adjustments.process) < import_image_editor6.LATEST_PROCESS_VERSION;
651
+ const canvasRef = (0, import_react5.useRef)(null);
652
+ const containerRef = (0, import_react5.useRef)(null);
653
+ const [activeTab, setActiveTab] = (0, import_react5.useState)("crop");
654
+ const [activeCropRatio, setActiveCropRatio] = (0, import_react5.useState)(null);
655
+ const [inpaintPrompt, setInpaintPrompt] = (0, import_react5.useState)("");
656
+ const [inpaintMaskMode, setInpaintMaskMode] = (0, import_react5.useState)("annotations");
657
+ const [isRemovingBg, setIsRemovingBg] = (0, import_react5.useState)(false);
658
+ const [isInpainting, setIsInpainting] = (0, import_react5.useState)(false);
659
+ const [aiError, setAiError] = (0, import_react5.useState)(null);
660
+ const [aiSuccess, setAiSuccess] = (0, import_react5.useState)(null);
193
661
  const effectiveOnInpaint = onInpaint || aiHooks?.onInpaint;
194
662
  const effectiveOnRemoveBg = onRemoveBackground || aiHooks?.onRemoveBackground;
195
663
  const handleRemoveBackground = async () => {
@@ -251,18 +719,18 @@ var ImageEditor = ({
251
719
  setIsInpainting(false);
252
720
  }
253
721
  };
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)(
722
+ const [activeColor, setActiveColor] = (0, import_react5.useState)("#ef4444");
723
+ const [strokeWidth, setStrokeWidth] = (0, import_react5.useState)(3);
724
+ const [fontSize, setFontSize] = (0, import_react5.useState)(24);
725
+ const [textInput, setTextInput] = (0, import_react5.useState)("");
726
+ const draftAnnotationRef = (0, import_react5.useRef)(null);
727
+ const draftCropRef = (0, import_react5.useRef)(null);
728
+ const cropDragInfoRef = (0, import_react5.useRef)(null);
729
+ const annotationDragInfoRef = (0, import_react5.useRef)(null);
730
+ const isDrawingRef = (0, import_react5.useRef)(false);
731
+ const currentPathRef = (0, import_react5.useRef)([]);
732
+ const dragStartPointRef = (0, import_react5.useRef)(null);
733
+ const renderCanvas = (0, import_react5.useCallback)(
266
734
  async (draftAnnotation, draftCrop) => {
267
735
  const canvas = canvasRef.current;
268
736
  const container = containerRef.current;
@@ -281,7 +749,7 @@ var ImageEditor = ({
281
749
  },
282
750
  [controller, activeTab]
283
751
  );
284
- (0, import_react2.useEffect)(() => {
752
+ (0, import_react5.useEffect)(() => {
285
753
  const raf = typeof requestAnimationFrame !== "undefined" ? requestAnimationFrame : (cb) => setTimeout(cb, 0);
286
754
  const caf = typeof cancelAnimationFrame !== "undefined" ? cancelAnimationFrame : (id) => clearTimeout(id);
287
755
  const animId = raf(() => {
@@ -289,12 +757,12 @@ var ImageEditor = ({
289
757
  });
290
758
  return () => caf(animId);
291
759
  }, [renderCanvas, state]);
292
- (0, import_react2.useEffect)(() => {
760
+ (0, import_react5.useEffect)(() => {
293
761
  if (activeTab === "crop" && editor.state.activeTool !== "crop") {
294
762
  editor.setTool("crop");
295
763
  }
296
764
  }, [activeTab, editor]);
297
- (0, import_react2.useEffect)(() => {
765
+ (0, import_react5.useEffect)(() => {
298
766
  const container = containerRef.current;
299
767
  if (!container) return;
300
768
  if (typeof ResizeObserver !== "undefined") {
@@ -331,8 +799,8 @@ var ImageEditor = ({
331
799
  width: state.imageDimensions.width,
332
800
  height: state.imageDimensions.height
333
801
  };
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);
802
+ const handle = (0, import_image_editor6.hitTestCrop)(canvasPt, state, canvas.width, canvas.height, 14, activeCrop);
803
+ const startImgPt = (0, import_image_editor6.canvasToImagePoint)(canvasPt, state, canvas.width, canvas.height, true);
336
804
  if (handle) {
337
805
  cropDragInfoRef.current = {
338
806
  handle,
@@ -351,12 +819,12 @@ var ImageEditor = ({
351
819
  return;
352
820
  }
353
821
  if (activeTab === "annotate") {
354
- const imgPt = (0, import_image_editor2.canvasToImagePoint)(canvasPt, state, canvas.width, canvas.height, false);
822
+ const imgPt = (0, import_image_editor6.canvasToImagePoint)(canvasPt, state, canvas.width, canvas.height, false);
355
823
  dragStartPointRef.current = imgPt;
356
824
  if (state.activeTool === "select") {
357
825
  for (let i = state.annotations.length - 1; i >= 0; i--) {
358
826
  const ann = state.annotations[i];
359
- if ((0, import_image_editor2.hitTestAnnotation)(imgPt, ann)) {
827
+ if ((0, import_image_editor6.hitTestAnnotation)(imgPt, ann)) {
360
828
  editor.selectAnnotation(ann.id);
361
829
  annotationDragInfoRef.current = {
362
830
  annId: ann.id,
@@ -393,7 +861,7 @@ var ImageEditor = ({
393
861
  if (activeTab === "crop") {
394
862
  const drag = cropDragInfoRef.current;
395
863
  if (drag) {
396
- const currentImgPt = (0, import_image_editor2.canvasToImagePoint)(canvasPt, state, canvas.width, canvas.height, true);
864
+ const currentImgPt = (0, import_image_editor6.canvasToImagePoint)(canvasPt, state, canvas.width, canvas.height, true);
397
865
  let newCrop;
398
866
  if (drag.handle === "new") {
399
867
  const minX = Math.max(0, Math.min(drag.startImgPt.x, currentImgPt.x));
@@ -407,7 +875,7 @@ var ImageEditor = ({
407
875
  height: Math.round(Math.max(20, maxY - minY))
408
876
  };
409
877
  } else {
410
- newCrop = (0, import_image_editor2.calculateCropDrag)(
878
+ newCrop = (0, import_image_editor6.calculateCropDrag)(
411
879
  drag.startCrop,
412
880
  drag.handle,
413
881
  drag.startImgPt,
@@ -427,15 +895,15 @@ var ImageEditor = ({
427
895
  width: state.imageDimensions.width,
428
896
  height: state.imageDimensions.height
429
897
  };
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);
898
+ const handle = (0, import_image_editor6.hitTestCrop)(canvasPt, state, canvas.width, canvas.height, 14, activeCrop);
899
+ canvas.style.cursor = (0, import_image_editor6.getCropCursor)(handle, state.transform);
432
900
  }
433
901
  return;
434
902
  }
435
903
  if (activeTab === "annotate") {
436
904
  if (annotationDragInfoRef.current) {
437
905
  const drag = annotationDragInfoRef.current;
438
- const currentImgPt2 = (0, import_image_editor2.canvasToImagePoint)(canvasPt, state, canvas.width, canvas.height, false);
906
+ const currentImgPt2 = (0, import_image_editor6.canvasToImagePoint)(canvasPt, state, canvas.width, canvas.height, false);
439
907
  const dx = currentImgPt2.x - drag.startImgPt.x;
440
908
  const dy = currentImgPt2.y - drag.startImgPt.y;
441
909
  editor.updateAnnotation(drag.annId, {
@@ -447,10 +915,10 @@ var ImageEditor = ({
447
915
  const startPt = dragStartPointRef.current;
448
916
  if (!startPt) {
449
917
  if (state.activeTool === "select") {
450
- const imgPt = (0, import_image_editor2.canvasToImagePoint)(canvasPt, state, canvas.width, canvas.height, false);
918
+ const imgPt = (0, import_image_editor6.canvasToImagePoint)(canvasPt, state, canvas.width, canvas.height, false);
451
919
  let hovered = false;
452
920
  for (let i = state.annotations.length - 1; i >= 0; i--) {
453
- if ((0, import_image_editor2.hitTestAnnotation)(imgPt, state.annotations[i])) {
921
+ if ((0, import_image_editor6.hitTestAnnotation)(imgPt, state.annotations[i])) {
454
922
  hovered = true;
455
923
  break;
456
924
  }
@@ -461,7 +929,7 @@ var ImageEditor = ({
461
929
  }
462
930
  return;
463
931
  }
464
- const currentImgPt = (0, import_image_editor2.canvasToImagePoint)(canvasPt, state, canvas.width, canvas.height, false);
932
+ const currentImgPt = (0, import_image_editor6.canvasToImagePoint)(canvasPt, state, canvas.width, canvas.height, false);
465
933
  if (state.activeTool === "pen" && isDrawingRef.current) {
466
934
  currentPathRef.current.push(currentImgPt);
467
935
  const draft = {
@@ -556,7 +1024,7 @@ var ImageEditor = ({
556
1024
  }
557
1025
  const startPt = dragStartPointRef.current;
558
1026
  if (startPt) {
559
- const currentImgPt = (0, import_image_editor2.canvasToImagePoint)(canvasPt, state, canvas.width, canvas.height, false);
1027
+ const currentImgPt = (0, import_image_editor6.canvasToImagePoint)(canvasPt, state, canvas.width, canvas.height, false);
560
1028
  if (state.activeTool === "pen" && isDrawingRef.current) {
561
1029
  if (currentPathRef.current.length > 1) {
562
1030
  editor.addAnnotation({
@@ -652,15 +1120,15 @@ var ImageEditor = ({
652
1120
  link.click();
653
1121
  }
654
1122
  };
655
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1123
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
656
1124
  "div",
657
1125
  {
658
1126
  className: `flex flex-col bg-slate-900 border border-slate-800 rounded-xl overflow-hidden shadow-2xl text-slate-100 ${className}`,
659
1127
  style,
660
1128
  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)(
1129
+ /* @__PURE__ */ (0, import_jsx_runtime5.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: [
1130
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex items-center gap-1 bg-slate-900 p-1 rounded-lg border border-slate-800 text-xs", children: [
1131
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
664
1132
  "button",
665
1133
  {
666
1134
  type: "button",
@@ -672,7 +1140,7 @@ var ImageEditor = ({
672
1140
  children: "Transform & Crop"
673
1141
  }
674
1142
  ),
675
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1143
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
676
1144
  "button",
677
1145
  {
678
1146
  type: "button",
@@ -684,7 +1152,7 @@ var ImageEditor = ({
684
1152
  children: "Adjustments"
685
1153
  }
686
1154
  ),
687
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1155
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
688
1156
  "button",
689
1157
  {
690
1158
  type: "button",
@@ -696,7 +1164,7 @@ var ImageEditor = ({
696
1164
  children: "Annotations"
697
1165
  }
698
1166
  ),
699
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1167
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
700
1168
  "button",
701
1169
  {
702
1170
  type: "button",
@@ -708,7 +1176,7 @@ var ImageEditor = ({
708
1176
  children: "Depth Mask"
709
1177
  }
710
1178
  ),
711
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1179
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
712
1180
  "button",
713
1181
  {
714
1182
  type: "button",
@@ -718,14 +1186,14 @@ var ImageEditor = ({
718
1186
  },
719
1187
  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
1188
  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" }) }),
1189
+ /* @__PURE__ */ (0, import_jsx_runtime5.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_runtime5.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
1190
  "AI Tools"
723
1191
  ]
724
1192
  }
725
1193
  )
726
1194
  ] }),
727
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex items-center gap-2 text-xs", children: [
728
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1195
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex items-center gap-2 text-xs", children: [
1196
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
729
1197
  "button",
730
1198
  {
731
1199
  type: "button",
@@ -736,7 +1204,7 @@ var ImageEditor = ({
736
1204
  children: "\u21A9"
737
1205
  }
738
1206
  ),
739
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1207
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
740
1208
  "button",
741
1209
  {
742
1210
  type: "button",
@@ -747,7 +1215,7 @@ var ImageEditor = ({
747
1215
  children: "\u21AA"
748
1216
  }
749
1217
  ),
750
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1218
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
751
1219
  "button",
752
1220
  {
753
1221
  type: "button",
@@ -756,8 +1224,8 @@ var ImageEditor = ({
756
1224
  children: "Reset"
757
1225
  }
758
1226
  ),
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)(
1227
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "h-4 w-px bg-slate-800 mx-1" }),
1228
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
761
1229
  "button",
762
1230
  {
763
1231
  type: "button",
@@ -767,11 +1235,11 @@ var ImageEditor = ({
767
1235
  children: "-"
768
1236
  }
769
1237
  ),
770
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "font-mono text-slate-400 w-10 text-center", children: [
1238
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { className: "font-mono text-slate-400 w-10 text-center", children: [
771
1239
  Math.round(state.zoom * 100),
772
1240
  "%"
773
1241
  ] }),
774
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1242
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
775
1243
  "button",
776
1244
  {
777
1245
  type: "button",
@@ -781,8 +1249,8 @@ var ImageEditor = ({
781
1249
  children: "+"
782
1250
  }
783
1251
  ),
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)(
1252
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "h-4 w-px bg-slate-800 mx-1" }),
1253
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
786
1254
  "button",
787
1255
  {
788
1256
  type: "button",
@@ -793,7 +1261,7 @@ var ImageEditor = ({
793
1261
  children: "Hold Compare"
794
1262
  }
795
1263
  ),
796
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1264
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
797
1265
  "button",
798
1266
  {
799
1267
  type: "button",
@@ -804,13 +1272,13 @@ var ImageEditor = ({
804
1272
  )
805
1273
  ] })
806
1274
  ] }),
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)(
1275
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex flex-col md:flex-row flex-1 min-h-[520px] md:h-[560px]", children: [
1276
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
809
1277
  "div",
810
1278
  {
811
1279
  ref: containerRef,
812
1280
  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)(
1281
+ children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
814
1282
  "canvas",
815
1283
  {
816
1284
  ref: canvasRef,
@@ -823,11 +1291,11 @@ var ImageEditor = ({
823
1291
  )
824
1292
  }
825
1293
  ),
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)(
1294
+ /* @__PURE__ */ (0, import_jsx_runtime5.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: [
1295
+ activeTab === "crop" && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex flex-col gap-4 text-xs", children: [
1296
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Aspect Ratio Presets" }),
1297
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "grid grid-cols-3 gap-2", children: [
1298
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
831
1299
  "button",
832
1300
  {
833
1301
  type: "button",
@@ -839,7 +1307,7 @@ var ImageEditor = ({
839
1307
  children: "Freeform"
840
1308
  }
841
1309
  ),
842
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1310
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
843
1311
  "button",
844
1312
  {
845
1313
  type: "button",
@@ -851,7 +1319,7 @@ var ImageEditor = ({
851
1319
  children: "1:1 Square"
852
1320
  }
853
1321
  ),
854
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1322
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
855
1323
  "button",
856
1324
  {
857
1325
  type: "button",
@@ -863,7 +1331,7 @@ var ImageEditor = ({
863
1331
  children: "4:3 Standard"
864
1332
  }
865
1333
  ),
866
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1334
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
867
1335
  "button",
868
1336
  {
869
1337
  type: "button",
@@ -875,7 +1343,7 @@ var ImageEditor = ({
875
1343
  children: "16:9 Wide"
876
1344
  }
877
1345
  ),
878
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1346
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
879
1347
  "button",
880
1348
  {
881
1349
  type: "button",
@@ -887,7 +1355,7 @@ var ImageEditor = ({
887
1355
  children: "3:2 Photo"
888
1356
  }
889
1357
  ),
890
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1358
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
891
1359
  "button",
892
1360
  {
893
1361
  type: "button",
@@ -900,10 +1368,10 @@ var ImageEditor = ({
900
1368
  }
901
1369
  )
902
1370
  ] }),
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)(
1371
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "h-px bg-slate-800 my-1" }),
1372
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Orientation & Flips" }),
1373
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "grid grid-cols-2 gap-2", children: [
1374
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
907
1375
  "button",
908
1376
  {
909
1377
  type: "button",
@@ -912,7 +1380,7 @@ var ImageEditor = ({
912
1380
  children: "Rotate -90\xB0"
913
1381
  }
914
1382
  ),
915
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1383
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
916
1384
  "button",
917
1385
  {
918
1386
  type: "button",
@@ -921,7 +1389,7 @@ var ImageEditor = ({
921
1389
  children: "Rotate +90\xB0"
922
1390
  }
923
1391
  ),
924
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1392
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
925
1393
  "button",
926
1394
  {
927
1395
  type: "button",
@@ -930,7 +1398,7 @@ var ImageEditor = ({
930
1398
  children: "Flip Horizontal"
931
1399
  }
932
1400
  ),
933
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1401
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
934
1402
  "button",
935
1403
  {
936
1404
  type: "button",
@@ -941,10 +1409,10 @@ var ImageEditor = ({
941
1409
  )
942
1410
  ] })
943
1411
  ] }),
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)(
1412
+ activeTab === "adjust" && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex flex-col gap-3 text-xs", children: [
1413
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex items-center justify-between", children: [
1414
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Color Adjustments" }),
1415
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
948
1416
  "button",
949
1417
  {
950
1418
  type: "button",
@@ -954,174 +1422,49 @@ var ImageEditor = ({
954
1422
  }
955
1423
  )
956
1424
  ] }),
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",
1015
- {
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"
1022
- }
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",
1425
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Histogram, { histogram: editor.histogram }),
1426
+ import_image_editor6.IMAGE_ADJUSTMENT_CONTROLS.map((spec) => /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_react5.default.Fragment, { children: [
1427
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1428
+ ControlSlider,
1032
1429
  {
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"
1430
+ spec,
1431
+ value: (0, import_image_editor6.readAdjustmentControls)(state.adjustments)[spec.id],
1432
+ disabled: spec.requiresProcess !== void 0 && legacyProcess,
1433
+ onChange: (v) => editor.setAdjustments({ [spec.id]: v })
1039
1434
  }
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",
1435
+ ),
1436
+ spec.id === "tint" && legacyProcess && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
1437
+ "div",
1049
1438
  {
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"
1439
+ 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",
1440
+ "data-testid": "image-editor-process-notice",
1441
+ children: [
1442
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: "Legacy rendering (process 1). Tint needs the current process." }),
1443
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1444
+ "button",
1445
+ {
1446
+ type: "button",
1447
+ onClick: () => editor.upgradeProcess(),
1448
+ className: "shrink-0 rounded bg-amber-500/20 px-2 py-0.5 hover:bg-amber-500/30",
1449
+ children: "Update"
1450
+ }
1451
+ )
1452
+ ]
1058
1453
  }
1059
1454
  )
1060
- ] }),
1061
- legacyProcess && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1062
- "div",
1455
+ ] }, spec.id)),
1456
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "border-t border-slate-700/60 pt-3", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1457
+ AdjustmentLayersPanel,
1063
1458
  {
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
- ]
1459
+ controller,
1460
+ layers: state.adjustments.layers,
1461
+ histogram: editor.histogram
1078
1462
  }
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
- ] })
1463
+ ) })
1121
1464
  ] }),
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)(
1465
+ activeTab === "annotate" && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex flex-col gap-3 text-xs", children: [
1466
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Draw & Annotate" }),
1467
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "grid grid-cols-3 gap-1.5", children: ["pen", "rect", "circle", "arrow", "line"].map((t) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1125
1468
  "button",
1126
1469
  {
1127
1470
  type: "button",
@@ -1131,9 +1474,9 @@ var ImageEditor = ({
1131
1474
  },
1132
1475
  t
1133
1476
  )) }),
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)(
1477
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex items-center gap-2 mt-2", children: [
1478
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("label", { className: "text-slate-400", children: "Color:" }),
1479
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1137
1480
  "input",
1138
1481
  {
1139
1482
  type: "color",
@@ -1142,15 +1485,15 @@ var ImageEditor = ({
1142
1485
  className: "w-8 h-8 rounded border border-slate-700 cursor-pointer bg-transparent"
1143
1486
  }
1144
1487
  ),
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: [
1488
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex-1 ml-2", children: [
1489
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex justify-between text-slate-400 mb-0.5", children: [
1490
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: "Width:" }),
1491
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { className: "font-mono", children: [
1149
1492
  strokeWidth,
1150
1493
  "px"
1151
1494
  ] })
1152
1495
  ] }),
1153
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1496
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1154
1497
  "input",
1155
1498
  {
1156
1499
  type: "range",
@@ -1163,10 +1506,10 @@ var ImageEditor = ({
1163
1506
  )
1164
1507
  ] })
1165
1508
  ] }),
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)(
1509
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "h-px bg-slate-800 my-1" }),
1510
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Add Text" }),
1511
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex gap-2", children: [
1512
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1170
1513
  "input",
1171
1514
  {
1172
1515
  type: "text",
@@ -1177,7 +1520,7 @@ var ImageEditor = ({
1177
1520
  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
1521
  }
1179
1522
  ),
1180
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1523
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1181
1524
  "button",
1182
1525
  {
1183
1526
  type: "button",
@@ -1187,14 +1530,14 @@ var ImageEditor = ({
1187
1530
  }
1188
1531
  )
1189
1532
  ] }),
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: [
1533
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex items-center justify-between text-slate-400", children: [
1534
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: "Font Size:" }),
1535
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { className: "font-mono", children: [
1193
1536
  fontSize,
1194
1537
  "px"
1195
1538
  ] })
1196
1539
  ] }),
1197
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1540
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1198
1541
  "input",
1199
1542
  {
1200
1543
  type: "range",
@@ -1205,14 +1548,14 @@ var ImageEditor = ({
1205
1548
  className: "w-full accent-indigo-500"
1206
1549
  }
1207
1550
  ),
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: [
1551
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "h-px bg-slate-800 my-1" }),
1552
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex items-center justify-between", children: [
1553
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { className: "text-slate-400", children: [
1211
1554
  "Layers (",
1212
1555
  state.annotations.length,
1213
1556
  ")"
1214
1557
  ] }),
1215
- state.selectedAnnotationId && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1558
+ state.selectedAnnotationId && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1216
1559
  "button",
1217
1560
  {
1218
1561
  type: "button",
@@ -1221,7 +1564,7 @@ var ImageEditor = ({
1221
1564
  children: "Delete Selected"
1222
1565
  }
1223
1566
  ),
1224
- state.annotations.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1567
+ state.annotations.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1225
1568
  "button",
1226
1569
  {
1227
1570
  type: "button",
@@ -1232,10 +1575,10 @@ var ImageEditor = ({
1232
1575
  )
1233
1576
  ] })
1234
1577
  ] }),
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)(
1578
+ activeTab === "depth" && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex flex-col gap-3 text-xs", children: [
1579
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex items-center justify-between", children: [
1580
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Depth Masking" }),
1581
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1239
1582
  "button",
1240
1583
  {
1241
1584
  type: "button",
@@ -1245,8 +1588,8 @@ var ImageEditor = ({
1245
1588
  }
1246
1589
  )
1247
1590
  ] }),
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)(
1591
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("label", { className: "flex items-center gap-2 cursor-pointer text-slate-300", children: [
1592
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1250
1593
  "input",
1251
1594
  {
1252
1595
  type: "checkbox",
@@ -1255,12 +1598,12 @@ var ImageEditor = ({
1255
1598
  className: "rounded accent-indigo-500"
1256
1599
  }
1257
1600
  ),
1258
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "Enable Depth / Alpha Mask" })
1601
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: "Enable Depth / Alpha Mask" })
1259
1602
  ] }),
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)(
1603
+ state.depthMask.enabled && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
1604
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { children: [
1605
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("label", { className: "text-slate-400 block mb-1", children: "Mask Image URL:" }),
1606
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1264
1607
  "input",
1265
1608
  {
1266
1609
  type: "text",
@@ -1271,8 +1614,8 @@ var ImageEditor = ({
1271
1614
  }
1272
1615
  )
1273
1616
  ] }),
1274
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex gap-2", children: [
1275
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1617
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex gap-2", children: [
1618
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1276
1619
  "button",
1277
1620
  {
1278
1621
  type: "button",
@@ -1281,7 +1624,7 @@ var ImageEditor = ({
1281
1624
  children: "Threshold Mode"
1282
1625
  }
1283
1626
  ),
1284
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1627
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1285
1628
  "button",
1286
1629
  {
1287
1630
  type: "button",
@@ -1291,12 +1634,12 @@ var ImageEditor = ({
1291
1634
  }
1292
1635
  )
1293
1636
  ] }),
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] })
1637
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { children: [
1638
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex justify-between text-slate-400 mb-1", children: [
1639
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: "Depth Start (Lower):" }),
1640
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "font-mono", children: state.depthMask.depthRange[0] })
1298
1641
  ] }),
1299
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1642
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1300
1643
  "input",
1301
1644
  {
1302
1645
  type: "range",
@@ -1310,12 +1653,12 @@ var ImageEditor = ({
1310
1653
  }
1311
1654
  )
1312
1655
  ] }),
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] })
1656
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { children: [
1657
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex justify-between text-slate-400 mb-1", children: [
1658
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: "Depth Stop (Upper):" }),
1659
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "font-mono", children: state.depthMask.depthRange[1] })
1317
1660
  ] }),
1318
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1661
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1319
1662
  "input",
1320
1663
  {
1321
1664
  type: "range",
@@ -1329,12 +1672,12 @@ var ImageEditor = ({
1329
1672
  }
1330
1673
  )
1331
1674
  ] }),
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 })
1675
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { children: [
1676
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex justify-between text-slate-400 mb-1", children: [
1677
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: "Softness:" }),
1678
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "font-mono", children: state.depthMask.softness })
1336
1679
  ] }),
1337
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1680
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1338
1681
  "input",
1339
1682
  {
1340
1683
  type: "range",
@@ -1346,8 +1689,8 @@ var ImageEditor = ({
1346
1689
  }
1347
1690
  )
1348
1691
  ] }),
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)(
1692
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("label", { className: "flex items-center gap-2 cursor-pointer text-slate-300 mt-1", children: [
1693
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1351
1694
  "input",
1352
1695
  {
1353
1696
  type: "checkbox",
@@ -1356,52 +1699,52 @@ var ImageEditor = ({
1356
1699
  className: "rounded accent-indigo-500"
1357
1700
  }
1358
1701
  ),
1359
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "Invert Mask" })
1702
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: "Invert Mask" })
1360
1703
  ] })
1361
1704
  ] })
1362
1705
  ] }),
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" }) }),
1706
+ activeTab === "ai" && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex flex-col gap-4 text-xs", children: [
1707
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "p-3 rounded-lg bg-slate-950/60 border border-slate-800 flex flex-col gap-2.5", children: [
1708
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex items-center justify-between", children: [
1709
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px] flex items-center gap-1.5", children: [
1710
+ /* @__PURE__ */ (0, import_jsx_runtime5.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_runtime5.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
1711
  "Background Removal"
1369
1712
  ] }),
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..." })
1713
+ isRemovingBg && /* @__PURE__ */ (0, import_jsx_runtime5.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
1714
  ] }),
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)(
1715
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "text-slate-400 text-[11px] leading-relaxed", children: "Isolate foreground subject with AI alpha transparency segmentation." }),
1716
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1374
1717
  "button",
1375
1718
  {
1376
1719
  type: "button",
1377
1720
  onClick: handleRemoveBackground,
1378
1721
  disabled: isRemovingBg || isInpainting,
1379
1722
  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" }),
1723
+ children: isRemovingBg ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
1724
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "w-3.5 h-3.5 border-2 border-white/30 border-t-white rounded-full animate-spin" }),
1382
1725
  "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" }) }),
1726
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
1727
+ /* @__PURE__ */ (0, import_jsx_runtime5.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_runtime5.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
1728
  "Remove Background"
1386
1729
  ] })
1387
1730
  }
1388
1731
  )
1389
1732
  ] }),
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" })
1733
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "p-3 rounded-lg bg-slate-950/60 border border-slate-800 flex flex-col gap-2.5", children: [
1734
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex items-center justify-between", children: [
1735
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px] flex items-center gap-1.5", children: [
1736
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("svg", { className: "w-3.5 h-3.5 text-purple-400", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
1737
+ /* @__PURE__ */ (0, import_jsx_runtime5.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" }),
1738
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("path", { d: "m14 7 3 3" })
1396
1739
  ] }),
1397
1740
  "Generative Inpainting"
1398
1741
  ] }),
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..." })
1742
+ isInpainting && /* @__PURE__ */ (0, import_jsx_runtime5.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
1743
  ] }),
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)(
1744
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "text-slate-400 text-[11px] leading-relaxed", children: "Replace, modify, or fill selected regions using prompt-guided generative inpainting." }),
1745
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex flex-col gap-1", children: [
1746
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("label", { htmlFor: "ai-inpaint-prompt-react", className: "text-slate-300 text-[11px] font-medium", children: "Prompt" }),
1747
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1405
1748
  "textarea",
1406
1749
  {
1407
1750
  id: "ai-inpaint-prompt-react",
@@ -1413,10 +1756,10 @@ var ImageEditor = ({
1413
1756
  }
1414
1757
  )
1415
1758
  ] }),
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)(
1759
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex flex-col gap-1", children: [
1760
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "text-slate-300 text-[11px] font-medium", children: "Target Mask Region" }),
1761
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "grid grid-cols-2 gap-1.5", children: [
1762
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
1420
1763
  "button",
1421
1764
  {
1422
1765
  type: "button",
@@ -1429,7 +1772,7 @@ var ImageEditor = ({
1429
1772
  ]
1430
1773
  }
1431
1774
  ),
1432
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1775
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
1433
1776
  "button",
1434
1777
  {
1435
1778
  type: "button",
@@ -1444,25 +1787,25 @@ var ImageEditor = ({
1444
1787
  )
1445
1788
  ] })
1446
1789
  ] }),
1447
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1790
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1448
1791
  "button",
1449
1792
  {
1450
1793
  type: "button",
1451
1794
  onClick: handleInpaint,
1452
1795
  disabled: isInpainting || isRemovingBg || !inpaintPrompt.trim(),
1453
1796
  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" }),
1797
+ children: isInpainting ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
1798
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "w-3.5 h-3.5 border-2 border-white/30 border-t-white rounded-full animate-spin" }),
1456
1799
  "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" }) }),
1800
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
1801
+ /* @__PURE__ */ (0, import_jsx_runtime5.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_runtime5.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
1802
  "Apply Inpainting"
1460
1803
  ] })
1461
1804
  }
1462
1805
  )
1463
1806
  ] }),
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 })
1807
+ aiError && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "p-2.5 rounded-lg bg-rose-950/60 border border-rose-800 text-rose-300 text-[11px]", children: aiError }),
1808
+ aiSuccess && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "p-2.5 rounded-lg bg-emerald-950/60 border border-emerald-800 text-emerald-300 text-[11px]", children: aiSuccess })
1466
1809
  ] })
1467
1810
  ] })
1468
1811
  ] })
@@ -1472,7 +1815,12 @@ var ImageEditor = ({
1472
1815
  };
1473
1816
  // Annotate the CommonJS export names for ESM import in node:
1474
1817
  0 && (module.exports = {
1818
+ AdjustmentLayersPanel,
1819
+ ControlSlider,
1820
+ CurvesEditor,
1821
+ Histogram,
1475
1822
  ImageEditor,
1476
- useImageEditor
1823
+ useImageEditor,
1824
+ useImageHistogram
1477
1825
  });
1478
1826
  //# sourceMappingURL=index.cjs.map