@pixerate/editor-react 0.14.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 (54) hide show
  1. package/README.md +6 -1
  2. package/dist/canvas/index.cjs +352 -118
  3. package/dist/canvas/index.cjs.map +1 -1
  4. package/dist/canvas/index.d.cts +112 -8
  5. package/dist/canvas/index.d.ts +112 -8
  6. package/dist/canvas/index.js +258 -19
  7. package/dist/canvas/index.js.map +1 -1
  8. package/dist/chunk-4ZC2WCUO.js +196 -0
  9. package/dist/chunk-4ZC2WCUO.js.map +1 -0
  10. package/dist/chunk-7SPRQZ6H.js +216 -0
  11. package/dist/chunk-7SPRQZ6H.js.map +1 -0
  12. package/dist/{chunk-25S5EHH4.js → chunk-FYIPXHAE.js} +22 -13
  13. package/dist/chunk-FYIPXHAE.js.map +1 -0
  14. package/dist/{chunk-TL27COKM.js → chunk-XTFR3IMK.js} +683 -286
  15. package/dist/chunk-XTFR3IMK.js.map +1 -0
  16. package/dist/chunk-XWFU2CSG.js +167 -0
  17. package/dist/chunk-XWFU2CSG.js.map +1 -0
  18. package/dist/commands/index.cjs +232 -0
  19. package/dist/commands/index.cjs.map +1 -0
  20. package/dist/commands/index.d.cts +69 -0
  21. package/dist/commands/index.d.ts +69 -0
  22. package/dist/commands/index.js +13 -0
  23. package/dist/commands/index.js.map +1 -0
  24. package/dist/generation/index.cjs +235 -0
  25. package/dist/generation/index.cjs.map +1 -0
  26. package/dist/generation/index.d.cts +60 -0
  27. package/dist/generation/index.d.ts +60 -0
  28. package/dist/generation/index.js +13 -0
  29. package/dist/generation/index.js.map +1 -0
  30. package/dist/image-editor/index.cjs +688 -299
  31. package/dist/image-editor/index.cjs.map +1 -1
  32. package/dist/image-editor/index.d.cts +65 -5
  33. package/dist/image-editor/index.d.ts +65 -5
  34. package/dist/image-editor/index.js +13 -3
  35. package/dist/index.cjs +2200 -683
  36. package/dist/index.cjs.map +1 -1
  37. package/dist/index.d.cts +201 -23
  38. package/dist/index.d.ts +201 -23
  39. package/dist/index.js +743 -129
  40. package/dist/index.js.map +1 -1
  41. package/dist/persistence/index.cjs +191 -0
  42. package/dist/persistence/index.cjs.map +1 -0
  43. package/dist/persistence/index.d.cts +77 -0
  44. package/dist/persistence/index.d.ts +77 -0
  45. package/dist/persistence/index.js +9 -0
  46. package/dist/persistence/index.js.map +1 -0
  47. package/dist/spreadsheet/index.cjs +17 -10
  48. package/dist/spreadsheet/index.cjs.map +1 -1
  49. package/dist/spreadsheet/index.d.cts +9 -7
  50. package/dist/spreadsheet/index.d.ts +9 -7
  51. package/dist/spreadsheet/index.js +1 -1
  52. package/package.json +26 -2
  53. package/dist/chunk-25S5EHH4.js.map +0 -1
  54. package/dist/chunk-TL27COKM.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);
@@ -67,6 +84,21 @@ function useImageEditor(options = {}) {
67
84
  [controller]
68
85
  );
69
86
  const resetAdjustments = (0, import_react.useCallback)(() => controller.resetAdjustments(), [controller]);
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
+ );
70
102
  const setDepthMask = (0, import_react.useCallback)((partial) => controller.setDepthMask(partial), [controller]);
71
103
  const resetDepthMask = (0, import_react.useCallback)(() => controller.resetDepthMask(), [controller]);
72
104
  const addAnnotation = (0, import_react.useCallback)((ann) => controller.addAnnotation(ann), [controller]);
@@ -127,6 +159,13 @@ function useImageEditor(options = {}) {
127
159
  flipVertical,
128
160
  setAdjustments,
129
161
  resetAdjustments,
162
+ upgradeProcess,
163
+ addAdjustmentLayer,
164
+ updateAdjustmentLayer,
165
+ removeAdjustmentLayer,
166
+ moveAdjustmentLayer,
167
+ toggleAdjustmentLayer,
168
+ histogram,
130
169
  setDepthMask,
131
170
  resetDepthMask,
132
171
  addAnnotation,
@@ -155,14 +194,445 @@ function useImageEditor(options = {}) {
155
194
  applyBackgroundRemovedImage
156
195
  }),
157
196
  // eslint-disable-next-line react-hooks/exhaustive-deps
158
- [controller, state, historyTicks]
197
+ [controller, state, historyTicks, histogram]
159
198
  );
160
199
  }
161
200
 
162
201
  // src/image-editor/ImageEditor.tsx
163
- 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
164
206
  var import_image_editor2 = require("@pixerate/editor/image-editor");
165
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");
166
636
  var ImageEditor = ({
167
637
  editor: externalEditor,
168
638
  src,
@@ -177,16 +647,17 @@ var ImageEditor = ({
177
647
  const internalEditor = useImageEditor({ image: src });
178
648
  const editor = externalEditor || internalEditor;
179
649
  const { state, controller } = editor;
180
- const canvasRef = (0, import_react2.useRef)(null);
181
- const containerRef = (0, import_react2.useRef)(null);
182
- const [activeTab, setActiveTab] = (0, import_react2.useState)("crop");
183
- const [activeCropRatio, setActiveCropRatio] = (0, import_react2.useState)(null);
184
- const [inpaintPrompt, setInpaintPrompt] = (0, import_react2.useState)("");
185
- const [inpaintMaskMode, setInpaintMaskMode] = (0, import_react2.useState)("annotations");
186
- const [isRemovingBg, setIsRemovingBg] = (0, import_react2.useState)(false);
187
- const [isInpainting, setIsInpainting] = (0, import_react2.useState)(false);
188
- const [aiError, setAiError] = (0, import_react2.useState)(null);
189
- 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);
190
661
  const effectiveOnInpaint = onInpaint || aiHooks?.onInpaint;
191
662
  const effectiveOnRemoveBg = onRemoveBackground || aiHooks?.onRemoveBackground;
192
663
  const handleRemoveBackground = async () => {
@@ -248,18 +719,18 @@ var ImageEditor = ({
248
719
  setIsInpainting(false);
249
720
  }
250
721
  };
251
- const [activeColor, setActiveColor] = (0, import_react2.useState)("#ef4444");
252
- const [strokeWidth, setStrokeWidth] = (0, import_react2.useState)(3);
253
- const [fontSize, setFontSize] = (0, import_react2.useState)(24);
254
- const [textInput, setTextInput] = (0, import_react2.useState)("");
255
- const draftAnnotationRef = (0, import_react2.useRef)(null);
256
- const draftCropRef = (0, import_react2.useRef)(null);
257
- const cropDragInfoRef = (0, import_react2.useRef)(null);
258
- const annotationDragInfoRef = (0, import_react2.useRef)(null);
259
- const isDrawingRef = (0, import_react2.useRef)(false);
260
- const currentPathRef = (0, import_react2.useRef)([]);
261
- const dragStartPointRef = (0, import_react2.useRef)(null);
262
- 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)(
263
734
  async (draftAnnotation, draftCrop) => {
264
735
  const canvas = canvasRef.current;
265
736
  const container = containerRef.current;
@@ -278,7 +749,7 @@ var ImageEditor = ({
278
749
  },
279
750
  [controller, activeTab]
280
751
  );
281
- (0, import_react2.useEffect)(() => {
752
+ (0, import_react5.useEffect)(() => {
282
753
  const raf = typeof requestAnimationFrame !== "undefined" ? requestAnimationFrame : (cb) => setTimeout(cb, 0);
283
754
  const caf = typeof cancelAnimationFrame !== "undefined" ? cancelAnimationFrame : (id) => clearTimeout(id);
284
755
  const animId = raf(() => {
@@ -286,12 +757,12 @@ var ImageEditor = ({
286
757
  });
287
758
  return () => caf(animId);
288
759
  }, [renderCanvas, state]);
289
- (0, import_react2.useEffect)(() => {
760
+ (0, import_react5.useEffect)(() => {
290
761
  if (activeTab === "crop" && editor.state.activeTool !== "crop") {
291
762
  editor.setTool("crop");
292
763
  }
293
764
  }, [activeTab, editor]);
294
- (0, import_react2.useEffect)(() => {
765
+ (0, import_react5.useEffect)(() => {
295
766
  const container = containerRef.current;
296
767
  if (!container) return;
297
768
  if (typeof ResizeObserver !== "undefined") {
@@ -328,8 +799,8 @@ var ImageEditor = ({
328
799
  width: state.imageDimensions.width,
329
800
  height: state.imageDimensions.height
330
801
  };
331
- const handle = (0, import_image_editor2.hitTestCrop)(canvasPt, state, canvas.width, canvas.height, 14, activeCrop);
332
- 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);
333
804
  if (handle) {
334
805
  cropDragInfoRef.current = {
335
806
  handle,
@@ -348,12 +819,12 @@ var ImageEditor = ({
348
819
  return;
349
820
  }
350
821
  if (activeTab === "annotate") {
351
- 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);
352
823
  dragStartPointRef.current = imgPt;
353
824
  if (state.activeTool === "select") {
354
825
  for (let i = state.annotations.length - 1; i >= 0; i--) {
355
826
  const ann = state.annotations[i];
356
- if ((0, import_image_editor2.hitTestAnnotation)(imgPt, ann)) {
827
+ if ((0, import_image_editor6.hitTestAnnotation)(imgPt, ann)) {
357
828
  editor.selectAnnotation(ann.id);
358
829
  annotationDragInfoRef.current = {
359
830
  annId: ann.id,
@@ -390,7 +861,7 @@ var ImageEditor = ({
390
861
  if (activeTab === "crop") {
391
862
  const drag = cropDragInfoRef.current;
392
863
  if (drag) {
393
- 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);
394
865
  let newCrop;
395
866
  if (drag.handle === "new") {
396
867
  const minX = Math.max(0, Math.min(drag.startImgPt.x, currentImgPt.x));
@@ -404,7 +875,7 @@ var ImageEditor = ({
404
875
  height: Math.round(Math.max(20, maxY - minY))
405
876
  };
406
877
  } else {
407
- newCrop = (0, import_image_editor2.calculateCropDrag)(
878
+ newCrop = (0, import_image_editor6.calculateCropDrag)(
408
879
  drag.startCrop,
409
880
  drag.handle,
410
881
  drag.startImgPt,
@@ -424,15 +895,15 @@ var ImageEditor = ({
424
895
  width: state.imageDimensions.width,
425
896
  height: state.imageDimensions.height
426
897
  };
427
- const handle = (0, import_image_editor2.hitTestCrop)(canvasPt, state, canvas.width, canvas.height, 14, activeCrop);
428
- 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);
429
900
  }
430
901
  return;
431
902
  }
432
903
  if (activeTab === "annotate") {
433
904
  if (annotationDragInfoRef.current) {
434
905
  const drag = annotationDragInfoRef.current;
435
- 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);
436
907
  const dx = currentImgPt2.x - drag.startImgPt.x;
437
908
  const dy = currentImgPt2.y - drag.startImgPt.y;
438
909
  editor.updateAnnotation(drag.annId, {
@@ -444,10 +915,10 @@ var ImageEditor = ({
444
915
  const startPt = dragStartPointRef.current;
445
916
  if (!startPt) {
446
917
  if (state.activeTool === "select") {
447
- 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);
448
919
  let hovered = false;
449
920
  for (let i = state.annotations.length - 1; i >= 0; i--) {
450
- if ((0, import_image_editor2.hitTestAnnotation)(imgPt, state.annotations[i])) {
921
+ if ((0, import_image_editor6.hitTestAnnotation)(imgPt, state.annotations[i])) {
451
922
  hovered = true;
452
923
  break;
453
924
  }
@@ -458,7 +929,7 @@ var ImageEditor = ({
458
929
  }
459
930
  return;
460
931
  }
461
- 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);
462
933
  if (state.activeTool === "pen" && isDrawingRef.current) {
463
934
  currentPathRef.current.push(currentImgPt);
464
935
  const draft = {
@@ -553,7 +1024,7 @@ var ImageEditor = ({
553
1024
  }
554
1025
  const startPt = dragStartPointRef.current;
555
1026
  if (startPt) {
556
- 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);
557
1028
  if (state.activeTool === "pen" && isDrawingRef.current) {
558
1029
  if (currentPathRef.current.length > 1) {
559
1030
  editor.addAnnotation({
@@ -649,15 +1120,15 @@ var ImageEditor = ({
649
1120
  link.click();
650
1121
  }
651
1122
  };
652
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1123
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
653
1124
  "div",
654
1125
  {
655
1126
  className: `flex flex-col bg-slate-900 border border-slate-800 rounded-xl overflow-hidden shadow-2xl text-slate-100 ${className}`,
656
1127
  style,
657
1128
  children: [
658
- /* @__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: [
659
- /* @__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: [
660
- /* @__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)(
661
1132
  "button",
662
1133
  {
663
1134
  type: "button",
@@ -669,7 +1140,7 @@ var ImageEditor = ({
669
1140
  children: "Transform & Crop"
670
1141
  }
671
1142
  ),
672
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1143
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
673
1144
  "button",
674
1145
  {
675
1146
  type: "button",
@@ -681,7 +1152,7 @@ var ImageEditor = ({
681
1152
  children: "Adjustments"
682
1153
  }
683
1154
  ),
684
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1155
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
685
1156
  "button",
686
1157
  {
687
1158
  type: "button",
@@ -693,7 +1164,7 @@ var ImageEditor = ({
693
1164
  children: "Annotations"
694
1165
  }
695
1166
  ),
696
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1167
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
697
1168
  "button",
698
1169
  {
699
1170
  type: "button",
@@ -705,7 +1176,7 @@ var ImageEditor = ({
705
1176
  children: "Depth Mask"
706
1177
  }
707
1178
  ),
708
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1179
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
709
1180
  "button",
710
1181
  {
711
1182
  type: "button",
@@ -715,14 +1186,14 @@ var ImageEditor = ({
715
1186
  },
716
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"}`,
717
1188
  children: [
718
- /* @__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" }) }),
719
1190
  "AI Tools"
720
1191
  ]
721
1192
  }
722
1193
  )
723
1194
  ] }),
724
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex items-center gap-2 text-xs", children: [
725
- /* @__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)(
726
1197
  "button",
727
1198
  {
728
1199
  type: "button",
@@ -733,7 +1204,7 @@ var ImageEditor = ({
733
1204
  children: "\u21A9"
734
1205
  }
735
1206
  ),
736
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1207
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
737
1208
  "button",
738
1209
  {
739
1210
  type: "button",
@@ -744,7 +1215,7 @@ var ImageEditor = ({
744
1215
  children: "\u21AA"
745
1216
  }
746
1217
  ),
747
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1218
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
748
1219
  "button",
749
1220
  {
750
1221
  type: "button",
@@ -753,8 +1224,8 @@ var ImageEditor = ({
753
1224
  children: "Reset"
754
1225
  }
755
1226
  ),
756
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "h-4 w-px bg-slate-800 mx-1" }),
757
- /* @__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)(
758
1229
  "button",
759
1230
  {
760
1231
  type: "button",
@@ -764,11 +1235,11 @@ var ImageEditor = ({
764
1235
  children: "-"
765
1236
  }
766
1237
  ),
767
- /* @__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: [
768
1239
  Math.round(state.zoom * 100),
769
1240
  "%"
770
1241
  ] }),
771
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1242
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
772
1243
  "button",
773
1244
  {
774
1245
  type: "button",
@@ -778,8 +1249,8 @@ var ImageEditor = ({
778
1249
  children: "+"
779
1250
  }
780
1251
  ),
781
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "h-4 w-px bg-slate-800 mx-1" }),
782
- /* @__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)(
783
1254
  "button",
784
1255
  {
785
1256
  type: "button",
@@ -790,7 +1261,7 @@ var ImageEditor = ({
790
1261
  children: "Hold Compare"
791
1262
  }
792
1263
  ),
793
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1264
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
794
1265
  "button",
795
1266
  {
796
1267
  type: "button",
@@ -801,13 +1272,13 @@ var ImageEditor = ({
801
1272
  )
802
1273
  ] })
803
1274
  ] }),
804
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex flex-col md:flex-row flex-1 min-h-[520px] md:h-[560px]", children: [
805
- /* @__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)(
806
1277
  "div",
807
1278
  {
808
1279
  ref: containerRef,
809
1280
  className: "relative flex-1 h-[360px] md:h-full min-h-[320px] bg-slate-950 overflow-hidden select-none",
810
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1281
+ children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
811
1282
  "canvas",
812
1283
  {
813
1284
  ref: canvasRef,
@@ -820,11 +1291,11 @@ var ImageEditor = ({
820
1291
  )
821
1292
  }
822
1293
  ),
823
- /* @__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: [
824
- activeTab === "crop" && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex flex-col gap-4 text-xs", children: [
825
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Aspect Ratio Presets" }),
826
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "grid grid-cols-3 gap-2", children: [
827
- /* @__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)(
828
1299
  "button",
829
1300
  {
830
1301
  type: "button",
@@ -836,7 +1307,7 @@ var ImageEditor = ({
836
1307
  children: "Freeform"
837
1308
  }
838
1309
  ),
839
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1310
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
840
1311
  "button",
841
1312
  {
842
1313
  type: "button",
@@ -848,7 +1319,7 @@ var ImageEditor = ({
848
1319
  children: "1:1 Square"
849
1320
  }
850
1321
  ),
851
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1322
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
852
1323
  "button",
853
1324
  {
854
1325
  type: "button",
@@ -860,7 +1331,7 @@ var ImageEditor = ({
860
1331
  children: "4:3 Standard"
861
1332
  }
862
1333
  ),
863
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1334
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
864
1335
  "button",
865
1336
  {
866
1337
  type: "button",
@@ -872,7 +1343,7 @@ var ImageEditor = ({
872
1343
  children: "16:9 Wide"
873
1344
  }
874
1345
  ),
875
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1346
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
876
1347
  "button",
877
1348
  {
878
1349
  type: "button",
@@ -884,7 +1355,7 @@ var ImageEditor = ({
884
1355
  children: "3:2 Photo"
885
1356
  }
886
1357
  ),
887
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1358
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
888
1359
  "button",
889
1360
  {
890
1361
  type: "button",
@@ -897,10 +1368,10 @@ var ImageEditor = ({
897
1368
  }
898
1369
  )
899
1370
  ] }),
900
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "h-px bg-slate-800 my-1" }),
901
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Orientation & Flips" }),
902
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "grid grid-cols-2 gap-2", children: [
903
- /* @__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)(
904
1375
  "button",
905
1376
  {
906
1377
  type: "button",
@@ -909,7 +1380,7 @@ var ImageEditor = ({
909
1380
  children: "Rotate -90\xB0"
910
1381
  }
911
1382
  ),
912
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1383
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
913
1384
  "button",
914
1385
  {
915
1386
  type: "button",
@@ -918,7 +1389,7 @@ var ImageEditor = ({
918
1389
  children: "Rotate +90\xB0"
919
1390
  }
920
1391
  ),
921
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1392
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
922
1393
  "button",
923
1394
  {
924
1395
  type: "button",
@@ -927,7 +1398,7 @@ var ImageEditor = ({
927
1398
  children: "Flip Horizontal"
928
1399
  }
929
1400
  ),
930
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1401
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
931
1402
  "button",
932
1403
  {
933
1404
  type: "button",
@@ -938,10 +1409,10 @@ var ImageEditor = ({
938
1409
  )
939
1410
  ] })
940
1411
  ] }),
941
- activeTab === "adjust" && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex flex-col gap-3 text-xs", children: [
942
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex items-center justify-between", children: [
943
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Color Adjustments" }),
944
- /* @__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)(
945
1416
  "button",
946
1417
  {
947
1418
  type: "button",
@@ -951,136 +1422,49 @@ var ImageEditor = ({
951
1422
  }
952
1423
  )
953
1424
  ] }),
954
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
955
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex justify-between text-slate-400 mb-1", children: [
956
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "Brightness" }),
957
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "font-mono", children: state.adjustments.brightness })
958
- ] }),
959
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
960
- "input",
961
- {
962
- type: "range",
963
- min: "-100",
964
- max: "100",
965
- value: state.adjustments.brightness,
966
- onChange: (e) => editor.setAdjustments({ brightness: Number(e.target.value) }),
967
- className: "w-full accent-indigo-500"
968
- }
969
- )
970
- ] }),
971
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
972
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex justify-between text-slate-400 mb-1", children: [
973
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "Contrast" }),
974
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "font-mono", children: state.adjustments.contrast })
975
- ] }),
976
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
977
- "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,
978
1429
  {
979
- type: "range",
980
- min: "-100",
981
- max: "100",
982
- value: state.adjustments.contrast,
983
- onChange: (e) => editor.setAdjustments({ contrast: Number(e.target.value) }),
984
- 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 })
985
1434
  }
986
- )
987
- ] }),
988
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
989
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex justify-between text-slate-400 mb-1", children: [
990
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "Saturation" }),
991
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "font-mono", children: state.adjustments.saturation })
992
- ] }),
993
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
994
- "input",
995
- {
996
- type: "range",
997
- min: "-100",
998
- max: "100",
999
- value: state.adjustments.saturation,
1000
- onChange: (e) => editor.setAdjustments({ saturation: Number(e.target.value) }),
1001
- className: "w-full accent-indigo-500"
1002
- }
1003
- )
1004
- ] }),
1005
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
1006
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex justify-between text-slate-400 mb-1", children: [
1007
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "Exposure" }),
1008
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "font-mono", children: state.adjustments.exposure })
1009
- ] }),
1010
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1011
- "input",
1012
- {
1013
- type: "range",
1014
- min: "-100",
1015
- max: "100",
1016
- value: state.adjustments.exposure,
1017
- onChange: (e) => editor.setAdjustments({ exposure: Number(e.target.value) }),
1018
- className: "w-full accent-indigo-500"
1019
- }
1020
- )
1021
- ] }),
1022
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
1023
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex justify-between text-slate-400 mb-1", children: [
1024
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "Warmth / Temperature" }),
1025
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "font-mono", children: state.adjustments.temperature })
1026
- ] }),
1027
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1028
- "input",
1029
- {
1030
- type: "range",
1031
- min: "-100",
1032
- max: "100",
1033
- value: state.adjustments.temperature,
1034
- onChange: (e) => editor.setAdjustments({ temperature: Number(e.target.value) }),
1035
- className: "w-full accent-indigo-500"
1036
- }
1037
- )
1038
- ] }),
1039
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
1040
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex justify-between text-slate-400 mb-1", children: [
1041
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "Blur" }),
1042
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "font-mono", children: [
1043
- state.adjustments.blur,
1044
- "px"
1045
- ] })
1046
- ] }),
1047
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1048
- "input",
1049
- {
1050
- type: "range",
1051
- min: "0",
1052
- max: "30",
1053
- value: state.adjustments.blur,
1054
- onChange: (e) => editor.setAdjustments({ blur: Number(e.target.value) }),
1055
- className: "w-full accent-indigo-500"
1056
- }
1057
- )
1058
- ] }),
1059
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
1060
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex justify-between text-slate-400 mb-1", children: [
1061
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "Opacity" }),
1062
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "font-mono", children: [
1063
- Math.round(state.adjustments.opacity * 100),
1064
- "%"
1065
- ] })
1066
- ] }),
1067
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1068
- "input",
1435
+ ),
1436
+ spec.id === "tint" && legacyProcess && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
1437
+ "div",
1069
1438
  {
1070
- type: "range",
1071
- min: "0",
1072
- max: "1",
1073
- step: "0.05",
1074
- value: state.adjustments.opacity,
1075
- onChange: (e) => editor.setAdjustments({ opacity: Number(e.target.value) }),
1076
- className: "w-full accent-indigo-500"
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
+ ]
1077
1453
  }
1078
1454
  )
1079
- ] })
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,
1458
+ {
1459
+ controller,
1460
+ layers: state.adjustments.layers,
1461
+ histogram: editor.histogram
1462
+ }
1463
+ ) })
1080
1464
  ] }),
1081
- activeTab === "annotate" && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex flex-col gap-3 text-xs", children: [
1082
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Draw & Annotate" }),
1083
- /* @__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)(
1084
1468
  "button",
1085
1469
  {
1086
1470
  type: "button",
@@ -1090,9 +1474,9 @@ var ImageEditor = ({
1090
1474
  },
1091
1475
  t
1092
1476
  )) }),
1093
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex items-center gap-2 mt-2", children: [
1094
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { className: "text-slate-400", children: "Color:" }),
1095
- /* @__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)(
1096
1480
  "input",
1097
1481
  {
1098
1482
  type: "color",
@@ -1101,15 +1485,15 @@ var ImageEditor = ({
1101
1485
  className: "w-8 h-8 rounded border border-slate-700 cursor-pointer bg-transparent"
1102
1486
  }
1103
1487
  ),
1104
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex-1 ml-2", children: [
1105
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex justify-between text-slate-400 mb-0.5", children: [
1106
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "Width:" }),
1107
- /* @__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: [
1108
1492
  strokeWidth,
1109
1493
  "px"
1110
1494
  ] })
1111
1495
  ] }),
1112
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1496
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1113
1497
  "input",
1114
1498
  {
1115
1499
  type: "range",
@@ -1122,10 +1506,10 @@ var ImageEditor = ({
1122
1506
  )
1123
1507
  ] })
1124
1508
  ] }),
1125
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "h-px bg-slate-800 my-1" }),
1126
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Add Text" }),
1127
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex gap-2", children: [
1128
- /* @__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)(
1129
1513
  "input",
1130
1514
  {
1131
1515
  type: "text",
@@ -1136,7 +1520,7 @@ var ImageEditor = ({
1136
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"
1137
1521
  }
1138
1522
  ),
1139
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1523
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1140
1524
  "button",
1141
1525
  {
1142
1526
  type: "button",
@@ -1146,14 +1530,14 @@ var ImageEditor = ({
1146
1530
  }
1147
1531
  )
1148
1532
  ] }),
1149
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex items-center justify-between text-slate-400", children: [
1150
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "Font Size:" }),
1151
- /* @__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: [
1152
1536
  fontSize,
1153
1537
  "px"
1154
1538
  ] })
1155
1539
  ] }),
1156
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1540
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1157
1541
  "input",
1158
1542
  {
1159
1543
  type: "range",
@@ -1164,14 +1548,14 @@ var ImageEditor = ({
1164
1548
  className: "w-full accent-indigo-500"
1165
1549
  }
1166
1550
  ),
1167
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "h-px bg-slate-800 my-1" }),
1168
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex items-center justify-between", children: [
1169
- /* @__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: [
1170
1554
  "Layers (",
1171
1555
  state.annotations.length,
1172
1556
  ")"
1173
1557
  ] }),
1174
- state.selectedAnnotationId && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1558
+ state.selectedAnnotationId && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1175
1559
  "button",
1176
1560
  {
1177
1561
  type: "button",
@@ -1180,7 +1564,7 @@ var ImageEditor = ({
1180
1564
  children: "Delete Selected"
1181
1565
  }
1182
1566
  ),
1183
- state.annotations.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1567
+ state.annotations.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1184
1568
  "button",
1185
1569
  {
1186
1570
  type: "button",
@@ -1191,10 +1575,10 @@ var ImageEditor = ({
1191
1575
  )
1192
1576
  ] })
1193
1577
  ] }),
1194
- activeTab === "depth" && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex flex-col gap-3 text-xs", children: [
1195
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex items-center justify-between", children: [
1196
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Depth Masking" }),
1197
- /* @__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)(
1198
1582
  "button",
1199
1583
  {
1200
1584
  type: "button",
@@ -1204,8 +1588,8 @@ var ImageEditor = ({
1204
1588
  }
1205
1589
  )
1206
1590
  ] }),
1207
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { className: "flex items-center gap-2 cursor-pointer text-slate-300", children: [
1208
- /* @__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)(
1209
1593
  "input",
1210
1594
  {
1211
1595
  type: "checkbox",
@@ -1214,12 +1598,12 @@ var ImageEditor = ({
1214
1598
  className: "rounded accent-indigo-500"
1215
1599
  }
1216
1600
  ),
1217
- /* @__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" })
1218
1602
  ] }),
1219
- state.depthMask.enabled && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
1220
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
1221
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { className: "text-slate-400 block mb-1", children: "Mask Image URL:" }),
1222
- /* @__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)(
1223
1607
  "input",
1224
1608
  {
1225
1609
  type: "text",
@@ -1230,8 +1614,8 @@ var ImageEditor = ({
1230
1614
  }
1231
1615
  )
1232
1616
  ] }),
1233
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex gap-2", children: [
1234
- /* @__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)(
1235
1619
  "button",
1236
1620
  {
1237
1621
  type: "button",
@@ -1240,7 +1624,7 @@ var ImageEditor = ({
1240
1624
  children: "Threshold Mode"
1241
1625
  }
1242
1626
  ),
1243
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1627
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1244
1628
  "button",
1245
1629
  {
1246
1630
  type: "button",
@@ -1250,12 +1634,12 @@ var ImageEditor = ({
1250
1634
  }
1251
1635
  )
1252
1636
  ] }),
1253
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
1254
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex justify-between text-slate-400 mb-1", children: [
1255
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "Depth Start (Lower):" }),
1256
- /* @__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] })
1257
1641
  ] }),
1258
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1642
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1259
1643
  "input",
1260
1644
  {
1261
1645
  type: "range",
@@ -1269,12 +1653,12 @@ var ImageEditor = ({
1269
1653
  }
1270
1654
  )
1271
1655
  ] }),
1272
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
1273
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex justify-between text-slate-400 mb-1", children: [
1274
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "Depth Stop (Upper):" }),
1275
- /* @__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] })
1276
1660
  ] }),
1277
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1661
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1278
1662
  "input",
1279
1663
  {
1280
1664
  type: "range",
@@ -1288,12 +1672,12 @@ var ImageEditor = ({
1288
1672
  }
1289
1673
  )
1290
1674
  ] }),
1291
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
1292
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex justify-between text-slate-400 mb-1", children: [
1293
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "Softness:" }),
1294
- /* @__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 })
1295
1679
  ] }),
1296
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1680
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1297
1681
  "input",
1298
1682
  {
1299
1683
  type: "range",
@@ -1305,8 +1689,8 @@ var ImageEditor = ({
1305
1689
  }
1306
1690
  )
1307
1691
  ] }),
1308
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { className: "flex items-center gap-2 cursor-pointer text-slate-300 mt-1", children: [
1309
- /* @__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)(
1310
1694
  "input",
1311
1695
  {
1312
1696
  type: "checkbox",
@@ -1315,52 +1699,52 @@ var ImageEditor = ({
1315
1699
  className: "rounded accent-indigo-500"
1316
1700
  }
1317
1701
  ),
1318
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "Invert Mask" })
1702
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: "Invert Mask" })
1319
1703
  ] })
1320
1704
  ] })
1321
1705
  ] }),
1322
- activeTab === "ai" && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex flex-col gap-4 text-xs", children: [
1323
- /* @__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: [
1324
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex items-center justify-between", children: [
1325
- /* @__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: [
1326
- /* @__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" }) }),
1327
1711
  "Background Removal"
1328
1712
  ] }),
1329
- 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..." })
1330
1714
  ] }),
1331
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "text-slate-400 text-[11px] leading-relaxed", children: "Isolate foreground subject with AI alpha transparency segmentation." }),
1332
- /* @__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)(
1333
1717
  "button",
1334
1718
  {
1335
1719
  type: "button",
1336
1720
  onClick: handleRemoveBackground,
1337
1721
  disabled: isRemovingBg || isInpainting,
1338
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",
1339
- children: isRemovingBg ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
1340
- /* @__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" }),
1341
1725
  "Removing Background..."
1342
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
1343
- /* @__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" }) }),
1344
1728
  "Remove Background"
1345
1729
  ] })
1346
1730
  }
1347
1731
  )
1348
1732
  ] }),
1349
- /* @__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: [
1350
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex items-center justify-between", children: [
1351
- /* @__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: [
1352
- /* @__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: [
1353
- /* @__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" }),
1354
- /* @__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" })
1355
1739
  ] }),
1356
1740
  "Generative Inpainting"
1357
1741
  ] }),
1358
- 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..." })
1359
1743
  ] }),
1360
- /* @__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." }),
1361
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex flex-col gap-1", children: [
1362
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { htmlFor: "ai-inpaint-prompt-react", className: "text-slate-300 text-[11px] font-medium", children: "Prompt" }),
1363
- /* @__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)(
1364
1748
  "textarea",
1365
1749
  {
1366
1750
  id: "ai-inpaint-prompt-react",
@@ -1372,10 +1756,10 @@ var ImageEditor = ({
1372
1756
  }
1373
1757
  )
1374
1758
  ] }),
1375
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex flex-col gap-1", children: [
1376
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "text-slate-300 text-[11px] font-medium", children: "Target Mask Region" }),
1377
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "grid grid-cols-2 gap-1.5", children: [
1378
- /* @__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)(
1379
1763
  "button",
1380
1764
  {
1381
1765
  type: "button",
@@ -1388,7 +1772,7 @@ var ImageEditor = ({
1388
1772
  ]
1389
1773
  }
1390
1774
  ),
1391
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1775
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
1392
1776
  "button",
1393
1777
  {
1394
1778
  type: "button",
@@ -1403,25 +1787,25 @@ var ImageEditor = ({
1403
1787
  )
1404
1788
  ] })
1405
1789
  ] }),
1406
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1790
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1407
1791
  "button",
1408
1792
  {
1409
1793
  type: "button",
1410
1794
  onClick: handleInpaint,
1411
1795
  disabled: isInpainting || isRemovingBg || !inpaintPrompt.trim(),
1412
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",
1413
- children: isInpainting ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
1414
- /* @__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" }),
1415
1799
  "Generating Inpaint..."
1416
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
1417
- /* @__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" }) }),
1418
1802
  "Apply Inpainting"
1419
1803
  ] })
1420
1804
  }
1421
1805
  )
1422
1806
  ] }),
1423
- 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 }),
1424
- 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 })
1425
1809
  ] })
1426
1810
  ] })
1427
1811
  ] })
@@ -1431,7 +1815,12 @@ var ImageEditor = ({
1431
1815
  };
1432
1816
  // Annotate the CommonJS export names for ESM import in node:
1433
1817
  0 && (module.exports = {
1818
+ AdjustmentLayersPanel,
1819
+ ControlSlider,
1820
+ CurvesEditor,
1821
+ Histogram,
1434
1822
  ImageEditor,
1435
- useImageEditor
1823
+ useImageEditor,
1824
+ useImageHistogram
1436
1825
  });
1437
1826
  //# sourceMappingURL=index.cjs.map