@pixerate/editor-react 0.63.0 → 0.65.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -46,6 +46,7 @@ __export(src_exports, {
46
46
  ExportDocxButton: () => ExportDocxButton,
47
47
  FindReplaceBar: () => FindReplaceBar,
48
48
  FormulaHints: () => FormulaHints,
49
+ FreeTransformOptionsBar: () => FreeTransformOptionsBar,
49
50
  GenerationHistory: () => GenerationHistory,
50
51
  GenerationSettings: () => GenerationSettings,
51
52
  Histogram: () => Histogram,
@@ -53,6 +54,7 @@ __export(src_exports, {
53
54
  ImageEditor: () => ImageEditor,
54
55
  InsertTableGrid: () => InsertTableGrid,
55
56
  JobProgress: () => JobProgress,
57
+ LayerEffectsPanel: () => LayerEffectsPanel,
56
58
  LayersPanel: () => LayersPanel,
57
59
  MergePreview: () => MergePreview,
58
60
  NameManager: () => NameManager,
@@ -2003,17 +2005,17 @@ var SpreadsheetValidationDialog = ({ prompt, onResolve }) => {
2003
2005
  style === "warning" && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { className: "mt-2", children: "Continue?" })
2004
2006
  ] })
2005
2007
  ] }),
2006
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "mt-4 flex justify-end gap-2", children: buttons.map((button2, i) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2008
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "mt-4 flex justify-end gap-2", children: buttons.map((button3, i) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2007
2009
  "button",
2008
2010
  {
2009
2011
  ref: i === 0 ? firstButton : void 0,
2010
2012
  type: "button",
2011
- "data-action": button2.action,
2013
+ "data-action": button3.action,
2012
2014
  className: `rounded border border-border px-3 py-1 ${i === 0 ? "bg-primary text-primary-foreground" : "hover:bg-muted"}`,
2013
- onClick: () => onResolve(button2.action),
2014
- children: button2.label
2015
+ onClick: () => onResolve(button3.action),
2016
+ children: button3.label
2015
2017
  },
2016
- button2.action
2018
+ button3.action
2017
2019
  )) })
2018
2020
  ]
2019
2021
  }
@@ -3783,8 +3785,8 @@ function useImageEditor(options = {}) {
3783
3785
  }
3784
3786
 
3785
3787
  // src/image-editor/ImageEditor.tsx
3786
- var import_react42 = __toESM(require("react"), 1);
3787
- var import_image_editor10 = require("@pixerate/editor/image-editor");
3788
+ var import_react44 = __toESM(require("react"), 1);
3789
+ var import_image_editor12 = require("@pixerate/editor/image-editor");
3788
3790
 
3789
3791
  // src/image-editor/ControlSlider.tsx
3790
3792
  var import_image_editor2 = require("@pixerate/editor/image-editor");
@@ -4216,9 +4218,198 @@ var AdjustmentLayersPanel = ({ controller, layers: layersProp, histogram }) => {
4216
4218
  };
4217
4219
 
4218
4220
  // src/image-editor/LayersPanel.tsx
4221
+ var import_react39 = require("react");
4222
+
4223
+ // src/image-editor/LayerEffectsPanel.tsx
4219
4224
  var import_react38 = require("react");
4220
4225
  var import_image_editor6 = require("@pixerate/editor/image-editor");
4221
4226
  var import_jsx_runtime21 = require("react/jsx-runtime");
4227
+ var button = "px-1.5 py-0.5 rounded text-[10px] bg-slate-800 text-slate-300 hover:text-white disabled:opacity-40";
4228
+ var LayerEffectsPanel = ({ controller, layerId, className }) => {
4229
+ const [, setTick] = (0, import_react38.useState)(0);
4230
+ (0, import_react38.useEffect)(() => controller.subscribe(() => setTick((n) => n + 1)), [controller]);
4231
+ const [open, setOpen] = (0, import_react38.useState)(null);
4232
+ const [error, setError] = (0, import_react38.useState)(null);
4233
+ const commitNext = (0, import_react38.useRef)(true);
4234
+ const layer = layerId ? controller.getLayer(layerId) : controller.getActiveLayer();
4235
+ const model = (0, import_image_editor6.layerEffectsPanel)(layer);
4236
+ const id = layer?.id;
4237
+ const attempt = (fn) => {
4238
+ try {
4239
+ setError(null);
4240
+ fn();
4241
+ } catch (err) {
4242
+ setError(err instanceof Error ? err.message : String(err));
4243
+ }
4244
+ };
4245
+ const sliderEvents = {
4246
+ onPointerDown: () => commitNext.current = true,
4247
+ onPointerUp: () => commitNext.current = true,
4248
+ onKeyDown: () => commitNext.current = true
4249
+ };
4250
+ if (!model.available || !id) {
4251
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: `text-xs text-slate-500 ${className ?? ""}`, "data-testid": "layer-effects", children: model.reason });
4252
+ }
4253
+ const effects = controller.getLayerEffects(id);
4254
+ const patterns = controller.getDocumentPatterns();
4255
+ const change = (effectId, control, value, drag = false) => attempt(() => {
4256
+ const effect = controller.getLayerEffects(id).items.find((e) => e.id === effectId);
4257
+ controller.updateLayerEffect(id, effectId, (0, import_image_editor6.effectControlChange)(effect, control.key, value, patterns), drag ? commitNext.current : true);
4258
+ if (drag) commitNext.current = false;
4259
+ });
4260
+ const renderControl = (effectId, c) => {
4261
+ const label = `${c.label}`;
4262
+ switch (c.type) {
4263
+ case "range": {
4264
+ const k = c.percent ? 100 : 1;
4265
+ const shown = Math.round(c.value * k * 100) / 100;
4266
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("label", { className: "flex flex-col gap-0.5", children: [
4267
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("span", { className: "text-slate-400", children: [
4268
+ label,
4269
+ " ",
4270
+ shown,
4271
+ c.unit
4272
+ ] }),
4273
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
4274
+ "input",
4275
+ {
4276
+ type: "range",
4277
+ "aria-label": label,
4278
+ min: c.min * k,
4279
+ max: c.max * k,
4280
+ step: c.step * k,
4281
+ value: shown,
4282
+ disabled: model.readOnly,
4283
+ ...sliderEvents,
4284
+ onChange: (e) => change(effectId, c, Number(e.target.value) / k, true),
4285
+ className: "accent-indigo-500"
4286
+ }
4287
+ )
4288
+ ] }, c.key);
4289
+ }
4290
+ case "color":
4291
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("label", { className: "flex items-center gap-1.5 text-slate-300", children: [
4292
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("input", { type: "color", "aria-label": label, value: c.value, disabled: model.readOnly, onChange: (e) => change(effectId, c, e.target.value) }),
4293
+ label
4294
+ ] }, c.key);
4295
+ case "checkbox":
4296
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("label", { className: "flex items-center gap-1.5 text-slate-300", children: [
4297
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("input", { type: "checkbox", "aria-label": label, checked: c.value, disabled: model.readOnly, onChange: (e) => change(effectId, c, e.target.checked), className: "accent-indigo-500" }),
4298
+ label
4299
+ ] }, c.key);
4300
+ case "select":
4301
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("label", { className: "flex flex-col gap-0.5", children: [
4302
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "text-slate-400", children: label }),
4303
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
4304
+ "select",
4305
+ {
4306
+ "aria-label": label,
4307
+ value: c.value,
4308
+ disabled: model.readOnly,
4309
+ onChange: (e) => change(effectId, c, e.target.value),
4310
+ className: "bg-slate-800 border border-slate-700 rounded px-1.5 py-1",
4311
+ children: c.options.map((o) => /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("option", { value: o.value, children: o.label }, o.value))
4312
+ }
4313
+ )
4314
+ ] }, c.key);
4315
+ }
4316
+ };
4317
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: `flex flex-col gap-1.5 text-xs text-slate-200 ${className ?? ""}`, "data-testid": "layer-effects", "aria-label": "Layer effects", role: "group", children: [
4318
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "flex flex-wrap items-center gap-2", children: [
4319
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "font-semibold text-slate-300", children: "fx Effects" }),
4320
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("label", { className: "flex items-center gap-1 text-slate-300", children: [
4321
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
4322
+ "input",
4323
+ {
4324
+ type: "checkbox",
4325
+ "aria-label": "Effects on",
4326
+ checked: model.enabled,
4327
+ disabled: model.readOnly,
4328
+ onChange: (e) => attempt(() => controller.setLayerEffectsEnabled(id, e.target.checked)),
4329
+ className: "accent-indigo-500"
4330
+ }
4331
+ ),
4332
+ "On"
4333
+ ] }),
4334
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("label", { className: "flex items-center gap-1 text-slate-400", children: [
4335
+ "Scale",
4336
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
4337
+ "input",
4338
+ {
4339
+ type: "number",
4340
+ "aria-label": "Scale effects",
4341
+ min: 1,
4342
+ max: 1e3,
4343
+ step: 1,
4344
+ value: Math.round(model.scale * 100),
4345
+ disabled: model.readOnly,
4346
+ onChange: (e) => {
4347
+ const v = Number(e.target.value);
4348
+ if (v >= 1 && v <= 1e3) attempt(() => controller.setLayerEffectsEnabled(id, model.enabled, v / 100));
4349
+ },
4350
+ className: "w-14 bg-slate-800 border border-slate-700 rounded px-1"
4351
+ }
4352
+ ),
4353
+ "%"
4354
+ ] }),
4355
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
4356
+ "select",
4357
+ {
4358
+ "aria-label": "Add effect",
4359
+ value: "",
4360
+ disabled: model.readOnly,
4361
+ onChange: (e) => {
4362
+ const kind = e.target.value;
4363
+ if (!kind) return;
4364
+ attempt(() => setOpen(controller.addLayerEffect(id, kind).effect.id));
4365
+ },
4366
+ className: "bg-slate-800 border border-slate-700 rounded px-1.5 py-1",
4367
+ children: [
4368
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("option", { value: "", children: "Add effect\u2026" }),
4369
+ import_image_editor6.LAYER_EFFECT_MENU.map((m) => /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("option", { value: m.kind, children: m.label }, m.kind))
4370
+ ]
4371
+ }
4372
+ )
4373
+ ] }),
4374
+ model.readOnly && model.reason && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("p", { className: "text-slate-500", children: model.reason }),
4375
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("ul", { className: "flex flex-col gap-1", "aria-label": "Effects", children: model.rows.map((row) => {
4376
+ const effect = effects.items.find((e) => e.id === row.id);
4377
+ const expanded = open === row.id;
4378
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("li", { "data-effect-id": row.id, className: "rounded bg-slate-900/60 ring-1 ring-slate-800", children: [
4379
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "flex items-center gap-1.5 px-1.5 py-1", children: [
4380
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
4381
+ "input",
4382
+ {
4383
+ type: "checkbox",
4384
+ "aria-label": `${row.label} on`,
4385
+ checked: row.enabled,
4386
+ disabled: model.readOnly,
4387
+ onChange: (e) => attempt(() => controller.updateLayerEffect(id, row.id, { enabled: e.target.checked })),
4388
+ className: "accent-indigo-500"
4389
+ }
4390
+ ),
4391
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
4392
+ "button",
4393
+ {
4394
+ type: "button",
4395
+ "aria-expanded": expanded,
4396
+ className: `flex-1 text-left ${row.enabled && model.enabled ? "" : "text-slate-500"}`,
4397
+ onClick: () => setOpen(expanded ? null : row.id),
4398
+ children: row.label
4399
+ }
4400
+ ),
4401
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("button", { type: "button", className: button, "aria-label": `Remove ${row.label}`, disabled: model.readOnly, onClick: () => attempt(() => controller.removeLayerEffect(id, row.id)), children: "\u2715" })
4402
+ ] }),
4403
+ expanded && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "grid grid-cols-2 gap-x-2 gap-y-1 px-1.5 pb-1.5", "data-testid": "effect-settings", children: (0, import_image_editor6.layerEffectControls)(effect, patterns).map((c) => renderControl(row.id, c)) })
4404
+ ] }, row.id);
4405
+ }) }),
4406
+ error && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("p", { role: "alert", className: "text-rose-400", children: error })
4407
+ ] });
4408
+ };
4409
+
4410
+ // src/image-editor/LayersPanel.tsx
4411
+ var import_image_editor7 = require("@pixerate/editor/image-editor");
4412
+ var import_jsx_runtime22 = require("react/jsx-runtime");
4222
4413
  var iconButton = "px-1.5 py-0.5 rounded text-[10px] bg-slate-800 text-slate-300 hover:text-white disabled:opacity-40";
4223
4414
  var rowClass = (active) => `flex items-center gap-1.5 px-1.5 py-1 rounded cursor-pointer select-none ${active ? "bg-indigo-600/30 ring-1 ring-indigo-500" : "hover:bg-slate-800"}`;
4224
4415
  var Thumbnail = ({
@@ -4229,8 +4420,8 @@ var Thumbnail = ({
4229
4420
  selected,
4230
4421
  testId
4231
4422
  }) => {
4232
- const ref = (0, import_react38.useRef)(null);
4233
- (0, import_react38.useEffect)(() => {
4423
+ const ref = (0, import_react39.useRef)(null);
4424
+ (0, import_react39.useEffect)(() => {
4234
4425
  const canvas = ref.current;
4235
4426
  const ctx = canvas?.getContext("2d");
4236
4427
  if (!canvas || !ctx || !pixels || typeof ctx.createImageData !== "function") return;
@@ -4240,7 +4431,7 @@ var Thumbnail = ({
4240
4431
  img.data.set(pixels.data);
4241
4432
  ctx.putImageData(img, 0, 0);
4242
4433
  }, [pixels]);
4243
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
4434
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
4244
4435
  "canvas",
4245
4436
  {
4246
4437
  ref,
@@ -4253,15 +4444,15 @@ var Thumbnail = ({
4253
4444
  }
4254
4445
  );
4255
4446
  };
4256
- var LayersPanel = ({ controller, thumbnailSize = 32, psd = true, psdFileName = "image", className }) => {
4257
- const [, setTick] = (0, import_react38.useState)(0);
4258
- (0, import_react38.useEffect)(() => controller.subscribe(() => setTick((n) => n + 1)), [controller]);
4259
- const [error, setError] = (0, import_react38.useState)(null);
4260
- const [renaming, setRenaming] = (0, import_react38.useState)(null);
4261
- const [menu, setMenu] = (0, import_react38.useState)(null);
4262
- const [drag, setDrag] = (0, import_react38.useState)(null);
4263
- const [psdNotes, setPsdNotes] = (0, import_react38.useState)([]);
4264
- const fileInput = (0, import_react38.useRef)(null);
4447
+ var LayersPanel = ({ controller, thumbnailSize = 32, psd = true, psdFileName = "image", effects = true, className }) => {
4448
+ const [, setTick] = (0, import_react39.useState)(0);
4449
+ (0, import_react39.useEffect)(() => controller.subscribe(() => setTick((n) => n + 1)), [controller]);
4450
+ const [error, setError] = (0, import_react39.useState)(null);
4451
+ const [renaming, setRenaming] = (0, import_react39.useState)(null);
4452
+ const [menu, setMenu] = (0, import_react39.useState)(null);
4453
+ const [drag, setDrag] = (0, import_react39.useState)(null);
4454
+ const [psdNotes, setPsdNotes] = (0, import_react39.useState)([]);
4455
+ const fileInput = (0, import_react39.useRef)(null);
4265
4456
  const psdTask = async (fn) => {
4266
4457
  try {
4267
4458
  setError(null);
@@ -4270,9 +4461,9 @@ var LayersPanel = ({ controller, thumbnailSize = 32, psd = true, psdFileName = "
4270
4461
  setError(err instanceof Error ? err.message : String(err));
4271
4462
  }
4272
4463
  };
4273
- const commitNext = (0, import_react38.useRef)(true);
4464
+ const commitNext = (0, import_react39.useRef)(true);
4274
4465
  const state = controller.getState();
4275
- const rows = (0, import_image_editor6.layerPanelRows)(state.layers, state.activeLayerId);
4466
+ const rows = (0, import_image_editor7.layerPanelRows)(state.layers, state.activeLayerId);
4276
4467
  const active = controller.getActiveLayer();
4277
4468
  const attempt = (fn) => {
4278
4469
  try {
@@ -4284,7 +4475,7 @@ var LayersPanel = ({ controller, thumbnailSize = 32, psd = true, psdFileName = "
4284
4475
  };
4285
4476
  const run = async (action) => {
4286
4477
  setMenu(null);
4287
- setError(await (0, import_image_editor6.runLayerMenuAction)(controller, action));
4478
+ setError(await (0, import_image_editor7.runLayerMenuAction)(controller, action));
4288
4479
  };
4289
4480
  const prop = (key, value) => attempt(() => {
4290
4481
  controller.setLayerProperties(active.id, { [key]: value }, commitNext.current);
@@ -4302,20 +4493,20 @@ var LayersPanel = ({ controller, thumbnailSize = 32, psd = true, psdFileName = "
4302
4493
  if (!id) return;
4303
4494
  const rect = e.currentTarget.getBoundingClientRect();
4304
4495
  const fraction = rect.height ? (e.clientY - rect.top) / rect.height : 0.5;
4305
- const placement = (0, import_image_editor6.dropPlacement)(state.layers, id, row.layer.id, (0, import_image_editor6.dropZone)(fraction, row.layer.type === "group"));
4496
+ const placement = (0, import_image_editor7.dropPlacement)(state.layers, id, row.layer.id, (0, import_image_editor7.dropZone)(fraction, row.layer.type === "group"));
4306
4497
  if (placement) attempt(() => controller.moveLayer(id, placement));
4307
4498
  };
4308
4499
  const thumb = (layer) => layer.type === "pixel" ? controller.getLayerThumbnail(layer.id, { maxSide: 64 }) : null;
4309
4500
  const maskThumb = (layer) => layer.mask && !layer.mask.source ? controller.getLayerThumbnail(layer.id, { maxSide: 64, mask: true }) : null;
4310
4501
  const locked = (layer) => Boolean(layer.locks?.all || layer.locks?.pixels || layer.locks?.position || layer.locks?.transparency);
4311
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: `flex flex-col gap-2 text-xs text-slate-200 ${className ?? ""}`, "data-testid": "layers-panel", onClick: () => menu && setMenu(null), children: [
4312
- /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "flex flex-wrap items-center gap-1", role: "toolbar", "aria-label": "Layer actions", children: [
4313
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("button", { type: "button", className: iconButton, onClick: () => run("new-layer"), children: "New Layer" }),
4314
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("button", { type: "button", className: iconButton, onClick: () => run("new-group"), children: "New Group" }),
4315
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("button", { type: "button", className: iconButton, disabled: !active, onClick: () => run("duplicate"), children: "Duplicate" }),
4316
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("button", { type: "button", className: iconButton, disabled: !active, onClick: () => run("add-mask"), children: "Add Mask" }),
4317
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("button", { type: "button", className: iconButton, disabled: !active, onClick: () => run("delete"), children: "Delete" }),
4318
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
4502
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: `flex flex-col gap-2 text-xs text-slate-200 ${className ?? ""}`, "data-testid": "layers-panel", onClick: () => menu && setMenu(null), children: [
4503
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: "flex flex-wrap items-center gap-1", role: "toolbar", "aria-label": "Layer actions", children: [
4504
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("button", { type: "button", className: iconButton, onClick: () => run("new-layer"), children: "New Layer" }),
4505
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("button", { type: "button", className: iconButton, onClick: () => run("new-group"), children: "New Group" }),
4506
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("button", { type: "button", className: iconButton, disabled: !active, onClick: () => run("duplicate"), children: "Duplicate" }),
4507
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("button", { type: "button", className: iconButton, disabled: !active, onClick: () => run("add-mask"), children: "Add Mask" }),
4508
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("button", { type: "button", className: iconButton, disabled: !active, onClick: () => run("delete"), children: "Delete" }),
4509
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
4319
4510
  "button",
4320
4511
  {
4321
4512
  type: "button",
@@ -4330,39 +4521,39 @@ var LayersPanel = ({ controller, thumbnailSize = 32, psd = true, psdFileName = "
4330
4521
  children: "\u22EF"
4331
4522
  }
4332
4523
  ),
4333
- psd && /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_jsx_runtime21.Fragment, { children: [
4334
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("button", { type: "button", className: iconButton, onClick: () => fileInput.current?.click(), children: "Open PSD\u2026" }),
4335
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("button", { type: "button", className: iconButton, onClick: () => psdTask(async () => (await (0, import_image_editor6.downloadPsd)(controller, psdFileName)).warnings), children: "Export PSD" }),
4336
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
4524
+ psd && /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(import_jsx_runtime22.Fragment, { children: [
4525
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("button", { type: "button", className: iconButton, onClick: () => fileInput.current?.click(), children: "Open PSD\u2026" }),
4526
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("button", { type: "button", className: iconButton, onClick: () => psdTask(async () => (await (0, import_image_editor7.downloadPsd)(controller, psdFileName)).warnings), children: "Export PSD" }),
4527
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
4337
4528
  "input",
4338
4529
  {
4339
4530
  ref: fileInput,
4340
4531
  type: "file",
4341
- accept: import_image_editor6.PSD_FILE_ACCEPT,
4532
+ accept: import_image_editor7.PSD_FILE_ACCEPT,
4342
4533
  className: "hidden",
4343
4534
  "data-testid": "psd-file-input",
4344
4535
  "aria-label": "Open a PSD file",
4345
4536
  onChange: (e) => {
4346
4537
  const file = e.currentTarget.files?.[0];
4347
4538
  e.currentTarget.value = "";
4348
- if (file) psdTask(() => (0, import_image_editor6.openPsdFile)(controller, file));
4539
+ if (file) psdTask(() => (0, import_image_editor7.openPsdFile)(controller, file));
4349
4540
  }
4350
4541
  }
4351
4542
  )
4352
4543
  ] })
4353
4544
  ] }),
4354
- psdNotes.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("ul", { className: "rounded bg-amber-900/30 text-amber-200 px-2 py-1 space-y-0.5", "data-testid": "psd-warnings", "aria-label": "PSD warnings", children: [
4355
- psdNotes.map((n) => /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("li", { children: n }, n)),
4356
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("button", { type: "button", className: "underline", onClick: () => setPsdNotes([]), children: "Dismiss" }) })
4545
+ psdNotes.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("ul", { className: "rounded bg-amber-900/30 text-amber-200 px-2 py-1 space-y-0.5", "data-testid": "psd-warnings", "aria-label": "PSD warnings", children: [
4546
+ psdNotes.map((n) => /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("li", { children: n }, n)),
4547
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("button", { type: "button", className: "underline", onClick: () => setPsdNotes([]), children: "Dismiss" }) })
4357
4548
  ] }),
4358
- active && /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "grid grid-cols-2 gap-x-2 gap-y-1 items-center", "data-testid": "layer-properties", children: [
4359
- /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("label", { className: "flex flex-col gap-0.5", children: [
4360
- /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("span", { className: "text-slate-400", children: [
4549
+ active && /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: "grid grid-cols-2 gap-x-2 gap-y-1 items-center", "data-testid": "layer-properties", children: [
4550
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("label", { className: "flex flex-col gap-0.5", children: [
4551
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("span", { className: "text-slate-400", children: [
4361
4552
  "Opacity ",
4362
4553
  Math.round(active.opacity * 100),
4363
4554
  "%"
4364
4555
  ] }),
4365
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
4556
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
4366
4557
  "input",
4367
4558
  {
4368
4559
  type: "range",
@@ -4376,13 +4567,13 @@ var LayersPanel = ({ controller, thumbnailSize = 32, psd = true, psdFileName = "
4376
4567
  }
4377
4568
  )
4378
4569
  ] }),
4379
- /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("label", { className: "flex flex-col gap-0.5", children: [
4380
- /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("span", { className: "text-slate-400", children: [
4570
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("label", { className: "flex flex-col gap-0.5", children: [
4571
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("span", { className: "text-slate-400", children: [
4381
4572
  "Fill ",
4382
4573
  Math.round(((active.type === "pixel" ? active.fillOpacity : void 0) ?? 1) * 100),
4383
4574
  "%"
4384
4575
  ] }),
4385
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
4576
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
4386
4577
  "input",
4387
4578
  {
4388
4579
  type: "range",
@@ -4397,7 +4588,7 @@ var LayersPanel = ({ controller, thumbnailSize = 32, psd = true, psdFileName = "
4397
4588
  }
4398
4589
  )
4399
4590
  ] }),
4400
- /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
4591
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
4401
4592
  "select",
4402
4593
  {
4403
4594
  "aria-label": "Layer blend mode",
@@ -4405,13 +4596,13 @@ var LayersPanel = ({ controller, thumbnailSize = 32, psd = true, psdFileName = "
4405
4596
  value: active.blend,
4406
4597
  onChange: (e) => attempt(() => controller.setLayerProperties(active.id, { blend: e.target.value })),
4407
4598
  children: [
4408
- active.type === "group" && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("option", { value: "pass-through", children: "Pass Through" }),
4409
- import_image_editor6.BLEND_MODES.map((m) => /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("option", { value: m.id, children: m.label }, m.id))
4599
+ active.type === "group" && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("option", { value: "pass-through", children: "Pass Through" }),
4600
+ import_image_editor7.BLEND_MODES.map((m) => /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("option", { value: m.id, children: m.label }, m.id))
4410
4601
  ]
4411
4602
  }
4412
4603
  ),
4413
- /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("label", { className: "flex items-center gap-1.5 text-slate-300", children: [
4414
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
4604
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("label", { className: "flex items-center gap-1.5 text-slate-300", children: [
4605
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
4415
4606
  "input",
4416
4607
  {
4417
4608
  type: "checkbox",
@@ -4423,8 +4614,8 @@ var LayersPanel = ({ controller, thumbnailSize = 32, psd = true, psdFileName = "
4423
4614
  ),
4424
4615
  "Clip to below"
4425
4616
  ] }),
4426
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "col-span-2 flex flex-wrap gap-2 text-slate-400", role: "group", "aria-label": "Layer locks", children: ["pixels", "transparency", "position", "all"].map((k) => /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("label", { className: "flex items-center gap-1", children: [
4427
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
4617
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: "col-span-2 flex flex-wrap gap-2 text-slate-400", role: "group", "aria-label": "Layer locks", children: ["pixels", "transparency", "position", "all"].map((k) => /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("label", { className: "flex items-center gap-1", children: [
4618
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
4428
4619
  "input",
4429
4620
  {
4430
4621
  type: "checkbox",
@@ -4437,9 +4628,10 @@ var LayersPanel = ({ controller, thumbnailSize = 32, psd = true, psdFileName = "
4437
4628
  k
4438
4629
  ] }, k)) })
4439
4630
  ] }),
4440
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("ul", { className: "flex flex-col gap-0.5", role: "tree", "aria-label": "Layers", children: rows.map((row) => {
4631
+ effects && active && active.type !== "adjustment" && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(LayerEffectsPanel, { controller, className: "border-t border-slate-800 pt-1.5" }),
4632
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("ul", { className: "flex flex-col gap-0.5", role: "tree", "aria-label": "Layers", children: rows.map((row) => {
4441
4633
  const { layer } = row;
4442
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
4634
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
4443
4635
  "li",
4444
4636
  {
4445
4637
  role: "treeitem",
@@ -4469,7 +4661,7 @@ var LayersPanel = ({ controller, thumbnailSize = 32, psd = true, psdFileName = "
4469
4661
  className: rowClass(row.active),
4470
4662
  style: { paddingLeft: 6 + row.depth * 14 },
4471
4663
  children: [
4472
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
4664
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
4473
4665
  "button",
4474
4666
  {
4475
4667
  type: "button",
@@ -4483,7 +4675,7 @@ var LayersPanel = ({ controller, thumbnailSize = 32, psd = true, psdFileName = "
4483
4675
  children: layer.visible ? "\u25CF" : "\u25CB"
4484
4676
  }
4485
4677
  ),
4486
- layer.type === "group" && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
4678
+ layer.type === "group" && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
4487
4679
  "button",
4488
4680
  {
4489
4681
  type: "button",
@@ -4496,8 +4688,8 @@ var LayersPanel = ({ controller, thumbnailSize = 32, psd = true, psdFileName = "
4496
4688
  children: layer.collapsed ? "\u25B8" : "\u25BE"
4497
4689
  }
4498
4690
  ),
4499
- row.clipped && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "text-slate-500", "aria-label": "Clipped", children: "\u21B3" }),
4500
- layer.type === "pixel" ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
4691
+ row.clipped && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: "text-slate-500", "aria-label": "Clipped", children: "\u21B3" }),
4692
+ layer.type === "pixel" ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
4501
4693
  Thumbnail,
4502
4694
  {
4503
4695
  pixels: thumb(layer),
@@ -4505,15 +4697,31 @@ var LayersPanel = ({ controller, thumbnailSize = 32, psd = true, psdFileName = "
4505
4697
  label: `${row.label} pixels`,
4506
4698
  selected: row.active && state.activeTarget === "pixels" && Boolean(layer.mask)
4507
4699
  }
4508
- ) : /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
4700
+ ) : /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
4509
4701
  "span",
4510
4702
  {
4511
4703
  className: "shrink-0 grid place-items-center rounded bg-slate-800 text-[9px] text-slate-400 ring-1 ring-slate-700",
4512
4704
  style: { width: thumbnailSize, height: thumbnailSize },
4513
- children: (0, import_image_editor6.layerBadge)(layer)
4705
+ children: (0, import_image_editor7.layerBadge)(layer)
4514
4706
  }
4515
4707
  ),
4516
- layer.mask && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
4708
+ layer.mask && !layer.mask.source && layer.type === "pixel" && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
4709
+ "button",
4710
+ {
4711
+ type: "button",
4712
+ "aria-label": `${layer.mask.linked === false ? "Link" : "Unlink"} ${row.label} mask`,
4713
+ "aria-pressed": layer.mask.linked !== false,
4714
+ title: layer.mask.linked === false ? "Mask unlinked: it stays put when the layer moves or transforms. Click to link." : "Mask linked: it moves with the layer. Click to unlink.",
4715
+ "data-testid": "layer-mask-link",
4716
+ className: `shrink-0 text-[10px] leading-none ${layer.mask.linked === false ? "text-slate-600" : "text-slate-300"}`,
4717
+ onClick: (e) => {
4718
+ e.stopPropagation();
4719
+ attempt(() => controller.setLayerMaskProps(layer.id, { linked: layer.mask.linked === false }));
4720
+ },
4721
+ children: layer.mask.linked === false ? "\u26D3\uFE0E\u0338" : "\u26D3\uFE0E"
4722
+ }
4723
+ ),
4724
+ layer.mask && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
4517
4725
  Thumbnail,
4518
4726
  {
4519
4727
  pixels: maskThumb(layer),
@@ -4528,7 +4736,7 @@ var LayersPanel = ({ controller, thumbnailSize = 32, psd = true, psdFileName = "
4528
4736
  }
4529
4737
  }
4530
4738
  ),
4531
- renaming === layer.id ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
4739
+ renaming === layer.id ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
4532
4740
  "input",
4533
4741
  {
4534
4742
  autoFocus: true,
@@ -4545,7 +4753,7 @@ var LayersPanel = ({ controller, thumbnailSize = 32, psd = true, psdFileName = "
4545
4753
  if (e.key === "Escape") setRenaming(null);
4546
4754
  }
4547
4755
  }
4548
- ) : /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
4756
+ ) : /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
4549
4757
  "span",
4550
4758
  {
4551
4759
  className: `flex-1 min-w-0 truncate ${layer.visible ? "" : "text-slate-500"}`,
@@ -4556,15 +4764,30 @@ var LayersPanel = ({ controller, thumbnailSize = 32, psd = true, psdFileName = "
4556
4764
  children: row.label
4557
4765
  }
4558
4766
  ),
4559
- locked(layer) && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { "aria-label": "Locked", className: "text-slate-400", children: "\u{1F512}" }),
4560
- layer.blend !== "normal" && layer.blend !== "pass-through" && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "text-[9px] text-slate-500", children: layer.blend })
4767
+ (0, import_image_editor7.layerEffectsBadge)(layer) && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
4768
+ "button",
4769
+ {
4770
+ type: "button",
4771
+ "data-testid": "layer-fx-badge",
4772
+ "aria-label": (0, import_image_editor7.layerEffectsBadge)(layer) === "on" ? `Turn effects off for ${row.label}` : `Turn effects on for ${row.label}`,
4773
+ "aria-pressed": (0, import_image_editor7.layerEffectsBadge)(layer) === "on",
4774
+ className: `text-[9px] italic font-semibold ${(0, import_image_editor7.layerEffectsBadge)(layer) === "on" ? "text-indigo-300" : "text-slate-600 line-through"}`,
4775
+ onClick: (e) => {
4776
+ e.stopPropagation();
4777
+ attempt(() => controller.setLayerEffectsEnabled(layer.id, (0, import_image_editor7.layerEffectsBadge)(layer) !== "on"));
4778
+ },
4779
+ children: "fx"
4780
+ }
4781
+ ),
4782
+ locked(layer) && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { "aria-label": "Locked", className: "text-slate-400", children: "\u{1F512}" }),
4783
+ layer.blend !== "normal" && layer.blend !== "pass-through" && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: "text-[9px] text-slate-500", children: layer.blend })
4561
4784
  ]
4562
4785
  },
4563
4786
  layer.id
4564
4787
  );
4565
4788
  }) }),
4566
- error && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("p", { role: "alert", className: "text-rose-400", children: error }),
4567
- menu && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
4789
+ error && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("p", { role: "alert", className: "text-rose-400", children: error }),
4790
+ menu && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
4568
4791
  "div",
4569
4792
  {
4570
4793
  role: "menu",
@@ -4573,7 +4796,7 @@ var LayersPanel = ({ controller, thumbnailSize = 32, psd = true, psdFileName = "
4573
4796
  style: { left: menu.x, top: menu.y },
4574
4797
  onClick: (e) => e.stopPropagation(),
4575
4798
  onKeyDown: (e) => e.key === "Escape" && setMenu(null),
4576
- children: (0, import_image_editor6.layerMenu)(controller).map((item) => /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
4799
+ children: (0, import_image_editor7.layerMenu)(controller).map((item) => /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
4577
4800
  "button",
4578
4801
  {
4579
4802
  type: "button",
@@ -4591,14 +4814,14 @@ var LayersPanel = ({ controller, thumbnailSize = 32, psd = true, psdFileName = "
4591
4814
  };
4592
4815
 
4593
4816
  // src/image-editor/BrushOptionsBar.tsx
4594
- var import_react39 = require("react");
4595
- var import_image_editor7 = require("@pixerate/editor/image-editor");
4596
- var import_jsx_runtime22 = require("react/jsx-runtime");
4817
+ var import_react40 = require("react");
4818
+ var import_image_editor8 = require("@pixerate/editor/image-editor");
4819
+ var import_jsx_runtime23 = require("react/jsx-runtime");
4597
4820
  var chip = (on) => `px-2 py-1 rounded text-[11px] ${on ? "bg-indigo-600 text-white" : "bg-slate-800 text-slate-300 hover:text-white"}`;
4598
4821
  var BrushOptionsBar = ({ controller, className }) => {
4599
- const [, setTick] = (0, import_react39.useState)(0);
4600
- (0, import_react39.useEffect)(() => controller.subscribe(() => setTick((n) => n + 1)), [controller]);
4601
- const [error, setError] = (0, import_react39.useState)(null);
4822
+ const [, setTick] = (0, import_react40.useState)(0);
4823
+ (0, import_react40.useEffect)(() => controller.subscribe(() => setTick((n) => n + 1)), [controller]);
4824
+ const [error, setError] = (0, import_react40.useState)(null);
4602
4825
  const state = controller.getState();
4603
4826
  const { brush, foreground, background, mixer, mixerLoad } = state.paint;
4604
4827
  const attempt = (fn) => {
@@ -4609,8 +4832,8 @@ var BrushOptionsBar = ({ controller, className }) => {
4609
4832
  setError(err instanceof Error ? err.message : String(err));
4610
4833
  }
4611
4834
  };
4612
- return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: `flex flex-col gap-2 text-xs text-slate-200 ${className ?? ""}`, "data-testid": "brush-options", children: [
4613
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: "flex gap-1", role: "toolbar", "aria-label": "Paint tools", children: import_image_editor7.PAINT_TOOLS.map((t) => /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
4835
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: `flex flex-col gap-2 text-xs text-slate-200 ${className ?? ""}`, "data-testid": "brush-options", children: [
4836
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: "flex gap-1", role: "toolbar", "aria-label": "Paint tools", children: import_image_editor8.PAINT_TOOLS.map((t) => /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
4614
4837
  "button",
4615
4838
  {
4616
4839
  type: "button",
@@ -4622,9 +4845,9 @@ var BrushOptionsBar = ({ controller, className }) => {
4622
4845
  },
4623
4846
  t.id
4624
4847
  )) }),
4625
- /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: "flex items-center gap-2", role: "group", "aria-label": "Colours", children: [
4626
- /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("label", { className: "flex items-center gap-1", children: [
4627
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
4848
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "flex items-center gap-2", role: "group", "aria-label": "Colours", children: [
4849
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("label", { className: "flex items-center gap-1", children: [
4850
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
4628
4851
  "input",
4629
4852
  {
4630
4853
  type: "color",
@@ -4634,10 +4857,10 @@ var BrushOptionsBar = ({ controller, className }) => {
4634
4857
  className: "h-6 w-8 rounded border border-slate-700 bg-transparent"
4635
4858
  }
4636
4859
  ),
4637
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: "text-slate-400", children: "FG" })
4860
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { className: "text-slate-400", children: "FG" })
4638
4861
  ] }),
4639
- /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("label", { className: "flex items-center gap-1", children: [
4640
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
4862
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("label", { className: "flex items-center gap-1", children: [
4863
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
4641
4864
  "input",
4642
4865
  {
4643
4866
  type: "color",
@@ -4647,12 +4870,12 @@ var BrushOptionsBar = ({ controller, className }) => {
4647
4870
  className: "h-6 w-8 rounded border border-slate-700 bg-transparent"
4648
4871
  }
4649
4872
  ),
4650
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: "text-slate-400", children: "BG" })
4873
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { className: "text-slate-400", children: "BG" })
4651
4874
  ] }),
4652
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("button", { type: "button", title: "Swap colours (X)", className: chip(false), onClick: () => controller.swapPaintColors(), children: "Swap" }),
4653
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("button", { type: "button", title: "Default colours (D)", className: chip(false), onClick: () => controller.resetPaintColors(), children: "Default" })
4875
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("button", { type: "button", title: "Swap colours (X)", className: chip(false), onClick: () => controller.swapPaintColors(), children: "Swap" }),
4876
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("button", { type: "button", title: "Default colours (D)", className: chip(false), onClick: () => controller.resetPaintColors(), children: "Default" })
4654
4877
  ] }),
4655
- import_image_editor7.BRUSH_OPTION_CONTROLS.map((spec) => /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
4878
+ import_image_editor8.BRUSH_OPTION_CONTROLS.map((spec) => /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
4656
4879
  ControlSlider,
4657
4880
  {
4658
4881
  spec,
@@ -4661,21 +4884,21 @@ var BrushOptionsBar = ({ controller, className }) => {
4661
4884
  },
4662
4885
  spec.id
4663
4886
  )),
4664
- state.activeTool === "mixer" && /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: "flex flex-col gap-2 border-t border-slate-800 pt-2", role: "group", "aria-label": "Mixer Brush", "data-testid": "mixer-options", children: [
4665
- /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: "flex items-center gap-2", children: [
4666
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
4887
+ state.activeTool === "mixer" && /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "flex flex-col gap-2 border-t border-slate-800 pt-2", role: "group", "aria-label": "Mixer Brush", "data-testid": "mixer-options", children: [
4888
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "flex items-center gap-2", children: [
4889
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
4667
4890
  "span",
4668
4891
  {
4669
4892
  "aria-label": "Brush load",
4670
- title: mixerLoad.reservoir ? `Loaded ${(0, import_image_editor7.rgbUnitToHex)(mixerLoad.reservoir)}, ${Math.round(mixerLoad.amount * 100)}% left` : "Unloaded",
4893
+ title: mixerLoad.reservoir ? `Loaded ${(0, import_image_editor8.rgbUnitToHex)(mixerLoad.reservoir)}, ${Math.round(mixerLoad.amount * 100)}% left` : "Unloaded",
4671
4894
  className: "h-6 w-6 rounded border border-slate-700",
4672
- style: { background: mixerLoad.reservoir ? (0, import_image_editor7.rgbUnitToHex)(mixerLoad.reservoir) : "transparent" }
4895
+ style: { background: mixerLoad.reservoir ? (0, import_image_editor8.rgbUnitToHex)(mixerLoad.reservoir) : "transparent" }
4673
4896
  }
4674
4897
  ),
4675
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("button", { type: "button", title: "Fill the reservoir with the foreground colour", className: chip(false), onClick: () => controller.loadMixerBrush(), children: "Load brush" }),
4676
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("button", { type: "button", title: "Empty the picked-up colour", className: chip(false), onClick: () => controller.cleanMixerBrush(), children: "Clean brush" })
4898
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("button", { type: "button", title: "Fill the reservoir with the foreground colour", className: chip(false), onClick: () => controller.loadMixerBrush(), children: "Load brush" }),
4899
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("button", { type: "button", title: "Empty the picked-up colour", className: chip(false), onClick: () => controller.cleanMixerBrush(), children: "Clean brush" })
4677
4900
  ] }),
4678
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: "flex gap-1", role: "group", "aria-label": "Mixing", children: import_image_editor7.MIXER_MIXING_OPTIONS.map((m) => /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
4901
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: "flex gap-1", role: "group", "aria-label": "Mixing", children: import_image_editor8.MIXER_MIXING_OPTIONS.map((m) => /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
4679
4902
  "button",
4680
4903
  {
4681
4904
  type: "button",
@@ -4687,10 +4910,10 @@ var BrushOptionsBar = ({ controller, className }) => {
4687
4910
  },
4688
4911
  m.id
4689
4912
  )) }),
4690
- import_image_editor7.MIXER_OPTION_CONTROLS.map((spec) => /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(ControlSlider, { spec, value: mixer[spec.id], onChange: (v) => controller.setPaintOptions({ mixer: { [spec.id]: v } }) }, spec.id)),
4691
- /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: "flex flex-wrap gap-3 text-slate-300", children: [
4692
- /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("label", { className: "flex items-center gap-1.5", children: [
4693
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
4913
+ import_image_editor8.MIXER_OPTION_CONTROLS.map((spec) => /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(ControlSlider, { spec, value: mixer[spec.id], onChange: (v) => controller.setPaintOptions({ mixer: { [spec.id]: v } }) }, spec.id)),
4914
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "flex flex-wrap gap-3 text-slate-300", children: [
4915
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("label", { className: "flex items-center gap-1.5", children: [
4916
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
4694
4917
  "input",
4695
4918
  {
4696
4919
  type: "checkbox",
@@ -4702,8 +4925,8 @@ var BrushOptionsBar = ({ controller, className }) => {
4702
4925
  ),
4703
4926
  "Load each stroke"
4704
4927
  ] }),
4705
- /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("label", { className: "flex items-center gap-1.5", children: [
4706
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
4928
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("label", { className: "flex items-center gap-1.5", children: [
4929
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
4707
4930
  "input",
4708
4931
  {
4709
4932
  type: "checkbox",
@@ -4717,9 +4940,9 @@ var BrushOptionsBar = ({ controller, className }) => {
4717
4940
  ] })
4718
4941
  ] })
4719
4942
  ] }),
4720
- /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: "flex flex-wrap gap-3 text-slate-300", role: "group", "aria-label": "Pressure", children: [
4721
- /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("label", { className: "flex items-center gap-1.5", children: [
4722
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
4943
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "flex flex-wrap gap-3 text-slate-300", role: "group", "aria-label": "Pressure", children: [
4944
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("label", { className: "flex items-center gap-1.5", children: [
4945
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
4723
4946
  "input",
4724
4947
  {
4725
4948
  type: "checkbox",
@@ -4731,8 +4954,8 @@ var BrushOptionsBar = ({ controller, className }) => {
4731
4954
  ),
4732
4955
  "Pressure size"
4733
4956
  ] }),
4734
- /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("label", { className: "flex items-center gap-1.5", children: [
4735
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
4957
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("label", { className: "flex items-center gap-1.5", children: [
4958
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
4736
4959
  "input",
4737
4960
  {
4738
4961
  type: "checkbox",
@@ -4745,18 +4968,18 @@ var BrushOptionsBar = ({ controller, className }) => {
4745
4968
  "Pressure opacity"
4746
4969
  ] })
4747
4970
  ] }),
4748
- /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: "flex gap-1", children: [
4749
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("button", { type: "button", className: chip(false), onClick: () => attempt(() => controller.fillPixels()), children: "Fill" }),
4750
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("button", { type: "button", className: chip(false), onClick: () => attempt(() => controller.clearPixels()), children: "Clear" })
4971
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "flex gap-1", children: [
4972
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("button", { type: "button", className: chip(false), onClick: () => attempt(() => controller.fillPixels()), children: "Fill" }),
4973
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("button", { type: "button", className: chip(false), onClick: () => attempt(() => controller.clearPixels()), children: "Clear" })
4751
4974
  ] }),
4752
- error && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("p", { role: "alert", className: "text-rose-400", children: error })
4975
+ error && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("p", { role: "alert", className: "text-rose-400", children: error })
4753
4976
  ] });
4754
4977
  };
4755
4978
 
4756
4979
  // src/image-editor/SelectionOptionsBar.tsx
4757
- var import_react40 = require("react");
4758
- var import_image_editor8 = require("@pixerate/editor/image-editor");
4759
- var import_jsx_runtime23 = require("react/jsx-runtime");
4980
+ var import_react41 = require("react");
4981
+ var import_image_editor9 = require("@pixerate/editor/image-editor");
4982
+ var import_jsx_runtime24 = require("react/jsx-runtime");
4760
4983
  var MODIFY_OPS = [
4761
4984
  { op: "grow", label: "Grow" },
4762
4985
  { op: "shrink", label: "Shrink" },
@@ -4773,20 +4996,20 @@ var SelectionOptionsBar = ({
4773
4996
  quickMask,
4774
4997
  className = ""
4775
4998
  }) => {
4776
- const [radius, setRadius] = (0, import_react40.useState)(2);
4999
+ const [radius, setRadius] = (0, import_react41.useState)(2);
4777
5000
  const set = (patch) => onOptionsChange({ ...options, ...patch });
4778
5001
  const sel = selection === void 0 ? controller.getSelection() : selection;
4779
5002
  const qm = quickMask ?? Boolean(controller.getState().quickMask);
4780
- return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { "data-testid": "selection-options-bar", className: `flex flex-col gap-3 text-xs ${className}`, children: [
4781
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { role: "group", "aria-label": "Selection tool", className: "flex flex-wrap gap-1", children: import_image_editor8.SELECTION_TOOLS.map((t) => /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("button", { type: "button", "aria-pressed": options.tool === t.id, className: btn(options.tool === t.id), onClick: () => set({ tool: t.id }), children: t.label }, t.id)) }),
4782
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { role: "group", "aria-label": "Selection mode", className: "flex flex-wrap gap-1", children: import_image_editor8.SELECTION_MODES.map((m) => /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("button", { type: "button", "aria-pressed": options.mode === m, className: btn(options.mode === m), onClick: () => set({ mode: m }), children: import_image_editor8.SELECTION_MODE_LABELS[m] }, m)) }),
4783
- options.tool === "wand" && /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "flex flex-col gap-2", children: [
4784
- /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("label", { className: "flex flex-col gap-1", children: [
4785
- /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("span", { className: "flex justify-between text-slate-400", children: [
4786
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { children: "Tolerance" }),
4787
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { className: "font-mono", children: options.tolerance })
5003
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { "data-testid": "selection-options-bar", className: `flex flex-col gap-3 text-xs ${className}`, children: [
5004
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { role: "group", "aria-label": "Selection tool", className: "flex flex-wrap gap-1", children: import_image_editor9.SELECTION_TOOLS.map((t) => /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("button", { type: "button", "aria-pressed": options.tool === t.id, className: btn(options.tool === t.id), onClick: () => set({ tool: t.id }), children: t.label }, t.id)) }),
5005
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { role: "group", "aria-label": "Selection mode", className: "flex flex-wrap gap-1", children: import_image_editor9.SELECTION_MODES.map((m) => /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("button", { type: "button", "aria-pressed": options.mode === m, className: btn(options.mode === m), onClick: () => set({ mode: m }), children: import_image_editor9.SELECTION_MODE_LABELS[m] }, m)) }),
5006
+ options.tool === "wand" && /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className: "flex flex-col gap-2", children: [
5007
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("label", { className: "flex flex-col gap-1", children: [
5008
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("span", { className: "flex justify-between text-slate-400", children: [
5009
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("span", { children: "Tolerance" }),
5010
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("span", { className: "font-mono", children: options.tolerance })
4788
5011
  ] }),
4789
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
5012
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
4790
5013
  "input",
4791
5014
  {
4792
5015
  type: "range",
@@ -4800,17 +5023,17 @@ var SelectionOptionsBar = ({
4800
5023
  }
4801
5024
  )
4802
5025
  ] }),
4803
- /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("label", { className: "flex items-center gap-2 text-slate-300", children: [
4804
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("input", { type: "checkbox", checked: options.contiguous, onChange: (e) => set({ contiguous: e.target.checked }) }),
5026
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("label", { className: "flex items-center gap-2 text-slate-300", children: [
5027
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("input", { type: "checkbox", checked: options.contiguous, onChange: (e) => set({ contiguous: e.target.checked }) }),
4805
5028
  "Contiguous"
4806
5029
  ] }),
4807
- /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("label", { className: "flex items-center gap-2 text-slate-300", children: [
4808
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("input", { type: "checkbox", checked: options.sampleMerged, onChange: (e) => set({ sampleMerged: e.target.checked }) }),
5030
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("label", { className: "flex items-center gap-2 text-slate-300", children: [
5031
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("input", { type: "checkbox", checked: options.sampleMerged, onChange: (e) => set({ sampleMerged: e.target.checked }) }),
4809
5032
  "Sample adjusted image"
4810
5033
  ] }),
4811
- /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("label", { className: "flex items-center gap-2 text-slate-300", children: [
5034
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("label", { className: "flex items-center gap-2 text-slate-300", children: [
4812
5035
  "Sample",
4813
- /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
5036
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(
4814
5037
  "select",
4815
5038
  {
4816
5039
  "aria-label": "Sample from",
@@ -4818,27 +5041,27 @@ var SelectionOptionsBar = ({
4818
5041
  onChange: (e) => set({ sample: e.target.value }),
4819
5042
  className: "bg-slate-800 border border-slate-700 rounded px-1 py-0.5",
4820
5043
  children: [
4821
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("option", { value: "composite", children: "All layers" }),
4822
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("option", { value: "active-layer", children: "Active layer" })
5044
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("option", { value: "composite", children: "All layers" }),
5045
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("option", { value: "active-layer", children: "Active layer" })
4823
5046
  ]
4824
5047
  }
4825
5048
  )
4826
5049
  ] })
4827
5050
  ] }),
4828
- /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("label", { className: "flex items-center gap-2 text-slate-300", children: [
4829
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("input", { type: "checkbox", checked: options.antiAlias, onChange: (e) => set({ antiAlias: e.target.checked }) }),
5051
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("label", { className: "flex items-center gap-2 text-slate-300", children: [
5052
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("input", { type: "checkbox", checked: options.antiAlias, onChange: (e) => set({ antiAlias: e.target.checked }) }),
4830
5053
  "Anti-alias"
4831
5054
  ] }),
4832
- /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "flex flex-wrap gap-1", children: [
4833
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("button", { type: "button", className: btn(false), onClick: () => controller.selectAll(), children: "Select All" }),
4834
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("button", { type: "button", className: btn(false), disabled: !sel, onClick: () => controller.deselect(), children: "Deselect" }),
4835
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("button", { type: "button", className: btn(false), onClick: () => controller.invertSelection(), children: "Inverse" }),
4836
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("button", { type: "button", "aria-pressed": qm, className: btn(qm), onClick: () => controller.setQuickMask(!qm), children: "Quick Mask" })
5055
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className: "flex flex-wrap gap-1", children: [
5056
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("button", { type: "button", className: btn(false), onClick: () => controller.selectAll(), children: "Select All" }),
5057
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("button", { type: "button", className: btn(false), disabled: !sel, onClick: () => controller.deselect(), children: "Deselect" }),
5058
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("button", { type: "button", className: btn(false), onClick: () => controller.invertSelection(), children: "Inverse" }),
5059
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("button", { type: "button", "aria-pressed": qm, className: btn(qm), onClick: () => controller.setQuickMask(!qm), children: "Quick Mask" })
4837
5060
  ] }),
4838
- /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "flex flex-wrap items-center gap-1", children: [
4839
- /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("label", { className: "flex items-center gap-1 text-slate-400", children: [
5061
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className: "flex flex-wrap items-center gap-1", children: [
5062
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("label", { className: "flex items-center gap-1 text-slate-400", children: [
4840
5063
  "Radius",
4841
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
5064
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
4842
5065
  "input",
4843
5066
  {
4844
5067
  type: "number",
@@ -4852,24 +5075,24 @@ var SelectionOptionsBar = ({
4852
5075
  }
4853
5076
  )
4854
5077
  ] }),
4855
- MODIFY_OPS.map(({ op, label }) => /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("button", { type: "button", className: btn(false), disabled: !sel, onClick: () => controller.modifySelection(op, radius), children: label }, op))
5078
+ MODIFY_OPS.map(({ op, label }) => /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("button", { type: "button", className: btn(false), disabled: !sel, onClick: () => controller.modifySelection(op, radius), children: label }, op))
4856
5079
  ] }),
4857
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("p", { "data-testid": "selection-status", className: "text-slate-400", children: (0, import_image_editor8.describeSelection)(sel) })
5080
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("p", { "data-testid": "selection-status", className: "text-slate-400", children: (0, import_image_editor9.describeSelection)(sel) })
4858
5081
  ] });
4859
5082
  };
4860
5083
 
4861
5084
  // src/image-editor/RefineEdgePanel.tsx
4862
- var import_react41 = require("react");
4863
- var import_image_editor9 = require("@pixerate/editor/image-editor");
4864
- var import_jsx_runtime24 = require("react/jsx-runtime");
5085
+ var import_react42 = require("react");
5086
+ var import_image_editor10 = require("@pixerate/editor/image-editor");
5087
+ var import_jsx_runtime25 = require("react/jsx-runtime");
4865
5088
  var DEFAULT_PARAMS = { radius: 8, smartRadius: false, smooth: 0, feather: 0, contrast: 0, shiftEdge: 0 };
4866
- var button = "px-2 py-1 rounded-md border border-slate-800 bg-slate-950 text-[11px] text-slate-200 hover:text-white disabled:opacity-40";
5089
+ var button2 = "px-2 py-1 rounded-md border border-slate-800 bg-slate-950 text-[11px] text-slate-200 hover:text-white disabled:opacity-40";
4867
5090
  var RefineEdgePanel = ({ controller, selection, layers, className = "" }) => {
4868
- const [params, setParams] = (0, import_react41.useState)(DEFAULT_PARAMS);
4869
- const [busy, setBusy] = (0, import_react41.useState)(false);
4870
- const [error, setError] = (0, import_react41.useState)(null);
4871
- const [layerId, setLayerId] = (0, import_react41.useState)("");
4872
- const [newKind, setNewKind] = (0, import_react41.useState)("hueSaturation");
5091
+ const [params, setParams] = (0, import_react42.useState)(DEFAULT_PARAMS);
5092
+ const [busy, setBusy] = (0, import_react42.useState)(false);
5093
+ const [error, setError] = (0, import_react42.useState)(null);
5094
+ const [layerId, setLayerId] = (0, import_react42.useState)("");
5095
+ const [newKind, setNewKind] = (0, import_react42.useState)("hueSaturation");
4873
5096
  const sel = selection === void 0 ? controller.getSelection() : selection;
4874
5097
  const list = layers ?? controller.getAdjustmentLayers();
4875
5098
  const target = list.find((l) => l.id === layerId) ?? list[list.length - 1];
@@ -4884,17 +5107,17 @@ var RefineEdgePanel = ({ controller, selection, layers, className = "" }) => {
4884
5107
  setBusy(false);
4885
5108
  }
4886
5109
  };
4887
- return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { "data-testid": "refine-edge-panel", className: `flex flex-col gap-3 text-xs ${className}`, children: [
4888
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Refine Edge" }),
4889
- import_image_editor9.REFINE_EDGE_CONTROLS.map((c) => /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("label", { className: "flex flex-col gap-1", children: [
4890
- /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("span", { className: "flex justify-between text-slate-400", children: [
4891
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("span", { children: c.label }),
4892
- /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("span", { className: "font-mono", children: [
5110
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { "data-testid": "refine-edge-panel", className: `flex flex-col gap-3 text-xs ${className}`, children: [
5111
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Refine Edge" }),
5112
+ import_image_editor10.REFINE_EDGE_CONTROLS.map((c) => /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("label", { className: "flex flex-col gap-1", children: [
5113
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("span", { className: "flex justify-between text-slate-400", children: [
5114
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { children: c.label }),
5115
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("span", { className: "font-mono", children: [
4893
5116
  params[c.id],
4894
5117
  c.unit
4895
5118
  ] })
4896
5119
  ] }),
4897
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
5120
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
4898
5121
  "input",
4899
5122
  {
4900
5123
  type: "range",
@@ -4908,67 +5131,67 @@ var RefineEdgePanel = ({ controller, selection, layers, className = "" }) => {
4908
5131
  }
4909
5132
  )
4910
5133
  ] }, c.id)),
4911
- /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("label", { className: "flex items-center gap-2 text-slate-300", children: [
4912
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("input", { type: "checkbox", checked: params.smartRadius, onChange: (e) => setParams({ ...params, smartRadius: e.target.checked }) }),
5134
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("label", { className: "flex items-center gap-2 text-slate-300", children: [
5135
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("input", { type: "checkbox", checked: params.smartRadius, onChange: (e) => setParams({ ...params, smartRadius: e.target.checked }) }),
4913
5136
  "Smart Radius"
4914
5137
  ] }),
4915
- /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className: "flex gap-1", children: [
4916
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("button", { type: "button", className: button, disabled: !sel || busy, onClick: refine, children: "Refine Edge" }),
4917
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("button", { type: "button", className: button, onClick: () => setParams(DEFAULT_PARAMS), children: "Reset" })
5138
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: "flex gap-1", children: [
5139
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("button", { type: "button", className: button2, disabled: !sel || busy, onClick: refine, children: "Refine Edge" }),
5140
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("button", { type: "button", className: button2, onClick: () => setParams(DEFAULT_PARAMS), children: "Reset" })
4918
5141
  ] }),
4919
- error && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("p", { role: "alert", className: "text-rose-400", children: error }),
4920
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px] mt-2", children: "Layer Mask" }),
4921
- /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className: "flex gap-1 items-center", children: [
4922
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
5142
+ error && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("p", { role: "alert", className: "text-rose-400", children: error }),
5143
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px] mt-2", children: "Layer Mask" }),
5144
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: "flex gap-1 items-center", children: [
5145
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
4923
5146
  "select",
4924
5147
  {
4925
5148
  "aria-label": "New masked layer kind",
4926
5149
  value: newKind,
4927
5150
  onChange: (e) => setNewKind(e.target.value),
4928
5151
  className: "flex-1 bg-slate-950 border border-slate-800 rounded px-1 py-1 text-slate-200",
4929
- children: import_image_editor9.ADJUSTMENT_KINDS.map((k) => /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("option", { value: k.kind, children: k.label }, k.kind))
5152
+ children: import_image_editor10.ADJUSTMENT_KINDS.map((k) => /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("option", { value: k.kind, children: k.label }, k.kind))
4930
5153
  }
4931
5154
  ),
4932
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
5155
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
4933
5156
  "button",
4934
5157
  {
4935
5158
  type: "button",
4936
- className: button,
5159
+ className: button2,
4937
5160
  disabled: !sel,
4938
- onClick: () => sel && setLayerId(controller.addAdjustmentLayer({ kind: newKind, mask: (0, import_image_editor9.layerMaskFromSelection)(sel) })),
5161
+ onClick: () => sel && setLayerId(controller.addAdjustmentLayer({ kind: newKind, mask: (0, import_image_editor10.layerMaskFromSelection)(sel) })),
4939
5162
  children: "New Masked Layer"
4940
5163
  }
4941
5164
  )
4942
5165
  ] }),
4943
- list.length > 0 && target && /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className: "flex flex-col gap-2", children: [
4944
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
5166
+ list.length > 0 && target && /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: "flex flex-col gap-2", children: [
5167
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
4945
5168
  "select",
4946
5169
  {
4947
5170
  "aria-label": "Mask layer",
4948
5171
  value: target.id,
4949
5172
  onChange: (e) => setLayerId(e.target.value),
4950
5173
  className: "bg-slate-950 border border-slate-800 rounded px-1 py-1 text-slate-200",
4951
- children: list.map((l) => /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("option", { value: l.id, children: [
4952
- l.name || (0, import_image_editor9.adjustmentKindLabel)(l.kind),
5174
+ children: list.map((l) => /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("option", { value: l.id, children: [
5175
+ l.name || (0, import_image_editor10.adjustmentKindLabel)(l.kind),
4953
5176
  l.mask ? " (masked)" : ""
4954
5177
  ] }, l.id))
4955
5178
  }
4956
5179
  ),
4957
- /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className: "flex flex-wrap gap-1", children: [
4958
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("button", { type: "button", className: button, disabled: !sel, onClick: () => controller.applySelectionAsLayerMask(target.id), children: "Mask with Selection" }),
4959
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("button", { type: "button", className: button, disabled: !target.mask, onClick: () => controller.selectionFromLayerMask(target.id), children: "Load as Selection" }),
4960
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("button", { type: "button", className: button, disabled: !target.mask, onClick: () => controller.setAdjustmentLayerMask(target.id, null), children: "Remove Mask" })
5180
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: "flex flex-wrap gap-1", children: [
5181
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("button", { type: "button", className: button2, disabled: !sel, onClick: () => controller.applySelectionAsLayerMask(target.id), children: "Mask with Selection" }),
5182
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("button", { type: "button", className: button2, disabled: !target.mask, onClick: () => controller.selectionFromLayerMask(target.id), children: "Load as Selection" }),
5183
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("button", { type: "button", className: button2, disabled: !target.mask, onClick: () => controller.setAdjustmentLayerMask(target.id, null), children: "Remove Mask" })
4961
5184
  ] }),
4962
- target.mask && /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(import_jsx_runtime24.Fragment, { children: [
4963
- /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("label", { className: "flex flex-col gap-1", children: [
4964
- /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("span", { className: "flex justify-between text-slate-400", children: [
4965
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("span", { children: "Density" }),
4966
- /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("span", { className: "font-mono", children: [
5185
+ target.mask && /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(import_jsx_runtime25.Fragment, { children: [
5186
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("label", { className: "flex flex-col gap-1", children: [
5187
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("span", { className: "flex justify-between text-slate-400", children: [
5188
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { children: "Density" }),
5189
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("span", { className: "font-mono", children: [
4967
5190
  Math.round((target.mask.density ?? 1) * 100),
4968
5191
  "%"
4969
5192
  ] })
4970
5193
  ] }),
4971
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
5194
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
4972
5195
  "input",
4973
5196
  {
4974
5197
  type: "range",
@@ -4982,15 +5205,15 @@ var RefineEdgePanel = ({ controller, selection, layers, className = "" }) => {
4982
5205
  }
4983
5206
  )
4984
5207
  ] }),
4985
- /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("label", { className: "flex flex-col gap-1", children: [
4986
- /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("span", { className: "flex justify-between text-slate-400", children: [
4987
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("span", { children: "Mask Feather" }),
4988
- /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("span", { className: "font-mono", children: [
5208
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("label", { className: "flex flex-col gap-1", children: [
5209
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("span", { className: "flex justify-between text-slate-400", children: [
5210
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { children: "Mask Feather" }),
5211
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("span", { className: "font-mono", children: [
4989
5212
  target.mask.feather ?? 0,
4990
5213
  "px"
4991
5214
  ] })
4992
5215
  ] }),
4993
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
5216
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
4994
5217
  "input",
4995
5218
  {
4996
5219
  type: "range",
@@ -5009,8 +5232,83 @@ var RefineEdgePanel = ({ controller, selection, layers, className = "" }) => {
5009
5232
  ] });
5010
5233
  };
5011
5234
 
5235
+ // src/image-editor/FreeTransformOptionsBar.tsx
5236
+ var import_react43 = require("react");
5237
+ var import_image_editor11 = require("@pixerate/editor/image-editor");
5238
+ var import_jsx_runtime26 = require("react/jsx-runtime");
5239
+ var chip2 = (on) => `px-2 py-1 rounded text-[11px] ${on ? "bg-indigo-600 text-white" : "bg-slate-800 text-slate-300 hover:text-white"}`;
5240
+ var round = (v, unit) => unit === "px" ? Math.round(v * 10) / 10 : Math.round(v * 100) / 100;
5241
+ var FreeTransformOptionsBar = ({ tool, className }) => {
5242
+ const [, setTick] = (0, import_react43.useState)(0);
5243
+ (0, import_react43.useEffect)(() => tool.subscribe(() => setTick((n) => n + 1)), [tool]);
5244
+ const bar = tool.bar();
5245
+ if (!tool.active || !bar) return null;
5246
+ const apply = (field, raw) => {
5247
+ const v = Number(raw);
5248
+ if (raw.trim() !== "" && Number.isFinite(v) && Math.abs(v - bar[field]) > 1e-9) tool.setField(field, v);
5249
+ };
5250
+ return /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("div", { className: `flex flex-wrap items-center gap-2 text-xs text-slate-200 ${className ?? ""}`, "data-testid": "free-transform-options", role: "toolbar", "aria-label": "Free Transform options", children: [
5251
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("div", { className: "flex gap-1", role: "group", "aria-label": "Transform mode", children: import_image_editor11.FREE_TRANSFORM_MODE_OPTIONS.map((m) => /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("button", { type: "button", title: m.title, "aria-pressed": tool.mode === m.id, className: chip2(tool.mode === m.id), onClick: () => tool.setMode(m.id), children: m.label }, m.id)) }),
5252
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("div", { className: "grid grid-cols-3 gap-px", role: "group", "aria-label": "Reference point", children: import_image_editor11.TRANSFORM_ANCHORS.map((a) => /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("button", { type: "button", "aria-label": `Reference point ${a}`, title: `Reference point: ${a}`, className: "h-2.5 w-2.5 rounded-sm bg-slate-700 hover:bg-indigo-500", onClick: () => tool.setReference(a) }, a)) }),
5253
+ import_image_editor11.FREE_TRANSFORM_FIELDS.map((f) => /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("label", { className: "flex items-center gap-1", title: f.title, children: [
5254
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("span", { className: "text-slate-400", children: f.label }),
5255
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
5256
+ "input",
5257
+ {
5258
+ type: "number",
5259
+ "aria-label": f.title,
5260
+ defaultValue: round(bar[f.id], f.unit),
5261
+ disabled: !bar.affine,
5262
+ step: f.unit === "px" ? 1 : 0.1,
5263
+ className: "w-16 rounded border border-slate-700 bg-slate-900 px-1 py-0.5 text-right disabled:opacity-40",
5264
+ onBlur: (e) => apply(f.id, e.currentTarget.value),
5265
+ onKeyDown: (e) => {
5266
+ if (e.key === "Enter") {
5267
+ e.preventDefault();
5268
+ e.stopPropagation();
5269
+ apply(f.id, e.currentTarget.value);
5270
+ }
5271
+ }
5272
+ },
5273
+ `${f.id}:${round(bar[f.id], f.unit)}`
5274
+ ),
5275
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("span", { className: "text-slate-500", children: f.unit }),
5276
+ f.id === "scaleX" && /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
5277
+ "button",
5278
+ {
5279
+ type: "button",
5280
+ "aria-label": "Keep width and height proportional",
5281
+ "aria-pressed": tool.proportional,
5282
+ className: chip2(tool.proportional),
5283
+ onClick: () => {
5284
+ tool.proportional = !tool.proportional;
5285
+ setTick((n) => n + 1);
5286
+ },
5287
+ children: "\u26D3\uFE0E"
5288
+ }
5289
+ )
5290
+ ] }, f.id)),
5291
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("label", { className: "flex items-center gap-1", children: [
5292
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("span", { className: "text-slate-400", children: "Interpolation" }),
5293
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
5294
+ "select",
5295
+ {
5296
+ "aria-label": "Interpolation",
5297
+ value: tool.interpolation,
5298
+ onChange: (e) => tool.setInterpolation(e.target.value),
5299
+ className: "rounded border border-slate-700 bg-slate-900 px-1 py-0.5",
5300
+ children: import_image_editor11.INTERPOLATIONS.map((i) => /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("option", { value: i, children: i[0].toUpperCase() + i.slice(1) }, i))
5301
+ }
5302
+ )
5303
+ ] }),
5304
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("button", { type: "button", title: "Commit (Enter)", className: "px-2 py-1 rounded bg-emerald-600 text-white hover:bg-emerald-500", onClick: () => tool.commit(), children: "Commit" }),
5305
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("button", { type: "button", title: "Cancel (Esc)", className: chip2(false), onClick: () => tool.cancel(), children: "Cancel" }),
5306
+ tool.error && /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("span", { role: "alert", className: "text-rose-400", children: tool.error })
5307
+ ] });
5308
+ };
5309
+
5012
5310
  // src/image-editor/ImageEditor.tsx
5013
- var import_jsx_runtime25 = require("react/jsx-runtime");
5311
+ var import_jsx_runtime27 = require("react/jsx-runtime");
5014
5312
  var ImageEditor = ({
5015
5313
  editor: externalEditor,
5016
5314
  src,
@@ -5025,35 +5323,47 @@ var ImageEditor = ({
5025
5323
  const internalEditor = useImageEditor({ image: src });
5026
5324
  const editor = externalEditor || internalEditor;
5027
5325
  const { state, controller } = editor;
5028
- const legacyProcess = (0, import_image_editor10.normalizeProcessVersion)(state.adjustments.process) < import_image_editor10.LATEST_PROCESS_VERSION;
5029
- const canvasRef = (0, import_react42.useRef)(null);
5030
- const containerRef = (0, import_react42.useRef)(null);
5031
- const [activeTab, setActiveTab] = (0, import_react42.useState)("crop");
5032
- const [activeCropRatio, setActiveCropRatio] = (0, import_react42.useState)(null);
5033
- const [selectionOptions, setSelectionOptions] = (0, import_react42.useState)(import_image_editor10.DEFAULT_SELECTION_TOOL_OPTIONS);
5034
- const selectionOptionsRef = (0, import_react42.useRef)(selectionOptions);
5326
+ const legacyProcess = (0, import_image_editor12.normalizeProcessVersion)(state.adjustments.process) < import_image_editor12.LATEST_PROCESS_VERSION;
5327
+ const canvasRef = (0, import_react44.useRef)(null);
5328
+ const containerRef = (0, import_react44.useRef)(null);
5329
+ const [activeTab, setActiveTab] = (0, import_react44.useState)("crop");
5330
+ const [activeCropRatio, setActiveCropRatio] = (0, import_react44.useState)(null);
5331
+ const [selectionOptions, setSelectionOptions] = (0, import_react44.useState)(import_image_editor12.DEFAULT_SELECTION_TOOL_OPTIONS);
5332
+ const selectionOptionsRef = (0, import_react44.useRef)(selectionOptions);
5035
5333
  selectionOptionsRef.current = selectionOptions;
5036
- const selectionGestureRef = (0, import_react42.useRef)(null);
5334
+ const selectionGestureRef = (0, import_react44.useRef)(null);
5037
5335
  if (!selectionGestureRef.current || selectionGestureRef.current.controller !== controller) {
5038
- selectionGestureRef.current = new import_image_editor10.SelectionGesture(controller, () => selectionOptionsRef.current);
5336
+ selectionGestureRef.current = new import_image_editor12.SelectionGesture(controller, () => selectionOptionsRef.current);
5039
5337
  }
5040
- const selectionDraftRef = (0, import_react42.useRef)(null);
5041
- const [selectionError, setSelectionError] = (0, import_react42.useState)(null);
5042
- const paintGestureRef = (0, import_react42.useRef)(null);
5043
- const paintControllerRef = (0, import_react42.useRef)(controller);
5338
+ const selectionDraftRef = (0, import_react44.useRef)(null);
5339
+ const [selectionError, setSelectionError] = (0, import_react44.useState)(null);
5340
+ const paintGestureRef = (0, import_react44.useRef)(null);
5341
+ const paintControllerRef = (0, import_react44.useRef)(controller);
5044
5342
  if (!paintGestureRef.current || paintControllerRef.current !== controller) {
5045
- paintGestureRef.current = new import_image_editor10.PaintGesture(controller);
5343
+ paintGestureRef.current = new import_image_editor12.PaintGesture(controller);
5046
5344
  paintControllerRef.current = controller;
5047
5345
  }
5048
- const brushCursorRef = (0, import_react42.useRef)(null);
5049
- const [paintError, setPaintError] = (0, import_react42.useState)(null);
5050
- const [inpaintPrompt, setInpaintPrompt] = (0, import_react42.useState)("");
5051
- const [inpaintMaskMode, setInpaintMaskMode] = (0, import_react42.useState)("annotations");
5052
- const [isRemovingBg, setIsRemovingBg] = (0, import_react42.useState)(false);
5053
- const [isInpainting, setIsInpainting] = (0, import_react42.useState)(false);
5054
- const [aiError, setAiError] = (0, import_react42.useState)(null);
5055
- const [aiSuccess, setAiSuccess] = (0, import_react42.useState)(null);
5056
- const [aiTarget, setAiTarget] = (0, import_react42.useState)("auto");
5346
+ const brushCursorRef = (0, import_react44.useRef)(null);
5347
+ const [paintError, setPaintError] = (0, import_react44.useState)(null);
5348
+ const transformToolRef = (0, import_react44.useRef)(null);
5349
+ const transformControllerRef = (0, import_react44.useRef)(controller);
5350
+ if (!transformToolRef.current || transformControllerRef.current !== controller) {
5351
+ transformToolRef.current?.cancel();
5352
+ transformToolRef.current = new import_image_editor12.FreeTransformTool(controller, {
5353
+ schedule: (run) => typeof requestAnimationFrame !== "undefined" ? requestAnimationFrame(run) : run()
5354
+ });
5355
+ transformControllerRef.current = controller;
5356
+ }
5357
+ const transformTool = transformToolRef.current;
5358
+ const [, setTransformTick] = (0, import_react44.useState)(0);
5359
+ const [transformCursor, setTransformCursor] = (0, import_react44.useState)("default");
5360
+ const [inpaintPrompt, setInpaintPrompt] = (0, import_react44.useState)("");
5361
+ const [inpaintMaskMode, setInpaintMaskMode] = (0, import_react44.useState)("annotations");
5362
+ const [isRemovingBg, setIsRemovingBg] = (0, import_react44.useState)(false);
5363
+ const [isInpainting, setIsInpainting] = (0, import_react44.useState)(false);
5364
+ const [aiError, setAiError] = (0, import_react44.useState)(null);
5365
+ const [aiSuccess, setAiSuccess] = (0, import_react44.useState)(null);
5366
+ const [aiTarget, setAiTarget] = (0, import_react44.useState)("auto");
5057
5367
  const aiOptions = aiTarget === "auto" ? {} : { target: aiTarget };
5058
5368
  const effectiveOnInpaint = onInpaint || aiHooks?.onInpaint;
5059
5369
  const effectiveOnRemoveBg = onRemoveBackground || aiHooks?.onRemoveBackground;
@@ -5116,18 +5426,18 @@ var ImageEditor = ({
5116
5426
  setIsInpainting(false);
5117
5427
  }
5118
5428
  };
5119
- const [activeColor, setActiveColor] = (0, import_react42.useState)("#ef4444");
5120
- const [strokeWidth, setStrokeWidth] = (0, import_react42.useState)(3);
5121
- const [fontSize, setFontSize] = (0, import_react42.useState)(24);
5122
- const [textInput, setTextInput] = (0, import_react42.useState)("");
5123
- const draftAnnotationRef = (0, import_react42.useRef)(null);
5124
- const draftCropRef = (0, import_react42.useRef)(null);
5125
- const cropDragInfoRef = (0, import_react42.useRef)(null);
5126
- const annotationDragInfoRef = (0, import_react42.useRef)(null);
5127
- const isDrawingRef = (0, import_react42.useRef)(false);
5128
- const currentPathRef = (0, import_react42.useRef)([]);
5129
- const dragStartPointRef = (0, import_react42.useRef)(null);
5130
- const renderCanvas = (0, import_react42.useCallback)(
5429
+ const [activeColor, setActiveColor] = (0, import_react44.useState)("#ef4444");
5430
+ const [strokeWidth, setStrokeWidth] = (0, import_react44.useState)(3);
5431
+ const [fontSize, setFontSize] = (0, import_react44.useState)(24);
5432
+ const [textInput, setTextInput] = (0, import_react44.useState)("");
5433
+ const draftAnnotationRef = (0, import_react44.useRef)(null);
5434
+ const draftCropRef = (0, import_react44.useRef)(null);
5435
+ const cropDragInfoRef = (0, import_react44.useRef)(null);
5436
+ const annotationDragInfoRef = (0, import_react44.useRef)(null);
5437
+ const isDrawingRef = (0, import_react44.useRef)(false);
5438
+ const currentPathRef = (0, import_react44.useRef)([]);
5439
+ const dragStartPointRef = (0, import_react44.useRef)(null);
5440
+ const renderCanvas = (0, import_react44.useCallback)(
5131
5441
  async (draftAnnotation, draftCrop) => {
5132
5442
  const canvas = canvasRef.current;
5133
5443
  const container = containerRef.current;
@@ -5143,12 +5453,64 @@ var ImageEditor = ({
5143
5453
  draftAnnotation: draftAnnotation !== void 0 ? draftAnnotation : draftAnnotationRef.current,
5144
5454
  draftCrop: draftCrop !== void 0 ? draftCrop : draftCropRef.current,
5145
5455
  draftSelectionPath: selectionDraftRef.current,
5146
- brushCursor: activeTab === "paint" && brushCursorRef.current && controller.getState().activeTool !== "move" ? { ...brushCursorRef.current, diameter: controller.getState().paint.brush.size } : null
5456
+ brushCursor: activeTab === "paint" && brushCursorRef.current && controller.getState().activeTool !== "move" && !transformTool.active ? { ...brushCursorRef.current, diameter: controller.getState().paint.brush.size } : null,
5457
+ freeTransform: transformTool.overlay()
5147
5458
  });
5148
5459
  },
5149
- [controller, activeTab]
5460
+ [controller, activeTab, transformTool]
5150
5461
  );
5151
- (0, import_react42.useEffect)(() => {
5462
+ (0, import_react44.useEffect)(() => transformTool.subscribe(() => {
5463
+ setTransformTick((n) => n + 1);
5464
+ void renderCanvas();
5465
+ }), [transformTool, renderCanvas]);
5466
+ const transformPoint = (e) => {
5467
+ const canvas = canvasRef.current;
5468
+ const rect = canvas.getBoundingClientRect();
5469
+ const sx = rect.width > 0 ? canvas.width / rect.width : 1;
5470
+ const sy = rect.height > 0 ? canvas.height / rect.height : 1;
5471
+ const s = controller.getState();
5472
+ const cropping = activeTab === "crop";
5473
+ const projection = (0, import_image_editor12.getViewportProjection)(s, canvas.width, canvas.height, cropping);
5474
+ const pt = (0, import_image_editor12.canvasToImagePoint)({ x: (e.clientX - rect.left) * sx, y: (e.clientY - rect.top) * sy }, s, canvas.width, canvas.height, cropping);
5475
+ transformTool.setView(projection.scale);
5476
+ return { pt, tolerance: 6 / (projection.scale || 1) };
5477
+ };
5478
+ const handleTransformPointerDown = (e) => {
5479
+ if (!transformTool.active) return false;
5480
+ if (e.button !== 0) return true;
5481
+ e.preventDefault();
5482
+ try {
5483
+ e.currentTarget.setPointerCapture?.(e.pointerId);
5484
+ } catch {
5485
+ }
5486
+ const { pt, tolerance } = transformPoint(e);
5487
+ transformTool.down(pt, tolerance);
5488
+ return true;
5489
+ };
5490
+ const handleTransformPointerMove = (e) => {
5491
+ if (!transformTool.active) return false;
5492
+ const { pt, tolerance } = transformPoint(e);
5493
+ if (e.buttons & 1) transformTool.move(pt, e);
5494
+ else {
5495
+ const over = transformTool.hit(pt, tolerance);
5496
+ setTransformCursor(transformTool.cursor(over));
5497
+ }
5498
+ return true;
5499
+ };
5500
+ const handleTransformPointerUp = () => {
5501
+ if (!transformTool.active) return false;
5502
+ transformTool.up();
5503
+ return true;
5504
+ };
5505
+ (0, import_react44.useEffect)(() => {
5506
+ if (typeof window === "undefined") return;
5507
+ const onKey = (e) => {
5508
+ if (transformTool.key(e)) e.preventDefault();
5509
+ };
5510
+ window.addEventListener("keydown", onKey);
5511
+ return () => window.removeEventListener("keydown", onKey);
5512
+ }, [transformTool]);
5513
+ (0, import_react44.useEffect)(() => {
5152
5514
  const raf = typeof requestAnimationFrame !== "undefined" ? requestAnimationFrame : (cb) => setTimeout(cb, 0);
5153
5515
  const caf = typeof cancelAnimationFrame !== "undefined" ? cancelAnimationFrame : (id) => clearTimeout(id);
5154
5516
  const animId = raf(() => {
@@ -5156,12 +5518,12 @@ var ImageEditor = ({
5156
5518
  });
5157
5519
  return () => caf(animId);
5158
5520
  }, [renderCanvas, state]);
5159
- (0, import_react42.useEffect)(() => {
5521
+ (0, import_react44.useEffect)(() => {
5160
5522
  if (activeTab === "crop" && editor.state.activeTool !== "crop") {
5161
5523
  editor.setTool("crop");
5162
5524
  }
5163
5525
  }, [activeTab, editor]);
5164
- (0, import_react42.useEffect)(() => {
5526
+ (0, import_react44.useEffect)(() => {
5165
5527
  const container = containerRef.current;
5166
5528
  if (!container) return;
5167
5529
  if (typeof ResizeObserver !== "undefined") {
@@ -5197,11 +5559,12 @@ var ImageEditor = ({
5197
5559
  const sy = rect.height > 0 ? canvas.height / rect.height : 1;
5198
5560
  const s = controller.getState();
5199
5561
  return list.map((ev) => ({
5200
- ...(0, import_image_editor10.canvasToImagePoint)({ x: (ev.clientX - rect.left) * sx, y: (ev.clientY - rect.top) * sy }, s, canvas.width, canvas.height, false),
5201
- pressure: (0, import_image_editor10.pointerPressure)(ev)
5562
+ ...(0, import_image_editor12.canvasToImagePoint)({ x: (ev.clientX - rect.left) * sx, y: (ev.clientY - rect.top) * sy }, s, canvas.width, canvas.height, false),
5563
+ pressure: (0, import_image_editor12.pointerPressure)(ev)
5202
5564
  }));
5203
5565
  };
5204
5566
  const handlePaintPointerDown = (e) => {
5567
+ if (handleTransformPointerDown(e)) return;
5205
5568
  if (activeTab !== "paint" || e.button !== 0) return;
5206
5569
  e.preventDefault();
5207
5570
  try {
@@ -5212,6 +5575,7 @@ var ImageEditor = ({
5212
5575
  setPaintError(paintGestureRef.current.down(first));
5213
5576
  };
5214
5577
  const handlePaintPointerMove = (e) => {
5578
+ if (handleTransformPointerMove(e)) return;
5215
5579
  if (activeTab !== "paint") return;
5216
5580
  const samples = paintSamples(e);
5217
5581
  brushCursorRef.current = samples[samples.length - 1] ?? null;
@@ -5219,6 +5583,7 @@ var ImageEditor = ({
5219
5583
  else void renderCanvas();
5220
5584
  };
5221
5585
  const handlePaintPointerUp = () => {
5586
+ if (handleTransformPointerUp()) return;
5222
5587
  if (activeTab !== "paint") return;
5223
5588
  const g = paintGestureRef.current;
5224
5589
  if (g.active) {
@@ -5226,21 +5591,21 @@ var ImageEditor = ({
5226
5591
  if (g.error) setPaintError(g.error);
5227
5592
  }
5228
5593
  };
5229
- (0, import_react42.useEffect)(() => {
5594
+ (0, import_react44.useEffect)(() => {
5230
5595
  if (activeTab !== "paint" || typeof window === "undefined") return;
5231
5596
  const onKey = (e) => {
5232
- if ((0, import_image_editor10.handlePaintShortcut)(controller, e)) e.preventDefault();
5597
+ if ((0, import_image_editor12.handlePaintShortcut)(controller, e)) e.preventDefault();
5233
5598
  };
5234
5599
  window.addEventListener("keydown", onKey);
5235
5600
  return () => window.removeEventListener("keydown", onKey);
5236
5601
  }, [activeTab, controller]);
5237
5602
  const handleMouseDown = (e) => {
5238
5603
  const canvas = canvasRef.current;
5239
- if (!canvas) return;
5604
+ if (!canvas || transformTool.active) return;
5240
5605
  const canvasPt = getCanvasEventPoint(e);
5241
5606
  if (activeTab === "select") {
5242
5607
  const gesture = selectionGestureRef.current;
5243
- const imgPt = (0, import_image_editor10.canvasToImagePoint)(canvasPt, state, canvas.width, canvas.height, false);
5608
+ const imgPt = (0, import_image_editor12.canvasToImagePoint)(canvasPt, state, canvas.width, canvas.height, false);
5244
5609
  void gesture.begin(imgPt, e).then(() => setSelectionError(gesture.error));
5245
5610
  return;
5246
5611
  }
@@ -5251,8 +5616,8 @@ var ImageEditor = ({
5251
5616
  width: state.imageDimensions.width,
5252
5617
  height: state.imageDimensions.height
5253
5618
  };
5254
- const handle = (0, import_image_editor10.hitTestCrop)(canvasPt, state, canvas.width, canvas.height, 14, activeCrop);
5255
- const startImgPt = (0, import_image_editor10.canvasToImagePoint)(canvasPt, state, canvas.width, canvas.height, true);
5619
+ const handle = (0, import_image_editor12.hitTestCrop)(canvasPt, state, canvas.width, canvas.height, 14, activeCrop);
5620
+ const startImgPt = (0, import_image_editor12.canvasToImagePoint)(canvasPt, state, canvas.width, canvas.height, true);
5256
5621
  if (handle) {
5257
5622
  cropDragInfoRef.current = {
5258
5623
  handle,
@@ -5271,12 +5636,12 @@ var ImageEditor = ({
5271
5636
  return;
5272
5637
  }
5273
5638
  if (activeTab === "annotate") {
5274
- const imgPt = (0, import_image_editor10.canvasToImagePoint)(canvasPt, state, canvas.width, canvas.height, false);
5639
+ const imgPt = (0, import_image_editor12.canvasToImagePoint)(canvasPt, state, canvas.width, canvas.height, false);
5275
5640
  dragStartPointRef.current = imgPt;
5276
5641
  if (state.activeTool === "select") {
5277
5642
  for (let i = state.annotations.length - 1; i >= 0; i--) {
5278
5643
  const ann = state.annotations[i];
5279
- if ((0, import_image_editor10.hitTestAnnotation)(imgPt, ann)) {
5644
+ if ((0, import_image_editor12.hitTestAnnotation)(imgPt, ann)) {
5280
5645
  editor.selectAnnotation(ann.id);
5281
5646
  annotationDragInfoRef.current = {
5282
5647
  annId: ann.id,
@@ -5308,11 +5673,11 @@ var ImageEditor = ({
5308
5673
  };
5309
5674
  const handleMouseMove = (e) => {
5310
5675
  const canvas = canvasRef.current;
5311
- if (!canvas) return;
5676
+ if (!canvas || transformTool.active) return;
5312
5677
  const canvasPt = getCanvasEventPoint(e);
5313
5678
  if (activeTab === "select") {
5314
5679
  const gesture = selectionGestureRef.current;
5315
- if (gesture.active && gesture.move((0, import_image_editor10.canvasToImagePoint)(canvasPt, state, canvas.width, canvas.height, false))) {
5680
+ if (gesture.active && gesture.move((0, import_image_editor12.canvasToImagePoint)(canvasPt, state, canvas.width, canvas.height, false))) {
5316
5681
  selectionDraftRef.current = gesture.draftPath;
5317
5682
  renderCanvas();
5318
5683
  }
@@ -5321,7 +5686,7 @@ var ImageEditor = ({
5321
5686
  if (activeTab === "crop") {
5322
5687
  const drag = cropDragInfoRef.current;
5323
5688
  if (drag) {
5324
- const currentImgPt = (0, import_image_editor10.canvasToImagePoint)(canvasPt, state, canvas.width, canvas.height, true);
5689
+ const currentImgPt = (0, import_image_editor12.canvasToImagePoint)(canvasPt, state, canvas.width, canvas.height, true);
5325
5690
  let newCrop;
5326
5691
  if (drag.handle === "new") {
5327
5692
  const minX = Math.max(0, Math.min(drag.startImgPt.x, currentImgPt.x));
@@ -5335,7 +5700,7 @@ var ImageEditor = ({
5335
5700
  height: Math.round(Math.max(20, maxY - minY))
5336
5701
  };
5337
5702
  } else {
5338
- newCrop = (0, import_image_editor10.calculateCropDrag)(
5703
+ newCrop = (0, import_image_editor12.calculateCropDrag)(
5339
5704
  drag.startCrop,
5340
5705
  drag.handle,
5341
5706
  drag.startImgPt,
@@ -5355,15 +5720,15 @@ var ImageEditor = ({
5355
5720
  width: state.imageDimensions.width,
5356
5721
  height: state.imageDimensions.height
5357
5722
  };
5358
- const handle = (0, import_image_editor10.hitTestCrop)(canvasPt, state, canvas.width, canvas.height, 14, activeCrop);
5359
- canvas.style.cursor = (0, import_image_editor10.getCropCursor)(handle, state.transform);
5723
+ const handle = (0, import_image_editor12.hitTestCrop)(canvasPt, state, canvas.width, canvas.height, 14, activeCrop);
5724
+ canvas.style.cursor = (0, import_image_editor12.getCropCursor)(handle, state.transform);
5360
5725
  }
5361
5726
  return;
5362
5727
  }
5363
5728
  if (activeTab === "annotate") {
5364
5729
  if (annotationDragInfoRef.current) {
5365
5730
  const drag = annotationDragInfoRef.current;
5366
- const currentImgPt2 = (0, import_image_editor10.canvasToImagePoint)(canvasPt, state, canvas.width, canvas.height, false);
5731
+ const currentImgPt2 = (0, import_image_editor12.canvasToImagePoint)(canvasPt, state, canvas.width, canvas.height, false);
5367
5732
  const dx = currentImgPt2.x - drag.startImgPt.x;
5368
5733
  const dy = currentImgPt2.y - drag.startImgPt.y;
5369
5734
  editor.updateAnnotation(drag.annId, {
@@ -5375,10 +5740,10 @@ var ImageEditor = ({
5375
5740
  const startPt = dragStartPointRef.current;
5376
5741
  if (!startPt) {
5377
5742
  if (state.activeTool === "select") {
5378
- const imgPt = (0, import_image_editor10.canvasToImagePoint)(canvasPt, state, canvas.width, canvas.height, false);
5743
+ const imgPt = (0, import_image_editor12.canvasToImagePoint)(canvasPt, state, canvas.width, canvas.height, false);
5379
5744
  let hovered = false;
5380
5745
  for (let i = state.annotations.length - 1; i >= 0; i--) {
5381
- if ((0, import_image_editor10.hitTestAnnotation)(imgPt, state.annotations[i])) {
5746
+ if ((0, import_image_editor12.hitTestAnnotation)(imgPt, state.annotations[i])) {
5382
5747
  hovered = true;
5383
5748
  break;
5384
5749
  }
@@ -5389,7 +5754,7 @@ var ImageEditor = ({
5389
5754
  }
5390
5755
  return;
5391
5756
  }
5392
- const currentImgPt = (0, import_image_editor10.canvasToImagePoint)(canvasPt, state, canvas.width, canvas.height, false);
5757
+ const currentImgPt = (0, import_image_editor12.canvasToImagePoint)(canvasPt, state, canvas.width, canvas.height, false);
5393
5758
  if (state.activeTool === "pen" && isDrawingRef.current) {
5394
5759
  currentPathRef.current.push(currentImgPt);
5395
5760
  const draft = {
@@ -5464,12 +5829,12 @@ var ImageEditor = ({
5464
5829
  };
5465
5830
  const handleMouseUp = (e) => {
5466
5831
  const canvas = canvasRef.current;
5467
- if (!canvas) return;
5832
+ if (!canvas || transformTool.active) return;
5468
5833
  const canvasPt = getCanvasEventPoint(e);
5469
5834
  if (activeTab === "select") {
5470
5835
  const gesture = selectionGestureRef.current;
5471
5836
  if (gesture.active) {
5472
- gesture.end((0, import_image_editor10.canvasToImagePoint)(canvasPt, state, canvas.width, canvas.height, false), e);
5837
+ gesture.end((0, import_image_editor12.canvasToImagePoint)(canvasPt, state, canvas.width, canvas.height, false), e);
5473
5838
  selectionDraftRef.current = null;
5474
5839
  renderCanvas();
5475
5840
  }
@@ -5493,7 +5858,7 @@ var ImageEditor = ({
5493
5858
  }
5494
5859
  const startPt = dragStartPointRef.current;
5495
5860
  if (startPt) {
5496
- const currentImgPt = (0, import_image_editor10.canvasToImagePoint)(canvasPt, state, canvas.width, canvas.height, false);
5861
+ const currentImgPt = (0, import_image_editor12.canvasToImagePoint)(canvasPt, state, canvas.width, canvas.height, false);
5497
5862
  if (state.activeTool === "pen" && isDrawingRef.current) {
5498
5863
  if (currentPathRef.current.length > 1) {
5499
5864
  editor.addAnnotation({
@@ -5589,15 +5954,15 @@ var ImageEditor = ({
5589
5954
  link.click();
5590
5955
  }
5591
5956
  };
5592
- return /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(
5957
+ return /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(
5593
5958
  "div",
5594
5959
  {
5595
5960
  className: `flex flex-col bg-slate-900 border border-slate-800 rounded-xl overflow-hidden shadow-2xl text-slate-100 ${className}`,
5596
5961
  style,
5597
5962
  children: [
5598
- /* @__PURE__ */ (0, import_jsx_runtime25.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: [
5599
- /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: "flex items-center gap-1 bg-slate-900 p-1 rounded-lg border border-slate-800 text-xs", children: [
5600
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
5963
+ /* @__PURE__ */ (0, import_jsx_runtime27.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: [
5964
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "flex items-center gap-1 bg-slate-900 p-1 rounded-lg border border-slate-800 text-xs", children: [
5965
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
5601
5966
  "button",
5602
5967
  {
5603
5968
  type: "button",
@@ -5609,7 +5974,7 @@ var ImageEditor = ({
5609
5974
  children: "Transform & Crop"
5610
5975
  }
5611
5976
  ),
5612
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
5977
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
5613
5978
  "button",
5614
5979
  {
5615
5980
  type: "button",
@@ -5621,7 +5986,7 @@ var ImageEditor = ({
5621
5986
  children: "Adjustments"
5622
5987
  }
5623
5988
  ),
5624
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
5989
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
5625
5990
  "button",
5626
5991
  {
5627
5992
  type: "button",
@@ -5633,7 +5998,7 @@ var ImageEditor = ({
5633
5998
  children: "Layers"
5634
5999
  }
5635
6000
  ),
5636
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
6001
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
5637
6002
  "button",
5638
6003
  {
5639
6004
  type: "button",
@@ -5645,7 +6010,7 @@ var ImageEditor = ({
5645
6010
  children: "Paint"
5646
6011
  }
5647
6012
  ),
5648
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
6013
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
5649
6014
  "button",
5650
6015
  {
5651
6016
  type: "button",
@@ -5657,7 +6022,7 @@ var ImageEditor = ({
5657
6022
  children: "Select & Mask"
5658
6023
  }
5659
6024
  ),
5660
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
6025
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
5661
6026
  "button",
5662
6027
  {
5663
6028
  type: "button",
@@ -5669,7 +6034,7 @@ var ImageEditor = ({
5669
6034
  children: "Annotations"
5670
6035
  }
5671
6036
  ),
5672
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
6037
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
5673
6038
  "button",
5674
6039
  {
5675
6040
  type: "button",
@@ -5681,7 +6046,7 @@ var ImageEditor = ({
5681
6046
  children: "Depth Mask"
5682
6047
  }
5683
6048
  ),
5684
- /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(
6049
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(
5685
6050
  "button",
5686
6051
  {
5687
6052
  type: "button",
@@ -5691,14 +6056,14 @@ var ImageEditor = ({
5691
6056
  },
5692
6057
  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"}`,
5693
6058
  children: [
5694
- /* @__PURE__ */ (0, import_jsx_runtime25.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_runtime25.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" }) }),
6059
+ /* @__PURE__ */ (0, import_jsx_runtime27.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_runtime27.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" }) }),
5695
6060
  "AI Tools"
5696
6061
  ]
5697
6062
  }
5698
6063
  )
5699
6064
  ] }),
5700
- /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: "flex items-center gap-2 text-xs", children: [
5701
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
6065
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "flex items-center gap-2 text-xs", children: [
6066
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
5702
6067
  "button",
5703
6068
  {
5704
6069
  type: "button",
@@ -5709,7 +6074,7 @@ var ImageEditor = ({
5709
6074
  children: "\u21A9"
5710
6075
  }
5711
6076
  ),
5712
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
6077
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
5713
6078
  "button",
5714
6079
  {
5715
6080
  type: "button",
@@ -5720,7 +6085,7 @@ var ImageEditor = ({
5720
6085
  children: "\u21AA"
5721
6086
  }
5722
6087
  ),
5723
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
6088
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
5724
6089
  "button",
5725
6090
  {
5726
6091
  type: "button",
@@ -5729,8 +6094,8 @@ var ImageEditor = ({
5729
6094
  children: "Reset"
5730
6095
  }
5731
6096
  ),
5732
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { className: "h-4 w-px bg-slate-800 mx-1" }),
5733
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
6097
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("div", { className: "h-4 w-px bg-slate-800 mx-1" }),
6098
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
5734
6099
  "button",
5735
6100
  {
5736
6101
  type: "button",
@@ -5740,11 +6105,11 @@ var ImageEditor = ({
5740
6105
  children: "-"
5741
6106
  }
5742
6107
  ),
5743
- /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("span", { className: "font-mono text-slate-400 w-10 text-center", children: [
6108
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("span", { className: "font-mono text-slate-400 w-10 text-center", children: [
5744
6109
  Math.round(state.zoom * 100),
5745
6110
  "%"
5746
6111
  ] }),
5747
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
6112
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
5748
6113
  "button",
5749
6114
  {
5750
6115
  type: "button",
@@ -5754,8 +6119,8 @@ var ImageEditor = ({
5754
6119
  children: "+"
5755
6120
  }
5756
6121
  ),
5757
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { className: "h-4 w-px bg-slate-800 mx-1" }),
5758
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
6122
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("div", { className: "h-4 w-px bg-slate-800 mx-1" }),
6123
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
5759
6124
  "button",
5760
6125
  {
5761
6126
  type: "button",
@@ -5766,7 +6131,7 @@ var ImageEditor = ({
5766
6131
  children: "Hold Compare"
5767
6132
  }
5768
6133
  ),
5769
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
6134
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
5770
6135
  "button",
5771
6136
  {
5772
6137
  type: "button",
@@ -5777,13 +6142,15 @@ var ImageEditor = ({
5777
6142
  )
5778
6143
  ] })
5779
6144
  ] }),
5780
- /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: "flex flex-col md:flex-row flex-1 min-h-[520px] md:h-[560px]", children: [
5781
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
6145
+ transformTool.active && /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(FreeTransformOptionsBar, { tool: transformTool, className: "border-b border-slate-800 bg-slate-900/95 px-4 py-2" }),
6146
+ !transformTool.active && transformTool.error && /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("div", { role: "alert", className: "border-b border-rose-900 bg-rose-950/80 px-4 py-1.5 text-xs text-rose-200", children: transformTool.error }),
6147
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "flex flex-col md:flex-row flex-1 min-h-[520px] md:h-[560px]", children: [
6148
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
5782
6149
  "div",
5783
6150
  {
5784
6151
  ref: containerRef,
5785
6152
  className: "relative flex-1 h-[360px] md:h-full min-h-[320px] bg-slate-950 overflow-hidden select-none",
5786
- children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
6153
+ children: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
5787
6154
  "canvas",
5788
6155
  {
5789
6156
  ref: canvasRef,
@@ -5796,16 +6163,17 @@ var ImageEditor = ({
5796
6163
  onPointerUp: handlePaintPointerUp,
5797
6164
  onPointerCancel: () => paintGestureRef.current?.cancel(),
5798
6165
  "data-testid": "image-editor-canvas",
5799
- className: `absolute inset-0 w-full h-full block ${activeTab === "paint" ? "touch-none cursor-crosshair" : ""}`
6166
+ style: transformTool.active ? { cursor: transformCursor } : void 0,
6167
+ className: `absolute inset-0 w-full h-full block ${transformTool.active ? "touch-none" : activeTab === "paint" ? "touch-none cursor-crosshair" : ""}`
5800
6168
  }
5801
6169
  )
5802
6170
  }
5803
6171
  ),
5804
- /* @__PURE__ */ (0, import_jsx_runtime25.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: [
5805
- activeTab === "crop" && /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: "flex flex-col gap-4 text-xs", children: [
5806
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Aspect Ratio Presets" }),
5807
- /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: "grid grid-cols-3 gap-2", children: [
5808
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
6172
+ /* @__PURE__ */ (0, import_jsx_runtime27.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: [
6173
+ activeTab === "crop" && /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "flex flex-col gap-4 text-xs", children: [
6174
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Aspect Ratio Presets" }),
6175
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "grid grid-cols-3 gap-2", children: [
6176
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
5809
6177
  "button",
5810
6178
  {
5811
6179
  type: "button",
@@ -5817,7 +6185,7 @@ var ImageEditor = ({
5817
6185
  children: "Freeform"
5818
6186
  }
5819
6187
  ),
5820
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
6188
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
5821
6189
  "button",
5822
6190
  {
5823
6191
  type: "button",
@@ -5829,7 +6197,7 @@ var ImageEditor = ({
5829
6197
  children: "1:1 Square"
5830
6198
  }
5831
6199
  ),
5832
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
6200
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
5833
6201
  "button",
5834
6202
  {
5835
6203
  type: "button",
@@ -5841,7 +6209,7 @@ var ImageEditor = ({
5841
6209
  children: "4:3 Standard"
5842
6210
  }
5843
6211
  ),
5844
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
6212
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
5845
6213
  "button",
5846
6214
  {
5847
6215
  type: "button",
@@ -5853,7 +6221,7 @@ var ImageEditor = ({
5853
6221
  children: "16:9 Wide"
5854
6222
  }
5855
6223
  ),
5856
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
6224
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
5857
6225
  "button",
5858
6226
  {
5859
6227
  type: "button",
@@ -5865,7 +6233,7 @@ var ImageEditor = ({
5865
6233
  children: "3:2 Photo"
5866
6234
  }
5867
6235
  ),
5868
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
6236
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
5869
6237
  "button",
5870
6238
  {
5871
6239
  type: "button",
@@ -5878,10 +6246,10 @@ var ImageEditor = ({
5878
6246
  }
5879
6247
  )
5880
6248
  ] }),
5881
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { className: "h-px bg-slate-800 my-1" }),
5882
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Orientation & Flips" }),
5883
- /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: "grid grid-cols-2 gap-2", children: [
5884
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
6249
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("div", { className: "h-px bg-slate-800 my-1" }),
6250
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Orientation & Flips" }),
6251
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "grid grid-cols-2 gap-2", children: [
6252
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
5885
6253
  "button",
5886
6254
  {
5887
6255
  type: "button",
@@ -5890,7 +6258,7 @@ var ImageEditor = ({
5890
6258
  children: "Rotate -90\xB0"
5891
6259
  }
5892
6260
  ),
5893
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
6261
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
5894
6262
  "button",
5895
6263
  {
5896
6264
  type: "button",
@@ -5899,7 +6267,7 @@ var ImageEditor = ({
5899
6267
  children: "Rotate +90\xB0"
5900
6268
  }
5901
6269
  ),
5902
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
6270
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
5903
6271
  "button",
5904
6272
  {
5905
6273
  type: "button",
@@ -5908,7 +6276,7 @@ var ImageEditor = ({
5908
6276
  children: "Flip Horizontal"
5909
6277
  }
5910
6278
  ),
5911
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
6279
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
5912
6280
  "button",
5913
6281
  {
5914
6282
  type: "button",
@@ -5919,10 +6287,10 @@ var ImageEditor = ({
5919
6287
  )
5920
6288
  ] })
5921
6289
  ] }),
5922
- activeTab === "adjust" && /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: "flex flex-col gap-3 text-xs", children: [
5923
- /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: "flex items-center justify-between", children: [
5924
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Color Adjustments" }),
5925
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
6290
+ activeTab === "adjust" && /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "flex flex-col gap-3 text-xs", children: [
6291
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "flex items-center justify-between", children: [
6292
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Color Adjustments" }),
6293
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
5926
6294
  "button",
5927
6295
  {
5928
6296
  type: "button",
@@ -5932,25 +6300,25 @@ var ImageEditor = ({
5932
6300
  }
5933
6301
  )
5934
6302
  ] }),
5935
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(Histogram, { histogram: editor.histogram }),
5936
- import_image_editor10.IMAGE_ADJUSTMENT_CONTROLS.map((spec) => /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(import_react42.default.Fragment, { children: [
5937
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
6303
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(Histogram, { histogram: editor.histogram }),
6304
+ import_image_editor12.IMAGE_ADJUSTMENT_CONTROLS.map((spec) => /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(import_react44.default.Fragment, { children: [
6305
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
5938
6306
  ControlSlider,
5939
6307
  {
5940
6308
  spec,
5941
- value: (0, import_image_editor10.readAdjustmentControls)(state.adjustments)[spec.id],
6309
+ value: (0, import_image_editor12.readAdjustmentControls)(state.adjustments)[spec.id],
5942
6310
  disabled: spec.requiresProcess !== void 0 && legacyProcess,
5943
6311
  onChange: (v) => editor.setAdjustments({ [spec.id]: v })
5944
6312
  }
5945
6313
  ),
5946
- spec.id === "tint" && legacyProcess && /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(
6314
+ spec.id === "tint" && legacyProcess && /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(
5947
6315
  "div",
5948
6316
  {
5949
6317
  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",
5950
6318
  "data-testid": "image-editor-process-notice",
5951
6319
  children: [
5952
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { children: "Legacy rendering (process 1). Tint needs the current process." }),
5953
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
6320
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { children: "Legacy rendering (process 1). Tint needs the current process." }),
6321
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
5954
6322
  "button",
5955
6323
  {
5956
6324
  type: "button",
@@ -5963,7 +6331,7 @@ var ImageEditor = ({
5963
6331
  }
5964
6332
  )
5965
6333
  ] }, spec.id)),
5966
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { className: "border-t border-slate-700/60 pt-3", children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
6334
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("div", { className: "border-t border-slate-700/60 pt-3", children: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
5967
6335
  AdjustmentLayersPanel,
5968
6336
  {
5969
6337
  controller,
@@ -5972,19 +6340,41 @@ var ImageEditor = ({
5972
6340
  }
5973
6341
  ) })
5974
6342
  ] }),
5975
- activeTab === "layers" && /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: "flex flex-col gap-3 text-xs", children: [
5976
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Layers" }),
5977
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(LayersPanel, { controller })
6343
+ activeTab === "layers" && /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "flex flex-col gap-3 text-xs", children: [
6344
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Layers" }),
6345
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
6346
+ "button",
6347
+ {
6348
+ type: "button",
6349
+ title: "Edit \u203A Free Transform (Ctrl/Cmd+T): the selected pixels, or the whole active layer",
6350
+ className: "px-2 py-1 rounded text-[11px] bg-slate-800 text-slate-300 hover:text-white self-start disabled:opacity-40",
6351
+ disabled: transformTool.active,
6352
+ onClick: () => transformTool.begin(),
6353
+ children: "Free Transform"
6354
+ }
6355
+ ),
6356
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(LayersPanel, { controller })
5978
6357
  ] }),
5979
- activeTab === "paint" && /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: "flex flex-col gap-3 text-xs", children: [
5980
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Paint" }),
5981
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(BrushOptionsBar, { controller }),
5982
- paintError && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("p", { role: "alert", className: "text-rose-400", "data-testid": "paint-error", children: paintError }),
5983
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { className: "border-t border-slate-700/60 pt-3", children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(LayersPanel, { controller }) })
6358
+ activeTab === "paint" && /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "flex flex-col gap-3 text-xs", children: [
6359
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Paint" }),
6360
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(BrushOptionsBar, { controller }),
6361
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
6362
+ "button",
6363
+ {
6364
+ type: "button",
6365
+ title: "Edit \u203A Free Transform (Ctrl/Cmd+T): the selected pixels, or the whole active layer",
6366
+ className: "px-2 py-1 rounded text-[11px] bg-slate-800 text-slate-300 hover:text-white self-start",
6367
+ disabled: transformTool.active,
6368
+ onClick: () => transformTool.begin(),
6369
+ children: "Free Transform"
6370
+ }
6371
+ ),
6372
+ paintError && /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("p", { role: "alert", className: "text-rose-400", "data-testid": "paint-error", children: paintError }),
6373
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("div", { className: "border-t border-slate-700/60 pt-3", children: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(LayersPanel, { controller }) })
5984
6374
  ] }),
5985
- activeTab === "annotate" && /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: "flex flex-col gap-3 text-xs", children: [
5986
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Draw & Annotate" }),
5987
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { className: "grid grid-cols-3 gap-1.5", children: ["pen", "rect", "circle", "arrow", "line"].map((t) => /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
6375
+ activeTab === "annotate" && /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "flex flex-col gap-3 text-xs", children: [
6376
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Draw & Annotate" }),
6377
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("div", { className: "grid grid-cols-3 gap-1.5", children: ["pen", "rect", "circle", "arrow", "line"].map((t) => /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
5988
6378
  "button",
5989
6379
  {
5990
6380
  type: "button",
@@ -5994,9 +6384,9 @@ var ImageEditor = ({
5994
6384
  },
5995
6385
  t
5996
6386
  )) }),
5997
- /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: "flex items-center gap-2 mt-2", children: [
5998
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("label", { className: "text-slate-400", children: "Color:" }),
5999
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
6387
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "flex items-center gap-2 mt-2", children: [
6388
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("label", { className: "text-slate-400", children: "Color:" }),
6389
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
6000
6390
  "input",
6001
6391
  {
6002
6392
  type: "color",
@@ -6005,15 +6395,15 @@ var ImageEditor = ({
6005
6395
  className: "w-8 h-8 rounded border border-slate-700 cursor-pointer bg-transparent"
6006
6396
  }
6007
6397
  ),
6008
- /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: "flex-1 ml-2", children: [
6009
- /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: "flex justify-between text-slate-400 mb-0.5", children: [
6010
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { children: "Width:" }),
6011
- /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("span", { className: "font-mono", children: [
6398
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "flex-1 ml-2", children: [
6399
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "flex justify-between text-slate-400 mb-0.5", children: [
6400
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { children: "Width:" }),
6401
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("span", { className: "font-mono", children: [
6012
6402
  strokeWidth,
6013
6403
  "px"
6014
6404
  ] })
6015
6405
  ] }),
6016
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
6406
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
6017
6407
  "input",
6018
6408
  {
6019
6409
  type: "range",
@@ -6026,10 +6416,10 @@ var ImageEditor = ({
6026
6416
  )
6027
6417
  ] })
6028
6418
  ] }),
6029
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { className: "h-px bg-slate-800 my-1" }),
6030
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Add Text" }),
6031
- /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: "flex gap-2", children: [
6032
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
6419
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("div", { className: "h-px bg-slate-800 my-1" }),
6420
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Add Text" }),
6421
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "flex gap-2", children: [
6422
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
6033
6423
  "input",
6034
6424
  {
6035
6425
  type: "text",
@@ -6040,7 +6430,7 @@ var ImageEditor = ({
6040
6430
  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"
6041
6431
  }
6042
6432
  ),
6043
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
6433
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
6044
6434
  "button",
6045
6435
  {
6046
6436
  type: "button",
@@ -6050,14 +6440,14 @@ var ImageEditor = ({
6050
6440
  }
6051
6441
  )
6052
6442
  ] }),
6053
- /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: "flex items-center justify-between text-slate-400", children: [
6054
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { children: "Font Size:" }),
6055
- /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("span", { className: "font-mono", children: [
6443
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "flex items-center justify-between text-slate-400", children: [
6444
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { children: "Font Size:" }),
6445
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("span", { className: "font-mono", children: [
6056
6446
  fontSize,
6057
6447
  "px"
6058
6448
  ] })
6059
6449
  ] }),
6060
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
6450
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
6061
6451
  "input",
6062
6452
  {
6063
6453
  type: "range",
@@ -6068,14 +6458,14 @@ var ImageEditor = ({
6068
6458
  className: "w-full accent-indigo-500"
6069
6459
  }
6070
6460
  ),
6071
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { className: "h-px bg-slate-800 my-1" }),
6072
- /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: "flex items-center justify-between", children: [
6073
- /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("span", { className: "text-slate-400", children: [
6461
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("div", { className: "h-px bg-slate-800 my-1" }),
6462
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "flex items-center justify-between", children: [
6463
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("span", { className: "text-slate-400", children: [
6074
6464
  "Layers (",
6075
6465
  state.annotations.length,
6076
6466
  ")"
6077
6467
  ] }),
6078
- state.selectedAnnotationId && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
6468
+ state.selectedAnnotationId && /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
6079
6469
  "button",
6080
6470
  {
6081
6471
  type: "button",
@@ -6084,7 +6474,7 @@ var ImageEditor = ({
6084
6474
  children: "Delete Selected"
6085
6475
  }
6086
6476
  ),
6087
- state.annotations.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
6477
+ state.annotations.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
6088
6478
  "button",
6089
6479
  {
6090
6480
  type: "button",
@@ -6095,10 +6485,10 @@ var ImageEditor = ({
6095
6485
  )
6096
6486
  ] })
6097
6487
  ] }),
6098
- activeTab === "depth" && /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: "flex flex-col gap-3 text-xs", children: [
6099
- /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: "flex items-center justify-between", children: [
6100
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Depth Masking" }),
6101
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
6488
+ activeTab === "depth" && /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "flex flex-col gap-3 text-xs", children: [
6489
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "flex items-center justify-between", children: [
6490
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Depth Masking" }),
6491
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
6102
6492
  "button",
6103
6493
  {
6104
6494
  type: "button",
@@ -6108,8 +6498,8 @@ var ImageEditor = ({
6108
6498
  }
6109
6499
  )
6110
6500
  ] }),
6111
- /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("label", { className: "flex items-center gap-2 cursor-pointer text-slate-300", children: [
6112
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
6501
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("label", { className: "flex items-center gap-2 cursor-pointer text-slate-300", children: [
6502
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
6113
6503
  "input",
6114
6504
  {
6115
6505
  type: "checkbox",
@@ -6118,12 +6508,12 @@ var ImageEditor = ({
6118
6508
  className: "rounded accent-indigo-500"
6119
6509
  }
6120
6510
  ),
6121
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { children: "Enable Depth / Alpha Mask" })
6511
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { children: "Enable Depth / Alpha Mask" })
6122
6512
  ] }),
6123
- state.depthMask.enabled && /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(import_jsx_runtime25.Fragment, { children: [
6124
- /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { children: [
6125
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("label", { className: "text-slate-400 block mb-1", children: "Mask Image URL:" }),
6126
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
6513
+ state.depthMask.enabled && /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(import_jsx_runtime27.Fragment, { children: [
6514
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { children: [
6515
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("label", { className: "text-slate-400 block mb-1", children: "Mask Image URL:" }),
6516
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
6127
6517
  "input",
6128
6518
  {
6129
6519
  type: "text",
@@ -6134,8 +6524,8 @@ var ImageEditor = ({
6134
6524
  }
6135
6525
  )
6136
6526
  ] }),
6137
- /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: "flex gap-2", children: [
6138
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
6527
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "flex gap-2", children: [
6528
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
6139
6529
  "button",
6140
6530
  {
6141
6531
  type: "button",
@@ -6144,7 +6534,7 @@ var ImageEditor = ({
6144
6534
  children: "Threshold Mode"
6145
6535
  }
6146
6536
  ),
6147
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
6537
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
6148
6538
  "button",
6149
6539
  {
6150
6540
  type: "button",
@@ -6154,12 +6544,12 @@ var ImageEditor = ({
6154
6544
  }
6155
6545
  )
6156
6546
  ] }),
6157
- /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { children: [
6158
- /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: "flex justify-between text-slate-400 mb-1", children: [
6159
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { children: "Depth Start (Lower):" }),
6160
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { className: "font-mono", children: state.depthMask.depthRange[0] })
6547
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { children: [
6548
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "flex justify-between text-slate-400 mb-1", children: [
6549
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { children: "Depth Start (Lower):" }),
6550
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { className: "font-mono", children: state.depthMask.depthRange[0] })
6161
6551
  ] }),
6162
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
6552
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
6163
6553
  "input",
6164
6554
  {
6165
6555
  type: "range",
@@ -6173,12 +6563,12 @@ var ImageEditor = ({
6173
6563
  }
6174
6564
  )
6175
6565
  ] }),
6176
- /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { children: [
6177
- /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: "flex justify-between text-slate-400 mb-1", children: [
6178
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { children: "Depth Stop (Upper):" }),
6179
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { className: "font-mono", children: state.depthMask.depthRange[1] })
6566
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { children: [
6567
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "flex justify-between text-slate-400 mb-1", children: [
6568
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { children: "Depth Stop (Upper):" }),
6569
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { className: "font-mono", children: state.depthMask.depthRange[1] })
6180
6570
  ] }),
6181
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
6571
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
6182
6572
  "input",
6183
6573
  {
6184
6574
  type: "range",
@@ -6192,12 +6582,12 @@ var ImageEditor = ({
6192
6582
  }
6193
6583
  )
6194
6584
  ] }),
6195
- /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { children: [
6196
- /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: "flex justify-between text-slate-400 mb-1", children: [
6197
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { children: "Softness:" }),
6198
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { className: "font-mono", children: state.depthMask.softness })
6585
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { children: [
6586
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "flex justify-between text-slate-400 mb-1", children: [
6587
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { children: "Softness:" }),
6588
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { className: "font-mono", children: state.depthMask.softness })
6199
6589
  ] }),
6200
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
6590
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
6201
6591
  "input",
6202
6592
  {
6203
6593
  type: "range",
@@ -6209,8 +6599,8 @@ var ImageEditor = ({
6209
6599
  }
6210
6600
  )
6211
6601
  ] }),
6212
- /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("label", { className: "flex items-center gap-2 cursor-pointer text-slate-300 mt-1", children: [
6213
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
6602
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("label", { className: "flex items-center gap-2 cursor-pointer text-slate-300 mt-1", children: [
6603
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
6214
6604
  "input",
6215
6605
  {
6216
6606
  type: "checkbox",
@@ -6219,12 +6609,12 @@ var ImageEditor = ({
6219
6609
  className: "rounded accent-indigo-500"
6220
6610
  }
6221
6611
  ),
6222
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { children: "Invert Mask" })
6612
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { children: "Invert Mask" })
6223
6613
  ] })
6224
6614
  ] })
6225
6615
  ] }),
6226
- activeTab === "select" && /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: "flex flex-col gap-4 text-xs", children: [
6227
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
6616
+ activeTab === "select" && /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "flex flex-col gap-4 text-xs", children: [
6617
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
6228
6618
  SelectionOptionsBar,
6229
6619
  {
6230
6620
  controller,
@@ -6234,13 +6624,37 @@ var ImageEditor = ({
6234
6624
  quickMask: Boolean(state.quickMask)
6235
6625
  }
6236
6626
  ),
6237
- selectionError && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("p", { role: "alert", className: "text-rose-400", children: selectionError }),
6238
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(RefineEdgePanel, { controller, selection: state.selection ?? null, layers: state.adjustments.layers })
6627
+ selectionError && /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("p", { role: "alert", className: "text-rose-400", children: selectionError }),
6628
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "flex gap-1", children: [
6629
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
6630
+ "button",
6631
+ {
6632
+ type: "button",
6633
+ title: "Select \u203A Transform Selection: the outline only",
6634
+ className: "px-2 py-1 rounded text-[11px] bg-slate-800 text-slate-300 hover:text-white self-start disabled:opacity-40",
6635
+ disabled: transformTool.active,
6636
+ onClick: () => transformTool.begin({ content: "selection-outline" }),
6637
+ children: "Transform Selection"
6638
+ }
6639
+ ),
6640
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
6641
+ "button",
6642
+ {
6643
+ type: "button",
6644
+ title: "Edit \u203A Free Transform: the selected pixels of the active layer",
6645
+ className: "px-2 py-1 rounded text-[11px] bg-slate-800 text-slate-300 hover:text-white self-start disabled:opacity-40",
6646
+ disabled: transformTool.active,
6647
+ onClick: () => transformTool.begin({ content: "selection" }),
6648
+ children: "Free Transform Pixels"
6649
+ }
6650
+ )
6651
+ ] }),
6652
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(RefineEdgePanel, { controller, selection: state.selection ?? null, layers: state.adjustments.layers })
6239
6653
  ] }),
6240
- activeTab === "ai" && /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: "flex flex-col gap-4 text-xs", children: [
6241
- /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("label", { className: "flex items-center justify-between gap-2 text-slate-300", children: [
6242
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { children: "Results go to" }),
6243
- /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(
6654
+ activeTab === "ai" && /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "flex flex-col gap-4 text-xs", children: [
6655
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("label", { className: "flex items-center justify-between gap-2 text-slate-300", children: [
6656
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { children: "Results go to" }),
6657
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(
6244
6658
  "select",
6245
6659
  {
6246
6660
  "aria-label": "AI result target",
@@ -6248,54 +6662,54 @@ var ImageEditor = ({
6248
6662
  onChange: (e) => setAiTarget(e.target.value),
6249
6663
  className: "bg-slate-800 border border-slate-700 rounded px-1.5 py-1",
6250
6664
  children: [
6251
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("option", { value: "auto", children: controller.isMultiLayer ? "Auto (new layer)" : "Auto (replace image)" }),
6252
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("option", { value: "new-layer", children: "New layer / mask" }),
6253
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("option", { value: "replace", children: "Replace image" })
6665
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("option", { value: "auto", children: controller.isMultiLayer ? "Auto (new layer)" : "Auto (replace image)" }),
6666
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("option", { value: "new-layer", children: "New layer / mask" }),
6667
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("option", { value: "replace", children: "Replace image" })
6254
6668
  ]
6255
6669
  }
6256
6670
  )
6257
6671
  ] }),
6258
- /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: "p-3 rounded-lg bg-slate-950/60 border border-slate-800 flex flex-col gap-2.5", children: [
6259
- /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: "flex items-center justify-between", children: [
6260
- /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px] flex items-center gap-1.5", children: [
6261
- /* @__PURE__ */ (0, import_jsx_runtime25.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_runtime25.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" }) }),
6672
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "p-3 rounded-lg bg-slate-950/60 border border-slate-800 flex flex-col gap-2.5", children: [
6673
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "flex items-center justify-between", children: [
6674
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px] flex items-center gap-1.5", children: [
6675
+ /* @__PURE__ */ (0, import_jsx_runtime27.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_runtime27.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" }) }),
6262
6676
  "Background Removal"
6263
6677
  ] }),
6264
- isRemovingBg && /* @__PURE__ */ (0, import_jsx_runtime25.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..." })
6678
+ isRemovingBg && /* @__PURE__ */ (0, import_jsx_runtime27.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..." })
6265
6679
  ] }),
6266
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("p", { className: "text-slate-400 text-[11px] leading-relaxed", children: "Isolate foreground subject with AI alpha transparency segmentation." }),
6267
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
6680
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("p", { className: "text-slate-400 text-[11px] leading-relaxed", children: "Isolate foreground subject with AI alpha transparency segmentation." }),
6681
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
6268
6682
  "button",
6269
6683
  {
6270
6684
  type: "button",
6271
6685
  onClick: handleRemoveBackground,
6272
6686
  disabled: isRemovingBg || isInpainting,
6273
6687
  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",
6274
- children: isRemovingBg ? /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(import_jsx_runtime25.Fragment, { children: [
6275
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { className: "w-3.5 h-3.5 border-2 border-white/30 border-t-white rounded-full animate-spin" }),
6688
+ children: isRemovingBg ? /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(import_jsx_runtime27.Fragment, { children: [
6689
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("div", { className: "w-3.5 h-3.5 border-2 border-white/30 border-t-white rounded-full animate-spin" }),
6276
6690
  "Removing Background..."
6277
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(import_jsx_runtime25.Fragment, { children: [
6278
- /* @__PURE__ */ (0, import_jsx_runtime25.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_runtime25.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" }) }),
6691
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(import_jsx_runtime27.Fragment, { children: [
6692
+ /* @__PURE__ */ (0, import_jsx_runtime27.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_runtime27.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" }) }),
6279
6693
  "Remove Background"
6280
6694
  ] })
6281
6695
  }
6282
6696
  )
6283
6697
  ] }),
6284
- /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: "p-3 rounded-lg bg-slate-950/60 border border-slate-800 flex flex-col gap-2.5", children: [
6285
- /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: "flex items-center justify-between", children: [
6286
- /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px] flex items-center gap-1.5", children: [
6287
- /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("svg", { className: "w-3.5 h-3.5 text-purple-400", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
6288
- /* @__PURE__ */ (0, import_jsx_runtime25.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" }),
6289
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("path", { d: "m14 7 3 3" })
6698
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "p-3 rounded-lg bg-slate-950/60 border border-slate-800 flex flex-col gap-2.5", children: [
6699
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "flex items-center justify-between", children: [
6700
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px] flex items-center gap-1.5", children: [
6701
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("svg", { className: "w-3.5 h-3.5 text-purple-400", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
6702
+ /* @__PURE__ */ (0, import_jsx_runtime27.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" }),
6703
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("path", { d: "m14 7 3 3" })
6290
6704
  ] }),
6291
6705
  "Generative Inpainting"
6292
6706
  ] }),
6293
- isInpainting && /* @__PURE__ */ (0, import_jsx_runtime25.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..." })
6707
+ isInpainting && /* @__PURE__ */ (0, import_jsx_runtime27.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..." })
6294
6708
  ] }),
6295
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("p", { className: "text-slate-400 text-[11px] leading-relaxed", children: "Replace, modify, or fill selected regions using prompt-guided generative inpainting." }),
6296
- /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: "flex flex-col gap-1", children: [
6297
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("label", { htmlFor: "ai-inpaint-prompt-react", className: "text-slate-300 text-[11px] font-medium", children: "Prompt" }),
6298
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
6709
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("p", { className: "text-slate-400 text-[11px] leading-relaxed", children: "Replace, modify, or fill selected regions using prompt-guided generative inpainting." }),
6710
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "flex flex-col gap-1", children: [
6711
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("label", { htmlFor: "ai-inpaint-prompt-react", className: "text-slate-300 text-[11px] font-medium", children: "Prompt" }),
6712
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
6299
6713
  "textarea",
6300
6714
  {
6301
6715
  id: "ai-inpaint-prompt-react",
@@ -6307,10 +6721,10 @@ var ImageEditor = ({
6307
6721
  }
6308
6722
  )
6309
6723
  ] }),
6310
- /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: "flex flex-col gap-1", children: [
6311
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { className: "text-slate-300 text-[11px] font-medium", children: "Target Mask Region" }),
6312
- /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: "grid grid-cols-2 gap-1.5", children: [
6313
- /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(
6724
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "flex flex-col gap-1", children: [
6725
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { className: "text-slate-300 text-[11px] font-medium", children: "Target Mask Region" }),
6726
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "grid grid-cols-2 gap-1.5", children: [
6727
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(
6314
6728
  "button",
6315
6729
  {
6316
6730
  type: "button",
@@ -6323,7 +6737,7 @@ var ImageEditor = ({
6323
6737
  ]
6324
6738
  }
6325
6739
  ),
6326
- /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(
6740
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(
6327
6741
  "button",
6328
6742
  {
6329
6743
  type: "button",
@@ -6338,25 +6752,25 @@ var ImageEditor = ({
6338
6752
  )
6339
6753
  ] })
6340
6754
  ] }),
6341
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
6755
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
6342
6756
  "button",
6343
6757
  {
6344
6758
  type: "button",
6345
6759
  onClick: handleInpaint,
6346
6760
  disabled: isInpainting || isRemovingBg || !inpaintPrompt.trim(),
6347
6761
  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",
6348
- children: isInpainting ? /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(import_jsx_runtime25.Fragment, { children: [
6349
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { className: "w-3.5 h-3.5 border-2 border-white/30 border-t-white rounded-full animate-spin" }),
6762
+ children: isInpainting ? /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(import_jsx_runtime27.Fragment, { children: [
6763
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("div", { className: "w-3.5 h-3.5 border-2 border-white/30 border-t-white rounded-full animate-spin" }),
6350
6764
  "Generating Inpaint..."
6351
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(import_jsx_runtime25.Fragment, { children: [
6352
- /* @__PURE__ */ (0, import_jsx_runtime25.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_runtime25.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" }) }),
6765
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(import_jsx_runtime27.Fragment, { children: [
6766
+ /* @__PURE__ */ (0, import_jsx_runtime27.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_runtime27.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" }) }),
6353
6767
  "Apply Inpainting"
6354
6768
  ] })
6355
6769
  }
6356
6770
  )
6357
6771
  ] }),
6358
- aiError && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { className: "p-2.5 rounded-lg bg-rose-950/60 border border-rose-800 text-rose-300 text-[11px]", children: aiError }),
6359
- aiSuccess && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { className: "p-2.5 rounded-lg bg-emerald-950/60 border border-emerald-800 text-emerald-300 text-[11px]", children: aiSuccess })
6772
+ aiError && /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("div", { className: "p-2.5 rounded-lg bg-rose-950/60 border border-rose-800 text-rose-300 text-[11px]", children: aiError }),
6773
+ aiSuccess && /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("div", { className: "p-2.5 rounded-lg bg-emerald-950/60 border border-emerald-800 text-emerald-300 text-[11px]", children: aiSuccess })
6360
6774
  ] })
6361
6775
  ] })
6362
6776
  ] })
@@ -6366,7 +6780,7 @@ var ImageEditor = ({
6366
6780
  };
6367
6781
 
6368
6782
  // src/review/useReview.ts
6369
- var import_react43 = require("react");
6783
+ var import_react45 = require("react");
6370
6784
  var import_editor12 = require("@pixerate/editor");
6371
6785
  var EMPTY_COMMENTS = Object.freeze({
6372
6786
  threads: [],
@@ -6385,23 +6799,23 @@ function isCommentStore(value) {
6385
6799
  }
6386
6800
  function useComments(source) {
6387
6801
  const store = isCommentStore(source) ? source : (0, import_editor12.getCommentStore)(source);
6388
- const subscribe = (0, import_react43.useCallback)(
6802
+ const subscribe = (0, import_react45.useCallback)(
6389
6803
  (onChange) => store ? store.subscribe(() => onChange()) : noopUnsubscribe,
6390
6804
  [store]
6391
6805
  );
6392
6806
  const getSnapshot = () => store?.getSnapshot() ?? EMPTY_COMMENTS;
6393
- const snapshot = (0, import_react43.useSyncExternalStore)(subscribe, getSnapshot, getSnapshot);
6394
- return (0, import_react43.useMemo)(() => ({ ...snapshot, store }), [snapshot, store]);
6807
+ const snapshot = (0, import_react45.useSyncExternalStore)(subscribe, getSnapshot, getSnapshot);
6808
+ return (0, import_react45.useMemo)(() => ({ ...snapshot, store }), [snapshot, store]);
6395
6809
  }
6396
6810
  function useTrackChanges(editor) {
6397
6811
  const store = (0, import_editor12.getTrackChangesStore)(editor ?? null);
6398
- const subscribe = (0, import_react43.useCallback)(
6812
+ const subscribe = (0, import_react45.useCallback)(
6399
6813
  (onChange) => store ? store.subscribe(() => onChange()) : noopUnsubscribe,
6400
6814
  [store]
6401
6815
  );
6402
6816
  const getSnapshot = () => store?.getSnapshot() ?? EMPTY_TRACK_CHANGES;
6403
- const snapshot = (0, import_react43.useSyncExternalStore)(subscribe, getSnapshot, getSnapshot);
6404
- return (0, import_react43.useMemo)(() => {
6817
+ const snapshot = (0, import_react45.useSyncExternalStore)(subscribe, getSnapshot, getSnapshot);
6818
+ return (0, import_react45.useMemo)(() => {
6405
6819
  const run = (fn) => () => {
6406
6820
  if (editor && !editor.isDestroyed) fn(editor);
6407
6821
  };
@@ -6424,9 +6838,9 @@ function useTrackChanges(editor) {
6424
6838
  }
6425
6839
 
6426
6840
  // src/review/CommentsPanel.tsx
6427
- var import_react44 = require("react");
6841
+ var import_react46 = require("react");
6428
6842
  var import_editor13 = require("@pixerate/editor");
6429
- var import_jsx_runtime26 = require("react/jsx-runtime");
6843
+ var import_jsx_runtime28 = require("react/jsx-runtime");
6430
6844
  var buttonClass2 = "rounded px-2 py-1 text-xs text-muted-foreground hover:bg-muted hover:text-foreground disabled:opacity-50";
6431
6845
  var primaryButtonClass = "rounded px-2 py-1 text-xs bg-primary/10 text-primary hover:bg-primary/20 disabled:opacity-50";
6432
6846
  var textareaClass = "w-full resize-y rounded-md border border-input bg-background p-2 text-sm text-foreground focus:outline-none focus:ring-2 focus:ring-primary";
@@ -6440,18 +6854,18 @@ var CommentsPanel = ({
6440
6854
  emptyMessage = "No comments yet. Select text and add a comment."
6441
6855
  }) => {
6442
6856
  const { threads, activeThreadId, store } = useComments(storeProp ?? editor);
6443
- const [showResolved, setShowResolved] = (0, import_react44.useState)(showResolvedInitial);
6857
+ const [showResolved, setShowResolved] = (0, import_react46.useState)(showResolvedInitial);
6444
6858
  const author = authorProp ?? (0, import_editor13.getCommentAuthor)(editor) ?? { id: "anonymous", name: "Anonymous" };
6445
6859
  const visible = threads.filter((t) => showResolved || !t.resolved);
6446
6860
  const resolvedCount = threads.filter((t) => t.resolved).length;
6447
- return /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("aside", { className, "aria-label": "Comments", children: [
6448
- /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("div", { className: "flex items-center justify-between gap-2", children: [
6449
- /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("span", { className: "text-xs font-medium text-muted-foreground", children: [
6861
+ return /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("aside", { className, "aria-label": "Comments", children: [
6862
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("div", { className: "flex items-center justify-between gap-2", children: [
6863
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("span", { className: "text-xs font-medium text-muted-foreground", children: [
6450
6864
  threads.length - resolvedCount,
6451
6865
  " open",
6452
6866
  resolvedCount > 0 ? ` \xB7 ${resolvedCount} resolved` : ""
6453
6867
  ] }),
6454
- resolvedCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
6868
+ resolvedCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
6455
6869
  "button",
6456
6870
  {
6457
6871
  type: "button",
@@ -6462,7 +6876,7 @@ var CommentsPanel = ({
6462
6876
  }
6463
6877
  )
6464
6878
  ] }),
6465
- visible.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("p", { className: "text-xs text-muted-foreground", children: emptyMessage }) : /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("ul", { className: "flex flex-col gap-2", role: "list", children: visible.map((thread) => /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
6879
+ visible.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("p", { className: "text-xs text-muted-foreground", children: emptyMessage }) : /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("ul", { className: "flex flex-col gap-2", role: "list", children: visible.map((thread) => /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
6466
6880
  CommentThreadCard,
6467
6881
  {
6468
6882
  thread,
@@ -6484,7 +6898,7 @@ var CommentThreadCard = ({
6484
6898
  author,
6485
6899
  formatTime
6486
6900
  }) => {
6487
- const [draft, setDraft] = (0, import_react44.useState)("");
6901
+ const [draft, setDraft] = (0, import_react46.useState)("");
6488
6902
  const isDraft = thread.body === "";
6489
6903
  const quote = thread.anchor?.quote ?? thread.lastQuote ?? "";
6490
6904
  const live = editor && !editor.isDestroyed ? editor : null;
@@ -6510,7 +6924,7 @@ var CommentThreadCard = ({
6510
6924
  setDraft("");
6511
6925
  };
6512
6926
  const stop = (e) => e.stopPropagation();
6513
- return /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(
6927
+ return /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(
6514
6928
  "li",
6515
6929
  {
6516
6930
  className: `flex flex-col gap-2 rounded-md border bg-background p-3 cursor-pointer ${active ? "border-primary ring-1 ring-primary" : "border-border"} ${thread.resolved ? "opacity-70" : ""}`,
@@ -6526,24 +6940,24 @@ var CommentThreadCard = ({
6526
6940
  }
6527
6941
  },
6528
6942
  children: [
6529
- /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("div", { className: "flex items-baseline justify-between gap-2", children: [
6530
- /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("span", { className: "font-medium", children: thread.author.name }),
6531
- /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("span", { className: "text-xs text-muted-foreground", children: formatTime(thread.createdAt) })
6943
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("div", { className: "flex items-baseline justify-between gap-2", children: [
6944
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("span", { className: "font-medium", children: thread.author.name }),
6945
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("span", { className: "text-xs text-muted-foreground", children: formatTime(thread.createdAt) })
6532
6946
  ] }),
6533
- quote && /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("blockquote", { className: "border-l-2 border-border pl-2 text-xs text-muted-foreground line-clamp-2", children: quote }),
6534
- thread.orphaned && /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("span", { className: "text-xs text-destructive", children: "The commented text was deleted." }),
6535
- thread.resolved && /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("span", { className: "text-xs text-muted-foreground", children: "Resolved" }),
6536
- !isDraft && /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("p", { className: "whitespace-pre-wrap break-words", children: thread.body }),
6537
- thread.replies.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("ul", { className: "flex flex-col gap-2 border-l border-border pl-3", role: "list", children: thread.replies.map((reply) => /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("li", { className: "flex flex-col gap-0.5", "data-reply-id": reply.id, children: [
6538
- /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("span", { className: "text-xs", children: [
6539
- /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("span", { className: "font-medium", children: reply.author.name }),
6947
+ quote && /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("blockquote", { className: "border-l-2 border-border pl-2 text-xs text-muted-foreground line-clamp-2", children: quote }),
6948
+ thread.orphaned && /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("span", { className: "text-xs text-destructive", children: "The commented text was deleted." }),
6949
+ thread.resolved && /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("span", { className: "text-xs text-muted-foreground", children: "Resolved" }),
6950
+ !isDraft && /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("p", { className: "whitespace-pre-wrap break-words", children: thread.body }),
6951
+ thread.replies.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("ul", { className: "flex flex-col gap-2 border-l border-border pl-3", role: "list", children: thread.replies.map((reply) => /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("li", { className: "flex flex-col gap-0.5", "data-reply-id": reply.id, children: [
6952
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("span", { className: "text-xs", children: [
6953
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("span", { className: "font-medium", children: reply.author.name }),
6540
6954
  " ",
6541
- /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("span", { className: "text-muted-foreground", children: formatTime(reply.createdAt) })
6955
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("span", { className: "text-muted-foreground", children: formatTime(reply.createdAt) })
6542
6956
  ] }),
6543
- /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("p", { className: "whitespace-pre-wrap break-words", children: reply.body })
6957
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("p", { className: "whitespace-pre-wrap break-words", children: reply.body })
6544
6958
  ] }, reply.id)) }),
6545
- (active || isDraft) && !thread.resolved && /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("div", { className: "flex flex-col gap-1", onClick: stop, children: [
6546
- /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
6959
+ (active || isDraft) && !thread.resolved && /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("div", { className: "flex flex-col gap-1", onClick: stop, children: [
6960
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
6547
6961
  "textarea",
6548
6962
  {
6549
6963
  className: textareaClass,
@@ -6561,9 +6975,9 @@ var CommentThreadCard = ({
6561
6975
  }
6562
6976
  }
6563
6977
  ),
6564
- /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("div", { className: "flex justify-end gap-1", children: [
6565
- isDraft && /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("button", { type: "button", className: buttonClass2, onClick: remove, children: "Cancel" }),
6566
- /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
6978
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("div", { className: "flex justify-end gap-1", children: [
6979
+ isDraft && /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("button", { type: "button", className: buttonClass2, onClick: remove, children: "Cancel" }),
6980
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
6567
6981
  "button",
6568
6982
  {
6569
6983
  type: "button",
@@ -6575,9 +6989,9 @@ var CommentThreadCard = ({
6575
6989
  )
6576
6990
  ] })
6577
6991
  ] }),
6578
- !isDraft && /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("div", { className: "flex justify-end gap-1", onClick: stop, children: [
6579
- /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("button", { type: "button", className: buttonClass2, onClick: () => setResolved(!thread.resolved), children: thread.resolved ? "Reopen" : "Resolve" }),
6580
- /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
6992
+ !isDraft && /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("div", { className: "flex justify-end gap-1", onClick: stop, children: [
6993
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("button", { type: "button", className: buttonClass2, onClick: () => setResolved(!thread.resolved), children: thread.resolved ? "Reopen" : "Resolve" }),
6994
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
6581
6995
  "button",
6582
6996
  {
6583
6997
  type: "button",
@@ -6593,7 +7007,7 @@ var CommentThreadCard = ({
6593
7007
  };
6594
7008
 
6595
7009
  // src/review/ReviewToolbar.tsx
6596
- var import_jsx_runtime27 = require("react/jsx-runtime");
7010
+ var import_jsx_runtime29 = require("react/jsx-runtime");
6597
7011
  var defaultDescriptionClass = "max-w-[16rem] truncate px-1 text-xs text-muted-foreground";
6598
7012
  var buttonClass3 = "rounded px-2 py-1 text-xs text-foreground hover:bg-muted disabled:opacity-40 disabled:hover:bg-transparent";
6599
7013
  var ReviewToolbar = ({
@@ -6606,8 +7020,8 @@ var ReviewToolbar = ({
6606
7020
  const count = review.changes.length;
6607
7021
  const current = review.currentChange;
6608
7022
  const index = current ? review.changes.findIndex((c) => c.id === current.id) + 1 : 0;
6609
- return /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className, role: "toolbar", "aria-label": "Review", children: [
6610
- /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
7023
+ return /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("div", { className, role: "toolbar", "aria-label": "Review", children: [
7024
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
6611
7025
  "button",
6612
7026
  {
6613
7027
  type: "button",
@@ -6618,8 +7032,8 @@ var ReviewToolbar = ({
6618
7032
  children: review.enabled ? "Tracking on" : "Track changes"
6619
7033
  }
6620
7034
  ),
6621
- /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { className: "mx-1 h-4 w-px bg-border", "aria-hidden": "true" }),
6622
- /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
7035
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("span", { className: "mx-1 h-4 w-px bg-border", "aria-hidden": "true" }),
7036
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
6623
7037
  "button",
6624
7038
  {
6625
7039
  type: "button",
@@ -6630,8 +7044,8 @@ var ReviewToolbar = ({
6630
7044
  children: "\u2039"
6631
7045
  }
6632
7046
  ),
6633
- /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { className: "min-w-[4rem] text-center text-xs text-muted-foreground", "aria-live": "polite", children: count === 0 ? "No changes" : current ? `${index} of ${count}` : `${count} changes` }),
6634
- /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
7047
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("span", { className: "min-w-[4rem] text-center text-xs text-muted-foreground", "aria-live": "polite", children: count === 0 ? "No changes" : current ? `${index} of ${count}` : `${count} changes` }),
7048
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
6635
7049
  "button",
6636
7050
  {
6637
7051
  type: "button",
@@ -6642,7 +7056,7 @@ var ReviewToolbar = ({
6642
7056
  children: "\u203A"
6643
7057
  }
6644
7058
  ),
6645
- showDescription && current && /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(
7059
+ showDescription && current && /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(
6646
7060
  "span",
6647
7061
  {
6648
7062
  className: descriptionClassName,
@@ -6654,8 +7068,8 @@ var ReviewToolbar = ({
6654
7068
  ]
6655
7069
  }
6656
7070
  ),
6657
- /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { className: "mx-1 h-4 w-px bg-border", "aria-hidden": "true" }),
6658
- /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
7071
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("span", { className: "mx-1 h-4 w-px bg-border", "aria-hidden": "true" }),
7072
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
6659
7073
  "button",
6660
7074
  {
6661
7075
  type: "button",
@@ -6665,7 +7079,7 @@ var ReviewToolbar = ({
6665
7079
  children: "Accept"
6666
7080
  }
6667
7081
  ),
6668
- /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
7082
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
6669
7083
  "button",
6670
7084
  {
6671
7085
  type: "button",
@@ -6675,18 +7089,18 @@ var ReviewToolbar = ({
6675
7089
  children: "Reject"
6676
7090
  }
6677
7091
  ),
6678
- /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("button", { type: "button", className: buttonClass3, disabled: count === 0, onClick: review.acceptAll, children: "Accept all" }),
6679
- /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("button", { type: "button", className: buttonClass3, disabled: count === 0, onClick: review.rejectAll, children: "Reject all" })
7092
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("button", { type: "button", className: buttonClass3, disabled: count === 0, onClick: review.acceptAll, children: "Accept all" }),
7093
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("button", { type: "button", className: buttonClass3, disabled: count === 0, onClick: review.rejectAll, children: "Reject all" })
6680
7094
  ] });
6681
7095
  };
6682
7096
 
6683
7097
  // src/review/CommentButton.tsx
6684
- var import_jsx_runtime28 = require("react/jsx-runtime");
7098
+ var import_jsx_runtime30 = require("react/jsx-runtime");
6685
7099
  var CommentButton = ({
6686
7100
  editor,
6687
7101
  className = "rounded px-2 py-1 text-xs text-popover-foreground hover:bg-muted",
6688
7102
  children = "Comment"
6689
- }) => /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
7103
+ }) => /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
6690
7104
  "button",
6691
7105
  {
6692
7106
  type: "button",
@@ -6698,13 +7112,13 @@ var CommentButton = ({
6698
7112
  );
6699
7113
 
6700
7114
  // src/review/useDocumentCompare.ts
6701
- var import_react45 = require("react");
7115
+ var import_react47 = require("react");
6702
7116
  var import_editor14 = require("@pixerate/editor");
6703
7117
  function useDocumentCompare(editor, options) {
6704
- const [summary, setSummary] = (0, import_react45.useState)(null);
6705
- const defaults = (0, import_react45.useRef)(options);
7118
+ const [summary, setSummary] = (0, import_react47.useState)(null);
7119
+ const defaults = (0, import_react47.useRef)(options);
6706
7120
  defaults.current = options;
6707
- const compare = (0, import_react45.useCallback)(
7121
+ const compare = (0, import_react47.useCallback)(
6708
7122
  (original, revised, overrides) => {
6709
7123
  if (!editor || editor.isDestroyed) return null;
6710
7124
  const result = (0, import_editor14.applyDocumentComparison)(editor, original, revised, {
@@ -6716,13 +7130,13 @@ function useDocumentCompare(editor, options) {
6716
7130
  },
6717
7131
  [editor]
6718
7132
  );
6719
- const clear = (0, import_react45.useCallback)(() => setSummary(null), []);
6720
- return (0, import_react45.useMemo)(() => ({ summary, compare, clear }), [summary, compare, clear]);
7133
+ const clear = (0, import_react47.useCallback)(() => setSummary(null), []);
7134
+ return (0, import_react47.useMemo)(() => ({ summary, compare, clear }), [summary, compare, clear]);
6721
7135
  }
6722
7136
 
6723
7137
  // src/review/TrackedChangesList.tsx
6724
7138
  var import_editor15 = require("@pixerate/editor");
6725
- var import_jsx_runtime29 = require("react/jsx-runtime");
7139
+ var import_jsx_runtime31 = require("react/jsx-runtime");
6726
7140
  var buttonClass4 = "rounded px-1.5 py-0.5 text-xs text-foreground hover:bg-muted disabled:opacity-40";
6727
7141
  var TrackedChangesList = ({
6728
7142
  editor,
@@ -6733,18 +7147,18 @@ var TrackedChangesList = ({
6733
7147
  }) => {
6734
7148
  const review = useTrackChanges(editor);
6735
7149
  if (review.changes.length === 0) {
6736
- return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("div", { className, "data-tracked-changes-list": "", children: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("div", { className: "pixerate-changes-empty text-xs text-muted-foreground", children: emptyState }) });
7150
+ return /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("div", { className, "data-tracked-changes-list": "", children: /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("div", { className: "pixerate-changes-empty text-xs text-muted-foreground", children: emptyState }) });
6737
7151
  }
6738
- return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("ul", { className, role: "list", "aria-label": "Tracked changes", "data-tracked-changes-list": "", children: review.changes.map((change) => {
7152
+ return /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("ul", { className, role: "list", "aria-label": "Tracked changes", "data-tracked-changes-list": "", children: review.changes.map((change) => {
6739
7153
  const active = change.id === review.currentChangeId;
6740
- return /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(
7154
+ return /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)(
6741
7155
  "li",
6742
7156
  {
6743
7157
  className: active ? `${itemClassName} ${activeItemClassName}` : itemClassName,
6744
7158
  "data-change-type": change.type,
6745
7159
  "data-change-id": change.id,
6746
7160
  children: [
6747
- /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(
7161
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)(
6748
7162
  "button",
6749
7163
  {
6750
7164
  type: "button",
@@ -6752,14 +7166,14 @@ var TrackedChangesList = ({
6752
7166
  title: `${change.author.name}: ${change.description}`,
6753
7167
  onClick: () => review.select(change.id),
6754
7168
  children: [
6755
- /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("span", { className: "font-medium", children: (0, import_editor15.trackedChangeLabel)(change) }),
7169
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("span", { className: "font-medium", children: (0, import_editor15.trackedChangeLabel)(change) }),
6756
7170
  change.author.name ? ` \xB7 ${change.author.name}` : "",
6757
- /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("span", { className: "block truncate text-muted-foreground", children: change.description })
7171
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("span", { className: "block truncate text-muted-foreground", children: change.description })
6758
7172
  ]
6759
7173
  }
6760
7174
  ),
6761
- /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("button", { type: "button", className: buttonClass4, onClick: () => review.accept(change.id), children: "Accept" }),
6762
- /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("button", { type: "button", className: buttonClass4, onClick: () => review.reject(change.id), children: "Reject" })
7175
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("button", { type: "button", className: buttonClass4, onClick: () => review.accept(change.id), children: "Accept" }),
7176
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("button", { type: "button", className: buttonClass4, onClick: () => review.reject(change.id), children: "Reject" })
6763
7177
  ]
6764
7178
  },
6765
7179
  `${change.type}:${change.id}`
@@ -6768,12 +7182,12 @@ var TrackedChangesList = ({
6768
7182
  };
6769
7183
 
6770
7184
  // src/commands/useCommandPalette.ts
6771
- var import_react46 = require("react");
7185
+ var import_react48 = require("react");
6772
7186
  var import_commands2 = require("@pixerate/editor/commands");
6773
7187
  function useCommandPalette(registry, options = {}) {
6774
- const optionsRef = (0, import_react46.useRef)(options);
7188
+ const optionsRef = (0, import_react48.useRef)(options);
6775
7189
  optionsRef.current = options;
6776
- const controller = (0, import_react46.useMemo)(
7190
+ const controller = (0, import_react48.useMemo)(
6777
7191
  () => (0, import_commands2.createCommandPaletteController)(registry, {
6778
7192
  context: () => {
6779
7193
  const c = optionsRef.current.context;
@@ -6794,9 +7208,9 @@ function useCommandPalette(registry, options = {}) {
6794
7208
  // eslint-disable-next-line react-hooks/exhaustive-deps
6795
7209
  [registry]
6796
7210
  );
6797
- const state = (0, import_react46.useSyncExternalStore)(controller.subscribe, controller.getState, controller.getState);
7211
+ const state = (0, import_react48.useSyncExternalStore)(controller.subscribe, controller.getState, controller.getState);
6798
7212
  const { hotkey, hotkeyTarget } = options;
6799
- (0, import_react46.useEffect)(() => {
7213
+ (0, import_react48.useEffect)(() => {
6800
7214
  if (!hotkey) return;
6801
7215
  const target = hotkeyTarget ?? (typeof window !== "undefined" ? window : null);
6802
7216
  if (!target) return;
@@ -6824,13 +7238,13 @@ function useCommandPalette(registry, options = {}) {
6824
7238
  }
6825
7239
 
6826
7240
  // src/commands/CommandPalette.tsx
6827
- var import_react47 = __toESM(require("react"), 1);
6828
- var import_jsx_runtime30 = require("react/jsx-runtime");
7241
+ var import_react49 = __toESM(require("react"), 1);
7242
+ var import_jsx_runtime32 = require("react/jsx-runtime");
6829
7243
  function HighlightedLabel({ item }) {
6830
- if (item.labelIndices.length === 0) return /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(import_jsx_runtime30.Fragment, { children: item.label });
7244
+ if (item.labelIndices.length === 0) return /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_jsx_runtime32.Fragment, { children: item.label });
6831
7245
  const marked = new Set(item.labelIndices);
6832
- return /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(import_jsx_runtime30.Fragment, { children: Array.from(item.label).map(
6833
- (ch, i) => marked.has(i) ? /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("span", { className: "font-semibold text-foreground", "data-match": "", children: ch }, i) : /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(import_react47.default.Fragment, { children: ch }, i)
7246
+ return /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_jsx_runtime32.Fragment, { children: Array.from(item.label).map(
7247
+ (ch, i) => marked.has(i) ? /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("span", { className: "font-semibold text-foreground", "data-match": "", children: ch }, i) : /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_react49.default.Fragment, { children: ch }, i)
6834
7248
  ) });
6835
7249
  }
6836
7250
  var CommandPalette = ({
@@ -6839,22 +7253,22 @@ var CommandPalette = ({
6839
7253
  emptyMessage = "No matching commands",
6840
7254
  className = ""
6841
7255
  }) => {
6842
- const inputRef = (0, import_react47.useRef)(null);
6843
- const listRef = (0, import_react47.useRef)(null);
6844
- const baseId = (0, import_react47.useId)();
7256
+ const inputRef = (0, import_react49.useRef)(null);
7257
+ const listRef = (0, import_react49.useRef)(null);
7258
+ const baseId = (0, import_react49.useId)();
6845
7259
  const listId = `${baseId}-list`;
6846
7260
  const optionId = (index) => `${baseId}-option-${index}`;
6847
7261
  const { open, query, items, activeIndex } = palette;
6848
- (0, import_react47.useEffect)(() => {
7262
+ (0, import_react49.useEffect)(() => {
6849
7263
  if (open) inputRef.current?.focus();
6850
7264
  }, [open]);
6851
- (0, import_react47.useEffect)(() => {
7265
+ (0, import_react49.useEffect)(() => {
6852
7266
  if (!open) return;
6853
7267
  const active = listRef.current?.querySelector('[aria-selected="true"]');
6854
7268
  active?.scrollIntoView?.({ block: "nearest" });
6855
7269
  }, [open, activeIndex]);
6856
7270
  if (!open) return null;
6857
- return /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
7271
+ return /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
6858
7272
  "div",
6859
7273
  {
6860
7274
  className: "fixed inset-0 z-50 flex items-start justify-center bg-black/30 px-4 pt-[15vh]",
@@ -6862,7 +7276,7 @@ var CommandPalette = ({
6862
7276
  onMouseDown: (e) => {
6863
7277
  if (e.target === e.currentTarget) palette.setOpen(false);
6864
7278
  },
6865
- children: /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)(
7279
+ children: /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)(
6866
7280
  "div",
6867
7281
  {
6868
7282
  role: "dialog",
@@ -6871,7 +7285,7 @@ var CommandPalette = ({
6871
7285
  className: `w-full max-w-lg overflow-hidden rounded-lg border border-border bg-popover text-popover-foreground shadow-xl ${className}`,
6872
7286
  style: { background: "var(--popover, var(--background, #fff))" },
6873
7287
  children: [
6874
- /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
7288
+ /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
6875
7289
  "input",
6876
7290
  {
6877
7291
  ref: inputRef,
@@ -6891,7 +7305,7 @@ var CommandPalette = ({
6891
7305
  className: "w-full border-b border-border bg-transparent px-3 py-2 text-sm outline-none placeholder:text-muted-foreground"
6892
7306
  }
6893
7307
  ),
6894
- items.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("div", { className: "px-3 py-6 text-center text-sm text-muted-foreground", children: emptyMessage }) : /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("ul", { ref: listRef, id: listId, role: "listbox", "aria-label": "Commands", className: "max-h-80 overflow-y-auto py-1", children: items.map((item, index) => /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)(
7308
+ items.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("div", { className: "px-3 py-6 text-center text-sm text-muted-foreground", children: emptyMessage }) : /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("ul", { ref: listRef, id: listId, role: "listbox", "aria-label": "Commands", className: "max-h-80 overflow-y-auto py-1", children: items.map((item, index) => /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)(
6895
7309
  "li",
6896
7310
  {
6897
7311
  id: optionId(index),
@@ -6906,12 +7320,12 @@ var CommandPalette = ({
6906
7320
  onClick: () => palette.run(item.id),
6907
7321
  className: `flex cursor-pointer items-center gap-2 px-3 py-1.5 text-sm ${index === activeIndex ? "bg-muted" : ""} ${item.enabled ? "" : "cursor-default opacity-50"}`,
6908
7322
  children: [
6909
- /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("span", { className: "min-w-0 flex-1 truncate", children: [
6910
- /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(HighlightedLabel, { item }),
6911
- item.disabledReason && /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("span", { className: "ml-2 text-xs text-muted-foreground", "data-disabled-reason": "", children: item.disabledReason })
7323
+ /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("span", { className: "min-w-0 flex-1 truncate", children: [
7324
+ /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(HighlightedLabel, { item }),
7325
+ item.disabledReason && /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("span", { className: "ml-2 text-xs text-muted-foreground", "data-disabled-reason": "", children: item.disabledReason })
6912
7326
  ] }),
6913
- item.group && /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("span", { className: "shrink-0 text-xs text-muted-foreground", children: item.group }),
6914
- item.shortcut && /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("kbd", { className: "shrink-0 rounded border border-border px-1.5 font-mono text-[10px] text-muted-foreground", children: item.shortcut })
7327
+ item.group && /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("span", { className: "shrink-0 text-xs text-muted-foreground", children: item.group }),
7328
+ item.shortcut && /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("kbd", { className: "shrink-0 rounded border border-border px-1.5 font-mono text-[10px] text-muted-foreground", children: item.shortcut })
6915
7329
  ]
6916
7330
  },
6917
7331
  item.id
@@ -6924,12 +7338,12 @@ var CommandPalette = ({
6924
7338
  };
6925
7339
 
6926
7340
  // src/commands/useCommandShortcuts.ts
6927
- var import_react48 = require("react");
7341
+ var import_react50 = require("react");
6928
7342
  function useCommandShortcuts(registry, options = {}) {
6929
- const optionsRef = (0, import_react48.useRef)(options);
7343
+ const optionsRef = (0, import_react50.useRef)(options);
6930
7344
  optionsRef.current = options;
6931
7345
  const { enabled = true, target } = options;
6932
- (0, import_react48.useEffect)(() => {
7346
+ (0, import_react50.useEffect)(() => {
6933
7347
  if (!registry || !enabled) return;
6934
7348
  const eventTarget = target ?? (typeof window !== "undefined" ? window : null);
6935
7349
  if (!eventTarget) return;
@@ -6947,14 +7361,14 @@ function useCommandShortcuts(registry, options = {}) {
6947
7361
  }, [registry, enabled, target]);
6948
7362
  }
6949
7363
  function useRegisterCommands(registry, specs) {
6950
- (0, import_react48.useEffect)(() => {
7364
+ (0, import_react50.useEffect)(() => {
6951
7365
  if (!registry || !specs || specs.length === 0) return;
6952
7366
  return registry.register(specs);
6953
7367
  }, [registry, specs]);
6954
7368
  }
6955
7369
 
6956
7370
  // src/persistence/useAutosave.ts
6957
- var import_react49 = require("react");
7371
+ var import_react51 = require("react");
6958
7372
  var import_persistence = require("@pixerate/editor/persistence");
6959
7373
  var IDLE = {
6960
7374
  status: "idle",
@@ -6965,18 +7379,18 @@ var IDLE = {
6965
7379
  ready: false
6966
7380
  };
6967
7381
  function useAutosave(options) {
6968
- const optionsRef = (0, import_react49.useRef)(options);
7382
+ const optionsRef = (0, import_react51.useRef)(options);
6969
7383
  optionsRef.current = options;
6970
7384
  const { storage, key, history, dirty } = options;
6971
7385
  const recover = options.recover !== false;
6972
7386
  const flushOnHide = options.flushOnHide !== false;
6973
7387
  const ready = options.ready !== false;
6974
- const [controller, setController] = (0, import_react49.useState)(null);
6975
- const [status, setStatus] = (0, import_react49.useState)(IDLE);
6976
- const [recovery, setRecovery] = (0, import_react49.useState)(null);
6977
- const [pendingRecovery, setPendingRecovery] = (0, import_react49.useState)(recover);
6978
- const revisionsRef = (0, import_react49.useRef)(null);
6979
- (0, import_react49.useEffect)(() => {
7388
+ const [controller, setController] = (0, import_react51.useState)(null);
7389
+ const [status, setStatus] = (0, import_react51.useState)(IDLE);
7390
+ const [recovery, setRecovery] = (0, import_react51.useState)(null);
7391
+ const [pendingRecovery, setPendingRecovery] = (0, import_react51.useState)(recover);
7392
+ const revisionsRef = (0, import_react51.useRef)(null);
7393
+ (0, import_react51.useEffect)(() => {
6980
7394
  const revisions = (0, import_persistence.createRevisionTracker)({ history, dirty });
6981
7395
  const o = optionsRef.current;
6982
7396
  const instance = (0, import_persistence.createAutosaveController)({
@@ -7019,10 +7433,10 @@ function useAutosave(options) {
7019
7433
  if (revisionsRef.current === revisions) revisionsRef.current = null;
7020
7434
  };
7021
7435
  }, [storage, key, history, dirty, recover]);
7022
- (0, import_react49.useEffect)(() => {
7436
+ (0, import_react51.useEffect)(() => {
7023
7437
  if (controller && ready && !pendingRecovery) controller.ready();
7024
7438
  }, [controller, ready, pendingRecovery]);
7025
- (0, import_react49.useEffect)(() => {
7439
+ (0, import_react51.useEffect)(() => {
7026
7440
  if (!controller || !flushOnHide || typeof window === "undefined") return;
7027
7441
  const flush = () => void controller.flushNow();
7028
7442
  const onVisibility = () => {
@@ -7035,31 +7449,31 @@ function useAutosave(options) {
7035
7449
  document.removeEventListener("visibilitychange", onVisibility);
7036
7450
  };
7037
7451
  }, [controller, flushOnHide]);
7038
- const restore = (0, import_react49.useCallback)(() => {
7452
+ const restore = (0, import_react51.useCallback)(() => {
7039
7453
  const snapshot = recovery?.snapshot;
7040
7454
  setRecovery(null);
7041
7455
  setPendingRecovery(false);
7042
7456
  return snapshot;
7043
7457
  }, [recovery]);
7044
- const discard = (0, import_react49.useCallback)(async () => {
7458
+ const discard = (0, import_react51.useCallback)(async () => {
7045
7459
  if (controller) await controller.discardRecovery();
7046
7460
  setRecovery(null);
7047
7461
  setPendingRecovery(false);
7048
7462
  }, [controller]);
7049
- const flushNow = (0, import_react49.useCallback)(async () => {
7463
+ const flushNow = (0, import_react51.useCallback)(async () => {
7050
7464
  await controller?.flushNow();
7051
7465
  }, [controller]);
7052
- const markClean = (0, import_react49.useCallback)(async () => {
7466
+ const markClean = (0, import_react51.useCallback)(async () => {
7053
7467
  revisionsRef.current?.markSaved();
7054
7468
  await controller?.markClean();
7055
7469
  }, [controller]);
7056
- const notifyChange = (0, import_react49.useCallback)(() => revisionsRef.current?.bump(), []);
7470
+ const notifyChange = (0, import_react51.useCallback)(() => revisionsRef.current?.bump(), []);
7057
7471
  return { status, recovery, pendingRecovery, restore, discard, flushNow, markClean, notifyChange, controller };
7058
7472
  }
7059
7473
 
7060
7474
  // src/persistence/RecoveryPrompt.tsx
7061
7475
  var import_persistence2 = require("@pixerate/editor/persistence");
7062
- var import_jsx_runtime31 = require("react/jsx-runtime");
7476
+ var import_jsx_runtime33 = require("react/jsx-runtime");
7063
7477
  var RecoveryPrompt = ({
7064
7478
  recovery,
7065
7479
  onRestore,
@@ -7071,7 +7485,7 @@ var RecoveryPrompt = ({
7071
7485
  }) => {
7072
7486
  if (!recovery) return null;
7073
7487
  const { name } = recovery.meta;
7074
- return /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)(
7488
+ return /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(
7075
7489
  "div",
7076
7490
  {
7077
7491
  role: "alertdialog",
@@ -7079,15 +7493,15 @@ var RecoveryPrompt = ({
7079
7493
  "data-recovery-prompt": "",
7080
7494
  className: `flex flex-wrap items-center gap-3 rounded-md border border-border bg-background px-3 py-2 text-sm text-foreground shadow-sm ${className}`,
7081
7495
  children: [
7082
- /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("span", { className: "min-w-0 flex-1", children: [
7496
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("span", { className: "min-w-0 flex-1", children: [
7083
7497
  (0, import_persistence2.formatRecoveryMessage)(recovery.meta, formatTime),
7084
- name ? /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("span", { className: "ml-1 text-xs text-muted-foreground", children: [
7498
+ name ? /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("span", { className: "ml-1 text-xs text-muted-foreground", children: [
7085
7499
  "(",
7086
7500
  name,
7087
7501
  ")"
7088
7502
  ] }) : null
7089
7503
  ] }),
7090
- /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
7504
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
7091
7505
  "button",
7092
7506
  {
7093
7507
  type: "button",
@@ -7097,7 +7511,7 @@ var RecoveryPrompt = ({
7097
7511
  children: restoreLabel
7098
7512
  }
7099
7513
  ),
7100
- /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
7514
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
7101
7515
  "button",
7102
7516
  {
7103
7517
  type: "button",
@@ -7113,17 +7527,17 @@ var RecoveryPrompt = ({
7113
7527
  };
7114
7528
 
7115
7529
  // src/generation/useGenerationPlan.ts
7116
- var import_react50 = require("react");
7530
+ var import_react52 = require("react");
7117
7531
  var import_generation = require("@pixerate/editor/generation");
7118
7532
  function useGenerationPlan(request, capabilities, options = {}) {
7119
7533
  const key = JSON.stringify([request, capabilities, options]);
7120
- return (0, import_react50.useMemo)(() => (0, import_generation.planGenerationRequest)(request, capabilities, options), [key]);
7534
+ return (0, import_react52.useMemo)(() => (0, import_generation.planGenerationRequest)(request, capabilities, options), [key]);
7121
7535
  }
7122
7536
  function useJobProgress(options) {
7123
7537
  const { startedAt, expectedMs, done, now, intervalMs = 1e3 } = options;
7124
- const [tick, setTick] = (0, import_react50.useState)(() => Date.now());
7538
+ const [tick, setTick] = (0, import_react52.useState)(() => Date.now());
7125
7539
  const ticking = !done && now === void 0;
7126
- (0, import_react50.useEffect)(() => {
7540
+ (0, import_react52.useEffect)(() => {
7127
7541
  if (!ticking) return;
7128
7542
  setTick(Date.now());
7129
7543
  const id = setInterval(() => setTick(Date.now()), Math.max(16, intervalMs));
@@ -7133,9 +7547,9 @@ function useJobProgress(options) {
7133
7547
  }
7134
7548
 
7135
7549
  // src/generation/GenerationSettings.tsx
7136
- var import_react51 = require("react");
7550
+ var import_react53 = require("react");
7137
7551
  var import_generation2 = require("@pixerate/editor/generation");
7138
- var import_jsx_runtime32 = require("react/jsx-runtime");
7552
+ var import_jsx_runtime34 = require("react/jsx-runtime");
7139
7553
  var inputClass2 = "w-full rounded-md border border-border bg-background px-2 py-1 text-sm outline-none focus:ring-1 focus:ring-ring";
7140
7554
  function Control({
7141
7555
  control,
@@ -7145,7 +7559,7 @@ function Control({
7145
7559
  }) {
7146
7560
  const shown = value ?? control.defaultValue;
7147
7561
  if (control.kind === "checkbox") {
7148
- return /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
7562
+ return /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
7149
7563
  "input",
7150
7564
  {
7151
7565
  id,
@@ -7157,7 +7571,7 @@ function Control({
7157
7571
  );
7158
7572
  }
7159
7573
  if (control.kind === "select") {
7160
- return /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)(
7574
+ return /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(
7161
7575
  "select",
7162
7576
  {
7163
7577
  id,
@@ -7166,14 +7580,14 @@ function Control({
7166
7580
  value: shown === void 0 ? "" : String(shown),
7167
7581
  onChange: (e) => onValue((0, import_generation2.parseControlValue)(control, e.target.value)),
7168
7582
  children: [
7169
- shown === void 0 && /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("option", { value: "", children: "Default" }),
7170
- control.options.map((option) => /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("option", { value: String(option.value), children: option.label }, String(option.value)))
7583
+ shown === void 0 && /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("option", { value: "", children: "Default" }),
7584
+ control.options.map((option) => /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("option", { value: String(option.value), children: option.label }, String(option.value)))
7171
7585
  ]
7172
7586
  }
7173
7587
  );
7174
7588
  }
7175
7589
  if (control.kind === "textarea") {
7176
- return /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
7590
+ return /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
7177
7591
  "textarea",
7178
7592
  {
7179
7593
  id,
@@ -7187,8 +7601,8 @@ function Control({
7187
7601
  );
7188
7602
  }
7189
7603
  const numeric = typeof shown === "number" ? shown : void 0;
7190
- return /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { className: "flex items-center gap-2", children: [
7191
- /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
7604
+ return /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("div", { className: "flex items-center gap-2", children: [
7605
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
7192
7606
  "input",
7193
7607
  {
7194
7608
  id,
@@ -7202,7 +7616,7 @@ function Control({
7202
7616
  onChange: (e) => onValue((0, import_generation2.parseControlValue)(control, e.target.value))
7203
7617
  }
7204
7618
  ),
7205
- control.kind === "range" && /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("span", { className: "w-10 text-right tabular-nums text-muted-foreground", "data-field-value": "", children: numeric ?? "\u2013" })
7619
+ control.kind === "range" && /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("span", { className: "w-10 text-right tabular-nums text-muted-foreground", "data-field-value": "", children: numeric ?? "\u2013" })
7206
7620
  ] });
7207
7621
  }
7208
7622
  var GenerationSettings = ({
@@ -7216,17 +7630,17 @@ var GenerationSettings = ({
7216
7630
  renderReference,
7217
7631
  className = ""
7218
7632
  }) => {
7219
- const baseId = (0, import_react51.useId)();
7220
- const controls = (0, import_react51.useMemo)(() => (0, import_generation2.getGenerationControls)(capabilities), [capabilities]);
7633
+ const baseId = (0, import_react53.useId)();
7634
+ const controls = (0, import_react53.useMemo)(() => (0, import_generation2.getGenerationControls)(capabilities), [capabilities]);
7221
7635
  const computed = useGenerationPlan(value, capabilities, { strategy });
7222
7636
  const plan = planProp ?? computed;
7223
7637
  const slots = (0, import_generation2.getReferenceSlots)(capabilities, value);
7224
- return /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { className: `flex flex-col gap-3 text-sm ${className}`, "data-generation-settings": "", "data-model": capabilities.id, children: [
7638
+ return /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("div", { className: `flex flex-col gap-3 text-sm ${className}`, "data-generation-settings": "", "data-model": capabilities.id, children: [
7225
7639
  controls.map((control) => {
7226
7640
  const id = `${baseId}-${control.key}`;
7227
- return /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { className: "flex flex-col gap-1", "data-control": control.key, children: [
7228
- /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("label", { htmlFor: id, className: "text-xs font-medium text-muted-foreground", title: control.description, children: control.label }),
7229
- /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
7641
+ return /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("div", { className: "flex flex-col gap-1", "data-control": control.key, children: [
7642
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("label", { htmlFor: id, className: "text-xs font-medium text-muted-foreground", title: control.description, children: control.label }),
7643
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
7230
7644
  Control,
7231
7645
  {
7232
7646
  control,
@@ -7237,23 +7651,23 @@ var GenerationSettings = ({
7237
7651
  )
7238
7652
  ] }, control.key);
7239
7653
  }),
7240
- slots.map((slot) => /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("fieldset", { className: "flex flex-col gap-1", "data-reference-role": slot.role, children: [
7241
- /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("legend", { className: "text-xs font-medium text-muted-foreground", title: slot.description, children: [
7654
+ slots.map((slot) => /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("fieldset", { className: "flex flex-col gap-1", "data-reference-role": slot.role, children: [
7655
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("legend", { className: "text-xs font-medium text-muted-foreground", title: slot.description, children: [
7242
7656
  slot.label,
7243
7657
  " ",
7244
- /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("span", { className: "tabular-nums", "data-reference-count": "", children: [
7658
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("span", { className: "tabular-nums", "data-reference-count": "", children: [
7245
7659
  "(",
7246
7660
  slot.references.length,
7247
7661
  "/",
7248
7662
  slot.max,
7249
7663
  ")"
7250
7664
  ] }),
7251
- slot.min > 0 && /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("span", { className: "text-destructive", children: " *" })
7665
+ slot.min > 0 && /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("span", { className: "text-destructive", children: " *" })
7252
7666
  ] }),
7253
- /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("ul", { className: "flex flex-wrap gap-1", children: [
7254
- slot.references.map((ref) => /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("li", { className: "flex items-center gap-1 rounded border border-border px-1.5 py-0.5", "data-reference-id": ref.id, children: [
7255
- renderReference ? renderReference(ref) : /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("span", { className: "max-w-[10rem] truncate", children: ref.id }),
7256
- /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
7667
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("ul", { className: "flex flex-wrap gap-1", children: [
7668
+ slot.references.map((ref) => /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("li", { className: "flex items-center gap-1 rounded border border-border px-1.5 py-0.5", "data-reference-id": ref.id, children: [
7669
+ renderReference ? renderReference(ref) : /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("span", { className: "max-w-[10rem] truncate", children: ref.id }),
7670
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
7257
7671
  "button",
7258
7672
  {
7259
7673
  type: "button",
@@ -7264,7 +7678,7 @@ var GenerationSettings = ({
7264
7678
  }
7265
7679
  )
7266
7680
  ] }, ref.id)),
7267
- onAddReference && slot.canAdd && /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
7681
+ onAddReference && slot.canAdd && /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
7268
7682
  "button",
7269
7683
  {
7270
7684
  type: "button",
@@ -7275,21 +7689,21 @@ var GenerationSettings = ({
7275
7689
  ) })
7276
7690
  ] })
7277
7691
  ] }, slot.role)),
7278
- showAdjustments && plan.adjustments.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("ul", { role: "status", className: "flex flex-col gap-0.5 text-xs text-muted-foreground", "data-generation-adjustments": "", children: plan.adjustments.map((adjustment, index) => /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("li", { "data-adjustment-field": adjustment.field, "data-adjustment-kind": adjustment.kind, children: adjustment.reason }, index)) }),
7279
- showAdjustments && plan.errors.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("ul", { role: "alert", className: "flex flex-col gap-0.5 text-xs text-destructive", "data-generation-errors": "", children: plan.errors.map((error, index) => /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("li", { "data-error-field": error.field, children: error.message }, index)) })
7692
+ showAdjustments && plan.adjustments.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("ul", { role: "status", className: "flex flex-col gap-0.5 text-xs text-muted-foreground", "data-generation-adjustments": "", children: plan.adjustments.map((adjustment, index) => /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("li", { "data-adjustment-field": adjustment.field, "data-adjustment-kind": adjustment.kind, children: adjustment.reason }, index)) }),
7693
+ showAdjustments && plan.errors.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("ul", { role: "alert", className: "flex flex-col gap-0.5 text-xs text-destructive", "data-generation-errors": "", children: plan.errors.map((error, index) => /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("li", { "data-error-field": error.field, children: error.message }, index)) })
7280
7694
  ] });
7281
7695
  };
7282
7696
 
7283
7697
  // src/generation/JobProgress.tsx
7284
- var import_jsx_runtime33 = require("react/jsx-runtime");
7698
+ var import_jsx_runtime35 = require("react/jsx-runtime");
7285
7699
  var JobProgress = ({ title, className = "", ...options }) => {
7286
7700
  const progress = useJobProgress(options);
7287
- return /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("div", { className: `flex flex-col gap-1 text-sm ${className}`, "data-job-progress": "", "data-done": options.done ? "" : void 0, children: [
7288
- /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("div", { className: "flex items-center justify-between gap-2 text-muted-foreground", children: [
7289
- title && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("span", { className: "truncate text-foreground", children: title }),
7290
- /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("span", { "data-job-progress-label": "", "aria-live": "polite", children: progress.label })
7701
+ return /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("div", { className: `flex flex-col gap-1 text-sm ${className}`, "data-job-progress": "", "data-done": options.done ? "" : void 0, children: [
7702
+ /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("div", { className: "flex items-center justify-between gap-2 text-muted-foreground", children: [
7703
+ title && /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("span", { className: "truncate text-foreground", children: title }),
7704
+ /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("span", { "data-job-progress-label": "", "aria-live": "polite", children: progress.label })
7291
7705
  ] }),
7292
- /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
7706
+ /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
7293
7707
  "div",
7294
7708
  {
7295
7709
  role: "progressbar",
@@ -7299,7 +7713,7 @@ var JobProgress = ({ title, className = "", ...options }) => {
7299
7713
  "aria-valuenow": progress.percent,
7300
7714
  "aria-valuetext": `${progress.percent}%, ${progress.label}`,
7301
7715
  className: "h-1.5 w-full overflow-hidden rounded-full bg-muted",
7302
- children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
7716
+ children: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
7303
7717
  "div",
7304
7718
  {
7305
7719
  className: "h-full rounded-full bg-primary transition-[width] duration-500",
@@ -7312,9 +7726,9 @@ var JobProgress = ({ title, className = "", ...options }) => {
7312
7726
  };
7313
7727
 
7314
7728
  // src/generation/AssetMentionChips.tsx
7315
- var import_react52 = require("react");
7729
+ var import_react54 = require("react");
7316
7730
  var import_generation3 = require("@pixerate/editor/generation");
7317
- var import_jsx_runtime34 = require("react/jsx-runtime");
7731
+ var import_jsx_runtime36 = require("react/jsx-runtime");
7318
7732
  var AssetMentionChips = ({
7319
7733
  content,
7320
7734
  onChange,
@@ -7324,14 +7738,14 @@ var AssetMentionChips = ({
7324
7738
  renderAsset,
7325
7739
  className = ""
7326
7740
  }) => {
7327
- const [open, setOpen] = (0, import_react52.useState)(null);
7328
- const rootRef = (0, import_react52.useRef)(null);
7329
- const chips = (0, import_react52.useMemo)(
7741
+ const [open, setOpen] = (0, import_react54.useState)(null);
7742
+ const rootRef = (0, import_react54.useRef)(null);
7743
+ const chips = (0, import_react54.useMemo)(
7330
7744
  () => (0, import_generation3.describeAssetMentions)((0, import_generation3.findTextMentions)(content), assets, capabilities),
7331
7745
  [content, assets, capabilities]
7332
7746
  );
7333
7747
  const options = roles ?? (capabilities ? (0, import_generation3.getAssetRoleOptions)(capabilities) : (0, import_generation3.defaultAssetRoleOptions)());
7334
- (0, import_react52.useEffect)(() => {
7748
+ (0, import_react54.useEffect)(() => {
7335
7749
  if (open === null) return;
7336
7750
  const onPointer = (event) => {
7337
7751
  if (!rootRef.current?.contains(event.target)) setOpen(null);
@@ -7339,19 +7753,19 @@ var AssetMentionChips = ({
7339
7753
  document.addEventListener("mousedown", onPointer);
7340
7754
  return () => document.removeEventListener("mousedown", onPointer);
7341
7755
  }, [open]);
7342
- const pick = (chip2, role) => {
7756
+ const pick = (chip3, role) => {
7343
7757
  setOpen(null);
7344
- if (chip2.start === void 0) return;
7345
- onChange((0, import_generation3.setMentionRoleInText)(content, chip2.start, role));
7758
+ if (chip3.start === void 0) return;
7759
+ onChange((0, import_generation3.setMentionRoleInText)(content, chip3.start, role));
7346
7760
  };
7347
7761
  if (chips.length === 0) return null;
7348
- return /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("div", { ref: rootRef, className: `flex flex-wrap gap-1.5 ${className}`, "data-asset-mention-chips": "", children: chips.map((chip2) => {
7349
- const error = chip2.issues.find((issue) => issue.severity === "error");
7350
- const warning = chip2.issues.find((issue) => issue.severity === "warning");
7762
+ return /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("div", { ref: rootRef, className: `flex flex-wrap gap-1.5 ${className}`, "data-asset-mention-chips": "", children: chips.map((chip3) => {
7763
+ const error = chip3.issues.find((issue) => issue.severity === "error");
7764
+ const warning = chip3.issues.find((issue) => issue.severity === "warning");
7351
7765
  const problem = error ?? warning;
7352
- const isOpen = open === chip2.index;
7353
- return /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("span", { className: "relative inline-flex", "data-asset-chip": chip2.asset.id, children: [
7354
- /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(
7766
+ const isOpen = open === chip3.index;
7767
+ return /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("span", { className: "relative inline-flex", "data-asset-chip": chip3.asset.id, children: [
7768
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(
7355
7769
  "button",
7356
7770
  {
7357
7771
  type: "button",
@@ -7359,82 +7773,82 @@ var AssetMentionChips = ({
7359
7773
  "aria-expanded": isOpen,
7360
7774
  title: problem?.message,
7361
7775
  "data-issue": problem?.code,
7362
- onClick: () => setOpen(isOpen ? null : chip2.index),
7776
+ onClick: () => setOpen(isOpen ? null : chip3.index),
7363
7777
  onKeyDown: (e) => {
7364
7778
  if (e.key === "Escape") setOpen(null);
7365
7779
  },
7366
7780
  className: `inline-flex items-center gap-1 rounded-full border px-2 py-0.5 text-xs ${error ? "border-red-500/60" : warning ? "border-amber-500/60" : "border-border"} bg-muted/40 hover:bg-muted`,
7367
7781
  children: [
7368
- /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("span", { children: renderAsset ? renderAsset(chip2) : `@${chip2.asset.label ?? chip2.handle}` }),
7369
- /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
7782
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("span", { children: renderAsset ? renderAsset(chip3) : `@${chip3.asset.label ?? chip3.handle}` }),
7783
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
7370
7784
  "span",
7371
7785
  {
7372
- className: `rounded px-1 text-[10px] uppercase tracking-wide ${chip2.role ? "bg-primary/15" : "opacity-60"}`,
7373
- "data-role-badge": chip2.role ?? "",
7374
- children: chip2.roleLabel ?? "No role"
7786
+ className: `rounded px-1 text-[10px] uppercase tracking-wide ${chip3.role ? "bg-primary/15" : "opacity-60"}`,
7787
+ "data-role-badge": chip3.role ?? "",
7788
+ children: chip3.roleLabel ?? "No role"
7375
7789
  }
7376
7790
  )
7377
7791
  ]
7378
7792
  }
7379
7793
  ),
7380
- isOpen && /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(
7794
+ isOpen && /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(
7381
7795
  "div",
7382
7796
  {
7383
7797
  role: "listbox",
7384
- "aria-label": `Role for ${chip2.asset.label ?? chip2.handle}`,
7798
+ "aria-label": `Role for ${chip3.asset.label ?? chip3.handle}`,
7385
7799
  className: "absolute left-0 top-full z-50 mt-1 flex min-w-[12rem] flex-col rounded-md border border-border bg-background p-1 text-xs shadow-lg",
7386
7800
  "data-role-picker": "",
7387
7801
  onKeyDown: (e) => {
7388
7802
  if (e.key === "Escape") setOpen(null);
7389
7803
  },
7390
7804
  children: [
7391
- options.map((option) => /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(
7805
+ options.map((option) => /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(
7392
7806
  "button",
7393
7807
  {
7394
7808
  type: "button",
7395
7809
  role: "option",
7396
- "aria-selected": chip2.role === option.role,
7810
+ "aria-selected": chip3.role === option.role,
7397
7811
  "data-role-option": option.role,
7398
7812
  title: option.description,
7399
- onClick: () => pick(chip2, option.role),
7813
+ onClick: () => pick(chip3, option.role),
7400
7814
  className: "flex items-center justify-between gap-2 rounded px-2 py-1 text-left hover:bg-muted",
7401
7815
  children: [
7402
- /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("span", { children: option.label }),
7403
- option.accepts?.length ? /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("span", { className: "text-[10px] opacity-60", children: option.accepts.join(", ") }) : null
7816
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("span", { children: option.label }),
7817
+ option.accepts?.length ? /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("span", { className: "text-[10px] opacity-60", children: option.accepts.join(", ") }) : null
7404
7818
  ]
7405
7819
  },
7406
7820
  option.role
7407
7821
  )),
7408
- /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
7822
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
7409
7823
  "button",
7410
7824
  {
7411
7825
  type: "button",
7412
7826
  role: "option",
7413
- "aria-selected": !chip2.role,
7827
+ "aria-selected": !chip3.role,
7414
7828
  "data-role-option": "",
7415
- onClick: () => pick(chip2, null),
7829
+ onClick: () => pick(chip3, null),
7416
7830
  className: "rounded px-2 py-1 text-left opacity-70 hover:bg-muted",
7417
7831
  children: "No role"
7418
7832
  }
7419
7833
  ),
7420
- chip2.issues.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("ul", { className: "mt-1 border-t border-border px-2 pt-1 text-[10px]", "data-chip-issues": "", children: chip2.issues.map((issue, i) => /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("li", { className: issue.severity === "error" ? "text-red-500" : "text-amber-500", children: issue.message }, i)) })
7834
+ chip3.issues.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("ul", { className: "mt-1 border-t border-border px-2 pt-1 text-[10px]", "data-chip-issues": "", children: chip3.issues.map((issue, i) => /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("li", { className: issue.severity === "error" ? "text-red-500" : "text-amber-500", children: issue.message }, i)) })
7421
7835
  ]
7422
7836
  }
7423
7837
  )
7424
- ] }, `${chip2.start}-${chip2.index}`);
7838
+ ] }, `${chip3.start}-${chip3.index}`);
7425
7839
  }) });
7426
7840
  };
7427
7841
 
7428
7842
  // src/generation/GenerationHistory.tsx
7429
- var import_react53 = require("react");
7843
+ var import_react55 = require("react");
7430
7844
  var import_generation4 = require("@pixerate/editor/generation");
7431
- var import_jsx_runtime35 = require("react/jsx-runtime");
7845
+ var import_jsx_runtime37 = require("react/jsx-runtime");
7432
7846
  function useGenerationRecords(store, filter) {
7433
- const [records, setRecords] = (0, import_react53.useState)([]);
7434
- const [loading, setLoading] = (0, import_react53.useState)(!!store);
7435
- const [error, setError] = (0, import_react53.useState)(null);
7847
+ const [records, setRecords] = (0, import_react55.useState)([]);
7848
+ const [loading, setLoading] = (0, import_react55.useState)(!!store);
7849
+ const [error, setError] = (0, import_react55.useState)(null);
7436
7850
  const filterKey = (0, import_generation4.stableStringify)(filter ?? {});
7437
- const refresh = (0, import_react53.useCallback)(async () => {
7851
+ const refresh = (0, import_react55.useCallback)(async () => {
7438
7852
  if (!store) {
7439
7853
  setRecords([]);
7440
7854
  setLoading(false);
@@ -7451,7 +7865,7 @@ function useGenerationRecords(store, filter) {
7451
7865
  setLoading(false);
7452
7866
  }
7453
7867
  }, [store, filterKey]);
7454
- (0, import_react53.useEffect)(() => {
7868
+ (0, import_react55.useEffect)(() => {
7455
7869
  void refresh();
7456
7870
  if (!store) return;
7457
7871
  return store.subscribe(() => void refresh());
@@ -7477,21 +7891,21 @@ var GenerationHistory = ({
7477
7891
  emptyText = "No generations yet.",
7478
7892
  className = ""
7479
7893
  }) => {
7480
- const [innerMode, setInnerMode] = (0, import_react53.useState)("exact");
7894
+ const [innerMode, setInnerMode] = (0, import_react55.useState)("exact");
7481
7895
  const mode = modeProp ?? innerMode;
7482
7896
  const setMode = (next) => {
7483
7897
  if (modeProp === void 0) setInnerMode(next);
7484
7898
  onModeChange?.(next);
7485
7899
  };
7486
- const options = (0, import_react53.useMemo)(() => ({ templates, capabilities, assets, mode }), [templates, capabilities, assets, mode]);
7487
- const results = (0, import_react53.useMemo)(() => records.map((record) => (0, import_generation4.recreateRequest)(record, options)), [records, options]);
7488
- const models = (0, import_react53.useMemo)(
7900
+ const options = (0, import_react55.useMemo)(() => ({ templates, capabilities, assets, mode }), [templates, capabilities, assets, mode]);
7901
+ const results = (0, import_react55.useMemo)(() => records.map((record) => (0, import_generation4.recreateRequest)(record, options)), [records, options]);
7902
+ const models = (0, import_react55.useMemo)(
7489
7903
  () => capabilities ? Array.isArray(capabilities) ? capabilities : [capabilities] : [],
7490
7904
  [capabilities]
7491
7905
  );
7492
- return /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("div", { className: `flex flex-col gap-2 text-sm ${className}`, "data-generation-history": "", children: [
7493
- /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("div", { className: "flex items-center gap-2 text-xs", role: "radiogroup", "aria-label": "Recreate mode", children: ["exact", "latest"].map((value) => /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("label", { className: "inline-flex items-center gap-1", children: [
7494
- /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
7906
+ return /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("div", { className: `flex flex-col gap-2 text-sm ${className}`, "data-generation-history": "", children: [
7907
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("div", { className: "flex items-center gap-2 text-xs", role: "radiogroup", "aria-label": "Recreate mode", children: ["exact", "latest"].map((value) => /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("label", { className: "inline-flex items-center gap-1", children: [
7908
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
7495
7909
  "input",
7496
7910
  {
7497
7911
  type: "radio",
@@ -7504,34 +7918,34 @@ var GenerationHistory = ({
7504
7918
  ),
7505
7919
  value === "exact" ? "Exact (recorded versions)" : "Latest templates & settings"
7506
7920
  ] }, value)) }),
7507
- records.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("p", { className: "text-xs text-muted-foreground", children: emptyText }),
7508
- /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("ul", { className: "flex flex-col gap-2", children: records.map((record, i) => {
7921
+ records.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("p", { className: "text-xs text-muted-foreground", children: emptyText }),
7922
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("ul", { className: "flex flex-col gap-2", children: records.map((record, i) => {
7509
7923
  const result = results[i];
7510
7924
  const model = models.find((m) => m.id === record.provenance.modelId);
7511
7925
  const blocked = !result.ok;
7512
- return /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)(
7926
+ return /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(
7513
7927
  "li",
7514
7928
  {
7515
7929
  className: "rounded-md border border-border p-2",
7516
7930
  "data-record": record.id,
7517
7931
  "data-drift": result.drift.length,
7518
7932
  children: [
7519
- /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("div", { className: "flex flex-wrap items-center justify-between gap-2 text-xs", children: [
7520
- /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("span", { className: "font-medium", children: model?.name ?? record.provenance.modelId }),
7521
- /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("span", { className: "text-muted-foreground", children: [
7522
- record.kind !== "generate" && /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("span", { "data-kind": "", children: [
7933
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("div", { className: "flex flex-wrap items-center justify-between gap-2 text-xs", children: [
7934
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("span", { className: "font-medium", children: model?.name ?? record.provenance.modelId }),
7935
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("span", { className: "text-muted-foreground", children: [
7936
+ record.kind !== "generate" && /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("span", { "data-kind": "", children: [
7523
7937
  record.kind,
7524
7938
  " \xB7 "
7525
7939
  ] }),
7526
- /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("time", { dateTime: record.createdAt, children: record.createdAt.replace("T", " ").slice(0, 16) }),
7527
- record.seed !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)(import_jsx_runtime35.Fragment, { children: [
7940
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("time", { dateTime: record.createdAt, children: record.createdAt.replace("T", " ").slice(0, 16) }),
7941
+ record.seed !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(import_jsx_runtime37.Fragment, { children: [
7528
7942
  " \xB7 seed ",
7529
7943
  record.seed
7530
7944
  ] })
7531
7945
  ] })
7532
7946
  ] }),
7533
- /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("p", { className: "mt-1 line-clamp-3 break-words text-xs", "data-record-prompt": "", children: record.provenance.resolvedPrompt }),
7534
- record.assets.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("div", { className: "mt-1 flex flex-wrap gap-1", children: record.assets.map((asset) => /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)(
7947
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("p", { className: "mt-1 line-clamp-3 break-words text-xs", "data-record-prompt": "", children: record.provenance.resolvedPrompt }),
7948
+ record.assets.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("div", { className: "mt-1 flex flex-wrap gap-1", children: record.assets.map((asset) => /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(
7535
7949
  "span",
7536
7950
  {
7537
7951
  className: "rounded-full bg-muted px-2 py-0.5 text-[10px]",
@@ -7545,16 +7959,16 @@ var GenerationHistory = ({
7545
7959
  `${asset.role}:${asset.id}`
7546
7960
  )) }),
7547
7961
  renderPreview?.(record),
7548
- result.drift.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("ul", { className: "mt-1 flex flex-col gap-0.5 text-[11px]", "data-drift-report": "", children: result.drift.map((item, j) => /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("li", { className: severityClass[item.severity], "data-drift-type": item.type, children: [
7549
- /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("strong", { children: [
7962
+ result.drift.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("ul", { className: "mt-1 flex flex-col gap-0.5 text-[11px]", "data-drift-report": "", children: result.drift.map((item, j) => /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("li", { className: severityClass[item.severity], "data-drift-type": item.type, children: [
7963
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("strong", { children: [
7550
7964
  import_generation4.DRIFT_TYPE_LABELS[item.type],
7551
7965
  ":"
7552
7966
  ] }),
7553
7967
  " ",
7554
7968
  item.message
7555
7969
  ] }, j)) }),
7556
- /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("div", { className: "mt-2 flex gap-2", children: [
7557
- onRecreate && /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
7970
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("div", { className: "mt-2 flex gap-2", children: [
7971
+ onRecreate && /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
7558
7972
  "button",
7559
7973
  {
7560
7974
  type: "button",
@@ -7566,7 +7980,7 @@ var GenerationHistory = ({
7566
7980
  children: "Recreate"
7567
7981
  }
7568
7982
  ),
7569
- onVariation && /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
7983
+ onVariation && /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
7570
7984
  "button",
7571
7985
  {
7572
7986
  type: "button",
@@ -7576,7 +7990,7 @@ var GenerationHistory = ({
7576
7990
  children: "Variation"
7577
7991
  }
7578
7992
  ),
7579
- onRemove && /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
7993
+ onRemove && /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
7580
7994
  "button",
7581
7995
  {
7582
7996
  type: "button",
@@ -7596,19 +8010,19 @@ var GenerationHistory = ({
7596
8010
  };
7597
8011
 
7598
8012
  // src/find-replace/useFindReplace.ts
7599
- var import_react54 = require("react");
8013
+ var import_react56 = require("react");
7600
8014
  var import_editor16 = require("@pixerate/editor");
7601
8015
  var noopUnsubscribe2 = () => {
7602
8016
  };
7603
8017
  function useFindReplace(editor) {
7604
8018
  const store = (0, import_editor16.getFindReplaceStore)(editor ?? null);
7605
- const subscribe = (0, import_react54.useCallback)(
8019
+ const subscribe = (0, import_react56.useCallback)(
7606
8020
  (onChange) => store ? store.subscribe(() => onChange()) : noopUnsubscribe2,
7607
8021
  [store]
7608
8022
  );
7609
8023
  const getSnapshot = () => store?.getSnapshot() ?? import_editor16.EMPTY_FIND_REPLACE_SNAPSHOT;
7610
- const snapshot = (0, import_react54.useSyncExternalStore)(subscribe, getSnapshot, getSnapshot);
7611
- return (0, import_react54.useMemo)(() => {
8024
+ const snapshot = (0, import_react56.useSyncExternalStore)(subscribe, getSnapshot, getSnapshot);
8025
+ return (0, import_react56.useMemo)(() => {
7612
8026
  const run = (fn) => {
7613
8027
  if (editor && !editor.isDestroyed && store) fn(editor);
7614
8028
  };
@@ -7632,9 +8046,9 @@ function useFindReplace(editor) {
7632
8046
  }
7633
8047
 
7634
8048
  // src/find-replace/FindReplaceBar.tsx
7635
- var import_react55 = require("react");
8049
+ var import_react57 = require("react");
7636
8050
  var import_editor17 = require("@pixerate/editor");
7637
- var import_jsx_runtime36 = require("react/jsx-runtime");
8051
+ var import_jsx_runtime38 = require("react/jsx-runtime");
7638
8052
  var inputClass3 = "min-w-0 flex-1 rounded border border-input bg-background px-2 py-1 text-xs text-foreground focus:outline-none focus:ring-1 focus:ring-ring";
7639
8053
  var buttonClass5 = "rounded px-2 py-1 text-xs text-foreground hover:bg-muted disabled:opacity-40 disabled:hover:bg-transparent";
7640
8054
  var toggleClass = (on) => `${buttonClass5} font-mono ${on ? "bg-primary/10 text-primary" : "text-muted-foreground"}`;
@@ -7647,10 +8061,10 @@ var FindReplaceBar = ({
7647
8061
  onClose
7648
8062
  }) => {
7649
8063
  const find = useFindReplace(editor);
7650
- const findInput = (0, import_react55.useRef)(null);
7651
- const [moreOpen, setMoreOpen] = (0, import_react55.useState)(false);
8064
+ const findInput = (0, import_react57.useRef)(null);
8065
+ const [moreOpen, setMoreOpen] = (0, import_react57.useState)(false);
7652
8066
  const visible = find.available && (alwaysOpen || find.open);
7653
- (0, import_react55.useEffect)(() => {
8067
+ (0, import_react57.useEffect)(() => {
7654
8068
  if (!visible || find.focusRequest === 0) return;
7655
8069
  findInput.current?.focus();
7656
8070
  findInput.current?.select();
@@ -7688,9 +8102,9 @@ var FindReplaceBar = ({
7688
8102
  if (isMod(e)) find.replaceAll();
7689
8103
  else find.replace();
7690
8104
  };
7691
- return /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("div", { className, role: "search", "aria-label": "Find and replace", onKeyDown: onBarKeyDown, children: [
7692
- /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("div", { className: "flex items-center gap-1", children: [
7693
- /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
8105
+ return /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("div", { className, role: "search", "aria-label": "Find and replace", onKeyDown: onBarKeyDown, children: [
8106
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("div", { className: "flex items-center gap-1", children: [
8107
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
7694
8108
  "button",
7695
8109
  {
7696
8110
  type: "button",
@@ -7701,7 +8115,7 @@ var FindReplaceBar = ({
7701
8115
  children: showReplace ? "\u25BE" : "\u25B8"
7702
8116
  }
7703
8117
  ),
7704
- /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
8118
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
7705
8119
  "input",
7706
8120
  {
7707
8121
  ref: findInput,
@@ -7715,7 +8129,7 @@ var FindReplaceBar = ({
7715
8129
  onKeyDown: onFindKeyDown
7716
8130
  }
7717
8131
  ),
7718
- import_editor17.FIND_OPTION_TOGGLES.filter((t) => !t.advanced).map((t) => /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
8132
+ import_editor17.FIND_OPTION_TOGGLES.filter((t) => !t.advanced).map((t) => /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
7719
8133
  "button",
7720
8134
  {
7721
8135
  type: "button",
@@ -7728,7 +8142,7 @@ var FindReplaceBar = ({
7728
8142
  },
7729
8143
  t.key
7730
8144
  )),
7731
- /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
8145
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
7732
8146
  "span",
7733
8147
  {
7734
8148
  className: `min-w-[5rem] px-1 text-center text-xs ${find.error ? "text-destructive" : "text-muted-foreground"}`,
@@ -7738,9 +8152,9 @@ var FindReplaceBar = ({
7738
8152
  children: status
7739
8153
  }
7740
8154
  ),
7741
- /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("button", { type: "button", className: buttonClass5, "aria-label": "Previous match", disabled: !hasMatches, onClick: find.previous, children: "\u2191" }),
7742
- /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("button", { type: "button", className: buttonClass5, "aria-label": "Next match", disabled: !hasMatches, onClick: find.next, children: "\u2193" }),
7743
- showMoreOptions && /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
8155
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("button", { type: "button", className: buttonClass5, "aria-label": "Previous match", disabled: !hasMatches, onClick: find.previous, children: "\u2191" }),
8156
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("button", { type: "button", className: buttonClass5, "aria-label": "Next match", disabled: !hasMatches, onClick: find.next, children: "\u2193" }),
8157
+ showMoreOptions && /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
7744
8158
  "button",
7745
8159
  {
7746
8160
  type: "button",
@@ -7751,10 +8165,10 @@ var FindReplaceBar = ({
7751
8165
  children: "\u2026"
7752
8166
  }
7753
8167
  ),
7754
- /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("button", { type: "button", className: buttonClass5, "aria-label": "Close find", onClick: close, children: "\xD7" })
8168
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("button", { type: "button", className: buttonClass5, "aria-label": "Close find", onClick: close, children: "\xD7" })
7755
8169
  ] }),
7756
- showReplace && /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("div", { className: "flex items-center gap-1 pl-7", children: [
7757
- /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
8170
+ showReplace && /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("div", { className: "flex items-center gap-1 pl-7", children: [
8171
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
7758
8172
  "input",
7759
8173
  {
7760
8174
  type: "text",
@@ -7766,7 +8180,7 @@ var FindReplaceBar = ({
7766
8180
  onKeyDown: onReplaceKeyDown
7767
8181
  }
7768
8182
  ),
7769
- /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
8183
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
7770
8184
  "button",
7771
8185
  {
7772
8186
  type: "button",
@@ -7776,11 +8190,11 @@ var FindReplaceBar = ({
7776
8190
  children: "Replace"
7777
8191
  }
7778
8192
  ),
7779
- /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("button", { type: "button", className: buttonClass5, disabled: !hasMatches, onClick: find.replaceAll, children: "Replace all" }),
7780
- find.lastReplaceCount !== null && /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("span", { className: "px-1 text-xs text-muted-foreground", "aria-live": "polite", children: find.lastReplaceCount === 1 ? "1 replaced" : `${find.lastReplaceCount} replaced` })
8193
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("button", { type: "button", className: buttonClass5, disabled: !hasMatches, onClick: find.replaceAll, children: "Replace all" }),
8194
+ find.lastReplaceCount !== null && /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("span", { className: "px-1 text-xs text-muted-foreground", "aria-live": "polite", children: find.lastReplaceCount === 1 ? "1 replaced" : `${find.lastReplaceCount} replaced` })
7781
8195
  ] }),
7782
- showMoreOptions && moreOpen && /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("div", { className: "flex flex-wrap items-center gap-x-3 gap-y-1 pl-7 text-xs text-muted-foreground", children: import_editor17.FIND_OPTION_TOGGLES.filter((t) => t.advanced).map((t) => /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("label", { className: "flex items-center gap-1", children: [
7783
- /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
8196
+ showMoreOptions && moreOpen && /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("div", { className: "flex flex-wrap items-center gap-x-3 gap-y-1 pl-7 text-xs text-muted-foreground", children: import_editor17.FIND_OPTION_TOGGLES.filter((t) => t.advanced).map((t) => /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("label", { className: "flex items-center gap-1", children: [
8197
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
7784
8198
  "input",
7785
8199
  {
7786
8200
  type: "checkbox",
@@ -7794,12 +8208,12 @@ var FindReplaceBar = ({
7794
8208
  };
7795
8209
 
7796
8210
  // src/tables/useTableMenu.ts
7797
- var import_react56 = require("react");
8211
+ var import_react58 = require("react");
7798
8212
  var import_editor18 = require("@pixerate/editor");
7799
8213
  function useTableMenu(editor) {
7800
- const store = (0, import_react56.useMemo)(() => (0, import_editor18.createTableStore)(editor), [editor]);
7801
- const snapshot = (0, import_react56.useSyncExternalStore)(store.subscribe, store.getSnapshot, store.getSnapshot);
7802
- return (0, import_react56.useMemo)(
8214
+ const store = (0, import_react58.useMemo)(() => (0, import_editor18.createTableStore)(editor), [editor]);
8215
+ const snapshot = (0, import_react58.useSyncExternalStore)(store.subscribe, store.getSnapshot, store.getSnapshot);
8216
+ return (0, import_react58.useMemo)(
7803
8217
  () => ({
7804
8218
  ...snapshot,
7805
8219
  run: (action, params) => (0, import_editor18.runTableAction)(editor, action, params, { focus: true }),
@@ -7810,9 +8224,9 @@ function useTableMenu(editor) {
7810
8224
  }
7811
8225
 
7812
8226
  // src/tables/TableToolbar.tsx
7813
- var import_react57 = __toESM(require("react"), 1);
8227
+ var import_react59 = __toESM(require("react"), 1);
7814
8228
  var import_editor19 = require("@pixerate/editor");
7815
- var import_jsx_runtime37 = require("react/jsx-runtime");
8229
+ var import_jsx_runtime39 = require("react/jsx-runtime");
7816
8230
  var keepSelection2 = (event) => event.preventDefault();
7817
8231
  var TableToolbar = ({
7818
8232
  editor,
@@ -7826,14 +8240,14 @@ var TableToolbar = ({
7826
8240
  }) => {
7827
8241
  const table = useTableMenu(editor);
7828
8242
  if (!editor || !table.inTable && !showWhenOutside) return null;
7829
- return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("div", { className, role: "toolbar", "aria-label": "Table", "data-table-toolbar": "", children: items.map((item, index) => {
8243
+ return /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("div", { className, role: "toolbar", "aria-label": "Table", "data-table-toolbar": "", children: items.map((item, index) => {
7830
8244
  const status = table.check(item.action, item.params);
7831
8245
  const enabled = status === true;
7832
8246
  const active = item.active?.(table);
7833
8247
  const separator = index > 0 && items[index - 1].group !== item.group;
7834
- return /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(import_react57.default.Fragment, { children: [
7835
- separator && /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("span", { className: separatorClassName, "aria-hidden": "true" }),
7836
- /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
8248
+ return /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)(import_react59.default.Fragment, { children: [
8249
+ separator && /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("span", { className: separatorClassName, "aria-hidden": "true" }),
8250
+ /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
7837
8251
  "button",
7838
8252
  {
7839
8253
  type: "button",
@@ -7855,8 +8269,8 @@ var TableToolbar = ({
7855
8269
  };
7856
8270
 
7857
8271
  // src/tables/InsertTableGrid.tsx
7858
- var import_react58 = require("react");
7859
- var import_jsx_runtime38 = require("react/jsx-runtime");
8272
+ var import_react60 = require("react");
8273
+ var import_jsx_runtime40 = require("react/jsx-runtime");
7860
8274
  var keepSelection3 = (event) => event.preventDefault();
7861
8275
  var InsertTableGrid = ({
7862
8276
  editor,
@@ -7869,14 +8283,14 @@ var InsertTableGrid = ({
7869
8283
  onInsert
7870
8284
  }) => {
7871
8285
  const table = useTableMenu(editor);
7872
- const [hover, setHover] = (0, import_react58.useState)(null);
8286
+ const [hover, setHover] = (0, import_react60.useState)(null);
7873
8287
  const status = table.actions.insertTable;
7874
8288
  const disabled = !editor || !status.enabled;
7875
8289
  const insert = (size) => {
7876
8290
  if (table.run("insertTable", { ...size, withHeaderRow })) onInsert?.(size);
7877
8291
  setHover(null);
7878
8292
  };
7879
- return /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(
8293
+ return /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(
7880
8294
  "div",
7881
8295
  {
7882
8296
  className,
@@ -7886,7 +8300,7 @@ var InsertTableGrid = ({
7886
8300
  "data-insert-table-grid": "",
7887
8301
  onMouseLeave: () => setHover(null),
7888
8302
  children: [
7889
- /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
8303
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
7890
8304
  "div",
7891
8305
  {
7892
8306
  role: "grid",
@@ -7895,7 +8309,7 @@ var InsertTableGrid = ({
7895
8309
  children: Array.from({ length: rows * cols }, (_, i) => {
7896
8310
  const size = { rows: Math.floor(i / cols) + 1, cols: i % cols + 1 };
7897
8311
  const active = !!hover && size.rows <= hover.rows && size.cols <= hover.cols;
7898
- return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
8312
+ return /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
7899
8313
  "button",
7900
8314
  {
7901
8315
  type: "button",
@@ -7915,7 +8329,7 @@ var InsertTableGrid = ({
7915
8329
  })
7916
8330
  }
7917
8331
  ),
7918
- /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("div", { "aria-live": "polite", "data-testid": "insert-table-size", children: disabled ? status.reason : hover ? `${hover.rows} \xD7 ${hover.cols}` : "Insert table" })
8332
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("div", { "aria-live": "polite", "data-testid": "insert-table-size", children: disabled ? status.reason : hover ? `${hover.rows} \xD7 ${hover.cols}` : "Insert table" })
7919
8333
  ]
7920
8334
  }
7921
8335
  );
@@ -7923,31 +8337,31 @@ var InsertTableGrid = ({
7923
8337
 
7924
8338
  // src/tables/TableBubbleMenu.tsx
7925
8339
  var import_editor20 = require("@pixerate/editor");
7926
- var import_jsx_runtime39 = require("react/jsx-runtime");
8340
+ var import_jsx_runtime41 = require("react/jsx-runtime");
7927
8341
  var TableBubbleMenu = ({
7928
8342
  editor,
7929
8343
  children,
7930
8344
  toolbarProps,
7931
8345
  pluginKey = "tableBubbleMenu",
7932
8346
  ...rest
7933
- }) => /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(BubbleMenu, { editor, pluginKey, shouldShow: import_editor20.shouldShowTableBubbleMenu, ...rest, children: children ?? /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(TableToolbar, { editor, ...toolbarProps }) });
8347
+ }) => /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(BubbleMenu, { editor, pluginKey, shouldShow: import_editor20.shouldShowTableBubbleMenu, ...rest, children: children ?? /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(TableToolbar, { editor, ...toolbarProps }) });
7934
8348
 
7935
8349
  // src/chart-node/ChartNodeView.tsx
7936
8350
  var import_client = require("react-dom/client");
7937
8351
 
7938
8352
  // src/chart/ChartCanvas.tsx
7939
- var import_react60 = require("react");
8353
+ var import_react62 = require("react");
7940
8354
  var import_chart2 = require("@pixerate/editor/chart");
7941
8355
 
7942
8356
  // src/chart/useChartEditor.ts
7943
- var import_react59 = require("react");
8357
+ var import_react61 = require("react");
7944
8358
  var import_chart = require("@pixerate/editor/chart");
7945
8359
  function useChartState(controller) {
7946
- return (0, import_react59.useSyncExternalStore)(controller.subscribe, controller.getState, controller.getState);
8360
+ return (0, import_react61.useSyncExternalStore)(controller.subscribe, controller.getState, controller.getState);
7947
8361
  }
7948
8362
 
7949
8363
  // src/chart/ChartCanvas.tsx
7950
- var import_jsx_runtime40 = require("react/jsx-runtime");
8364
+ var import_jsx_runtime42 = require("react/jsx-runtime");
7951
8365
  function renderChartMark(mark, fontFamily, interactive) {
7952
8366
  const common = {
7953
8367
  "data-role": mark.role,
@@ -7959,9 +8373,9 @@ function renderChartMark(mark, fontFamily, interactive) {
7959
8373
  };
7960
8374
  switch (mark.kind) {
7961
8375
  case "rect":
7962
- return /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("rect", { ...common, x: mark.x, y: mark.y, width: mark.width, height: mark.height, rx: mark.radius, style: { fill: mark.fill, stroke: mark.stroke, strokeWidth: mark.strokeWidth } }, mark.key);
8376
+ return /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("rect", { ...common, x: mark.x, y: mark.y, width: mark.width, height: mark.height, rx: mark.radius, style: { fill: mark.fill, stroke: mark.stroke, strokeWidth: mark.strokeWidth } }, mark.key);
7963
8377
  case "path":
7964
- return /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
8378
+ return /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
7965
8379
  "path",
7966
8380
  {
7967
8381
  ...common,
@@ -7974,11 +8388,11 @@ function renderChartMark(mark, fontFamily, interactive) {
7974
8388
  mark.key
7975
8389
  );
7976
8390
  case "circle":
7977
- return /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("circle", { ...common, cx: mark.cx, cy: mark.cy, r: mark.r, style: { fill: mark.fill, stroke: mark.stroke, strokeWidth: mark.strokeWidth } }, mark.key);
8391
+ return /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("circle", { ...common, cx: mark.cx, cy: mark.cy, r: mark.r, style: { fill: mark.fill, stroke: mark.stroke, strokeWidth: mark.strokeWidth } }, mark.key);
7978
8392
  case "line":
7979
- return /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("line", { ...common, x1: mark.x1, y1: mark.y1, x2: mark.x2, y2: mark.y2, strokeDasharray: mark.dash, style: { stroke: mark.stroke, strokeWidth: mark.strokeWidth } }, mark.key);
8393
+ return /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("line", { ...common, x1: mark.x1, y1: mark.y1, x2: mark.x2, y2: mark.y2, strokeDasharray: mark.dash, style: { stroke: mark.stroke, strokeWidth: mark.strokeWidth } }, mark.key);
7980
8394
  case "text":
7981
- return /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
8395
+ return /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
7982
8396
  "text",
7983
8397
  {
7984
8398
  ...common,
@@ -7995,23 +8409,23 @@ function renderChartMark(mark, fontFamily, interactive) {
7995
8409
  }
7996
8410
  }
7997
8411
  function ChartPresenceLayer({ presence }) {
7998
- const { actors } = (0, import_react60.useSyncExternalStore)(presence.subscribe, presence.getState, presence.getState);
7999
- return /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("g", { "data-chart-presence": "", pointerEvents: "none", children: actors.map((a) => /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("g", { transform: `translate(${a.x} ${a.y})`, "data-actor": a.id, children: [
8000
- /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("path", { d: "M0,0 L0,14 L4,10.5 L7,17 L9.5,16 L6.5,9.5 L11.5,9.5 Z", style: { fill: a.color, stroke: "#ffffff", strokeWidth: 1 }, transform: a.pressed ? "scale(0.9)" : void 0 }),
8001
- /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("g", { transform: "translate(13 16)", children: [
8002
- /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("rect", { x: 0, y: 0, rx: 4, height: 18, width: Math.max(28, (a.message ?? a.name).length * 6.4 + 12), style: { fill: a.color } }),
8003
- /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("text", { x: 6, y: 9, dominantBaseline: "central", style: { fill: "#ffffff", fontSize: 11, fontWeight: 600, fontFamily: "inherit" }, children: a.message ?? a.name })
8412
+ const { actors } = (0, import_react62.useSyncExternalStore)(presence.subscribe, presence.getState, presence.getState);
8413
+ return /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("g", { "data-chart-presence": "", pointerEvents: "none", children: actors.map((a) => /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("g", { transform: `translate(${a.x} ${a.y})`, "data-actor": a.id, children: [
8414
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("path", { d: "M0,0 L0,14 L4,10.5 L7,17 L9.5,16 L6.5,9.5 L11.5,9.5 Z", style: { fill: a.color, stroke: "#ffffff", strokeWidth: 1 }, transform: a.pressed ? "scale(0.9)" : void 0 }),
8415
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("g", { transform: "translate(13 16)", children: [
8416
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("rect", { x: 0, y: 0, rx: 4, height: 18, width: Math.max(28, (a.message ?? a.name).length * 6.4 + 12), style: { fill: a.color } }),
8417
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("text", { x: 6, y: 9, dominantBaseline: "central", style: { fill: "#ffffff", fontSize: 11, fontWeight: 600, fontFamily: "inherit" }, children: a.message ?? a.name })
8004
8418
  ] })
8005
8419
  ] }, a.id)) });
8006
8420
  }
8007
8421
  function ChartCanvas({ controller, width = 640, height = 400, responsive = true, theme, editable = true, measureText, emptyMessage, className, presence, sceneRef }) {
8008
8422
  const state = useChartState(controller);
8009
- const wrapper = (0, import_react60.useRef)(null);
8010
- const svg = (0, import_react60.useRef)(null);
8011
- const drag = (0, import_react60.useRef)(null);
8012
- const [measured, setMeasured] = (0, import_react60.useState)(null);
8013
- const [frozen, setFrozen] = (0, import_react60.useState)(null);
8014
- (0, import_react60.useEffect)(() => {
8423
+ const wrapper = (0, import_react62.useRef)(null);
8424
+ const svg = (0, import_react62.useRef)(null);
8425
+ const drag = (0, import_react62.useRef)(null);
8426
+ const [measured, setMeasured] = (0, import_react62.useState)(null);
8427
+ const [frozen, setFrozen] = (0, import_react62.useState)(null);
8428
+ (0, import_react62.useEffect)(() => {
8015
8429
  const el = wrapper.current;
8016
8430
  if (!responsive || !el || typeof ResizeObserver === "undefined") return;
8017
8431
  const observer = new ResizeObserver(([entry]) => {
@@ -8022,8 +8436,8 @@ function ChartCanvas({ controller, width = 640, height = 400, responsive = true,
8022
8436
  return () => observer.disconnect();
8023
8437
  }, [responsive]);
8024
8438
  const sceneWidth = responsive && measured || width;
8025
- const fullTheme = (0, import_react60.useMemo)(() => ({ ...import_chart2.CSS_VARIABLE_CHART_THEME, ...theme }), [theme]);
8026
- const scene = (0, import_react60.useMemo)(
8439
+ const fullTheme = (0, import_react62.useMemo)(() => ({ ...import_chart2.CSS_VARIABLE_CHART_THEME, ...theme }), [theme]);
8440
+ const scene = (0, import_react62.useMemo)(
8027
8441
  () => (0, import_chart2.layoutChart)(state.spec, {
8028
8442
  width: sceneWidth,
8029
8443
  height,
@@ -8039,8 +8453,8 @@ function ChartCanvas({ controller, width = 640, height = 400, responsive = true,
8039
8453
  [state.spec, state.selection, sceneWidth, height, fullTheme, editable, measureText, emptyMessage, frozen]
8040
8454
  );
8041
8455
  if (sceneRef) sceneRef.current = scene;
8042
- const byKey = (0, import_react60.useMemo)(() => new Map(scene.marks.map((m) => [m.key, m])), [scene]);
8043
- (0, import_react60.useEffect)(() => () => drag.current?.end(), []);
8456
+ const byKey = (0, import_react62.useMemo)(() => new Map(scene.marks.map((m) => [m.key, m])), [scene]);
8457
+ (0, import_react62.useEffect)(() => () => drag.current?.end(), []);
8044
8458
  const scenePoint = (e) => (0, import_chart2.chartClientToScene)(scene, svg.current.getBoundingClientRect(), e.clientX, e.clientY);
8045
8459
  const onPointerDown = (e) => {
8046
8460
  if (!editable || e.button !== 0) return;
@@ -8073,7 +8487,7 @@ function ChartCanvas({ controller, width = 640, height = 400, responsive = true,
8073
8487
  if (editable && (0, import_chart2.handleChartKey)(controller, scene, e)) e.preventDefault();
8074
8488
  };
8075
8489
  const label = state.spec.title || "Chart";
8076
- return /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("div", { ref: wrapper, className: `w-full ${className ?? ""}`, "data-chart-canvas": "", children: /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(
8490
+ return /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("div", { ref: wrapper, className: `w-full ${className ?? ""}`, "data-chart-canvas": "", children: /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(
8077
8491
  "svg",
8078
8492
  {
8079
8493
  ref: svg,
@@ -8091,14 +8505,14 @@ function ChartCanvas({ controller, width = 640, height = 400, responsive = true,
8091
8505
  onKeyDown,
8092
8506
  children: [
8093
8507
  scene.marks.map((m) => renderChartMark(m, scene.theme.fontFamily, editable)),
8094
- presence && /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(ChartPresenceLayer, { presence })
8508
+ presence && /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(ChartPresenceLayer, { presence })
8095
8509
  ]
8096
8510
  }
8097
8511
  ) });
8098
8512
  }
8099
8513
 
8100
8514
  // src/chart/ChartDataGrid.tsx
8101
- var import_react61 = require("react");
8515
+ var import_react63 = require("react");
8102
8516
  var import_chart3 = require("@pixerate/editor/chart");
8103
8517
 
8104
8518
  // src/chart/styles.ts
@@ -8111,11 +8525,11 @@ var chartSectionClass = "space-y-2 border-b border-border pb-3 last:border-b-0";
8111
8525
  var chartCellClass = "h-7 w-full min-w-[4.5rem] bg-transparent px-2 text-right text-xs tabular-nums text-foreground focus-visible:bg-background focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-inset focus-visible:ring-primary";
8112
8526
 
8113
8527
  // src/chart/ChartDataGrid.tsx
8114
- var import_jsx_runtime41 = require("react/jsx-runtime");
8528
+ var import_jsx_runtime43 = require("react/jsx-runtime");
8115
8529
  function ChartCommitInput({ value, onCommit, onKeyDown, onBlur, ...rest }) {
8116
- const [draft, setDraft] = (0, import_react61.useState)(value);
8117
- const typing = (0, import_react61.useRef)(false);
8118
- (0, import_react61.useEffect)(() => {
8530
+ const [draft, setDraft] = (0, import_react63.useState)(value);
8531
+ const typing = (0, import_react63.useRef)(false);
8532
+ (0, import_react63.useEffect)(() => {
8119
8533
  if (!typing.current) setDraft(value);
8120
8534
  }, [value]);
8121
8535
  const commit = () => {
@@ -8124,7 +8538,7 @@ function ChartCommitInput({ value, onCommit, onKeyDown, onBlur, ...rest }) {
8124
8538
  if (draft === value) return;
8125
8539
  if (onCommit(draft) === false) setDraft(value);
8126
8540
  };
8127
- return /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
8541
+ return /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
8128
8542
  "input",
8129
8543
  {
8130
8544
  ...rest,
@@ -8159,15 +8573,15 @@ function ChartDataGrid({ controller, categoryHeader = "Category", readOnly = fal
8159
8573
  e.preventDefault();
8160
8574
  controller.pasteCells(row, column, text);
8161
8575
  };
8162
- return /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { className: `overflow-auto rounded-md border border-border ${className ?? ""}`, "data-chart-grid": "", children: [
8163
- /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("table", { className: "w-full border-collapse text-xs", children: [
8164
- /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("thead", { className: "bg-muted/50", children: /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("tr", { children: [
8165
- /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("th", { className: "border-b border-r border-border px-2 py-1 text-left font-medium text-muted-foreground", children: categoryHeader }),
8576
+ return /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("div", { className: `overflow-auto rounded-md border border-border ${className ?? ""}`, "data-chart-grid": "", children: [
8577
+ /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("table", { className: "w-full border-collapse text-xs", children: [
8578
+ /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("thead", { className: "bg-muted/50", children: /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("tr", { children: [
8579
+ /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("th", { className: "border-b border-r border-border px-2 py-1 text-left font-medium text-muted-foreground", children: categoryHeader }),
8166
8580
  spec.series.map((s, si) => {
8167
8581
  const color = (0, import_chart3.chartSeriesColor)(spec, si);
8168
8582
  const selected = selection?.kind === "series" && selection.seriesId === s.id;
8169
- return /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("th", { "data-series-id": s.id, className: `border-b border-r border-border px-1 py-1 font-medium ${selected ? "bg-primary/10" : ""}`, children: /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { className: "flex items-center gap-1", children: [
8170
- /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
8583
+ return /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("th", { "data-series-id": s.id, className: `border-b border-r border-border px-1 py-1 font-medium ${selected ? "bg-primary/10" : ""}`, children: /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("div", { className: "flex items-center gap-1", children: [
8584
+ /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
8171
8585
  "input",
8172
8586
  {
8173
8587
  type: "color",
@@ -8180,7 +8594,7 @@ function ChartDataGrid({ controller, categoryHeader = "Category", readOnly = fal
8180
8594
  className: "h-5 w-5 shrink-0 cursor-pointer rounded border-0 bg-transparent p-0"
8181
8595
  }
8182
8596
  ),
8183
- /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
8597
+ /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
8184
8598
  ChartCommitInput,
8185
8599
  {
8186
8600
  value: s.name,
@@ -8190,8 +8604,8 @@ function ChartDataGrid({ controller, categoryHeader = "Category", readOnly = fal
8190
8604
  className: `${chartInputClass} h-6 min-w-[5rem] border-transparent bg-transparent font-medium ${s.hidden ? "text-muted-foreground line-through" : ""}`
8191
8605
  }
8192
8606
  ),
8193
- !readOnly && /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)(import_jsx_runtime41.Fragment, { children: [
8194
- /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
8607
+ !readOnly && /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)(import_jsx_runtime43.Fragment, { children: [
8608
+ /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
8195
8609
  "button",
8196
8610
  {
8197
8611
  type: "button",
@@ -8204,16 +8618,16 @@ function ChartDataGrid({ controller, categoryHeader = "Category", readOnly = fal
8204
8618
  children: s.hidden ? "\u25CB" : "\u25CF"
8205
8619
  }
8206
8620
  ),
8207
- /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("button", { type: "button", className: chartIconButtonClass, title: "Delete series", "aria-label": `Delete ${s.name}`, "data-action": "remove-series", onClick: () => controller.removeSeries(s.id), children: "\xD7" })
8621
+ /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("button", { type: "button", className: chartIconButtonClass, title: "Delete series", "aria-label": `Delete ${s.name}`, "data-action": "remove-series", onClick: () => controller.removeSeries(s.id), children: "\xD7" })
8208
8622
  ] })
8209
8623
  ] }) }, s.id);
8210
8624
  }),
8211
- !readOnly && /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("th", { className: "w-8 border-b border-border px-1", children: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("button", { type: "button", className: chartIconButtonClass, title: "Add series", "aria-label": "Add series", "data-action": "add-series", onClick: () => controller.addSeries(), children: "+" }) })
8625
+ !readOnly && /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("th", { className: "w-8 border-b border-border px-1", children: /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("button", { type: "button", className: chartIconButtonClass, title: "Add series", "aria-label": "Add series", "data-action": "add-series", onClick: () => controller.addSeries(), children: "+" }) })
8212
8626
  ] }) }),
8213
- /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("tbody", { children: spec.categories.map((category, i) => {
8627
+ /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("tbody", { children: spec.categories.map((category, i) => {
8214
8628
  const rowSelected = selection?.kind === "category" && selection.index === i;
8215
- return /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("tr", { "data-category-index": i, className: rowSelected ? "bg-primary/10" : void 0, children: [
8216
- /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("th", { className: "border-b border-r border-border p-0 text-left font-normal", children: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
8629
+ return /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("tr", { "data-category-index": i, className: rowSelected ? "bg-primary/10" : void 0, children: [
8630
+ /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("th", { className: "border-b border-r border-border p-0 text-left font-normal", children: /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
8217
8631
  ChartCommitInput,
8218
8632
  {
8219
8633
  value: category,
@@ -8227,7 +8641,7 @@ function ChartDataGrid({ controller, categoryHeader = "Category", readOnly = fal
8227
8641
  spec.series.map((s) => {
8228
8642
  const value = s.values[i];
8229
8643
  const selected = selection?.kind === "point" && selection.seriesId === s.id && selection.index === i;
8230
- return /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("td", { className: `border-b border-r border-border p-0 ${selected ? "bg-primary/10" : ""}`, children: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
8644
+ return /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("td", { className: `border-b border-r border-border p-0 ${selected ? "bg-primary/10" : ""}`, children: /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
8231
8645
  ChartCommitInput,
8232
8646
  {
8233
8647
  value: value === null || value === void 0 ? "" : String(value),
@@ -8242,38 +8656,38 @@ function ChartDataGrid({ controller, categoryHeader = "Category", readOnly = fal
8242
8656
  }
8243
8657
  ) }, s.id);
8244
8658
  }),
8245
- !readOnly && /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("td", { className: "border-b border-border px-1", children: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("button", { type: "button", className: chartIconButtonClass, title: "Delete category", "aria-label": `Delete ${category}`, "data-action": "remove-category", onClick: () => controller.removeCategory(i), children: "\xD7" }) })
8659
+ !readOnly && /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("td", { className: "border-b border-border px-1", children: /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("button", { type: "button", className: chartIconButtonClass, title: "Delete category", "aria-label": `Delete ${category}`, "data-action": "remove-category", onClick: () => controller.removeCategory(i), children: "\xD7" }) })
8246
8660
  ] }, i);
8247
8661
  }) })
8248
8662
  ] }),
8249
- !readOnly && /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("div", { className: "p-1", children: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("button", { type: "button", className: `${chartButtonClass} h-6 border-dashed`, "data-action": "add-category", onClick: () => controller.addCategory(), children: "+ Add category" }) })
8663
+ !readOnly && /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("div", { className: "p-1", children: /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("button", { type: "button", className: `${chartButtonClass} h-6 border-dashed`, "data-action": "add-category", onClick: () => controller.addCategory(), children: "+ Add category" }) })
8250
8664
  ] });
8251
8665
  }
8252
8666
 
8253
8667
  // src/chart/ChartInspector.tsx
8254
- var import_react62 = require("react");
8668
+ var import_react64 = require("react");
8255
8669
  var import_chart4 = require("@pixerate/editor/chart");
8256
- var import_jsx_runtime42 = require("react/jsx-runtime");
8670
+ var import_jsx_runtime44 = require("react/jsx-runtime");
8257
8671
  var cap = (s) => s.charAt(0).toUpperCase() + s.slice(1);
8258
8672
  function Field({ label, htmlFor, children }) {
8259
- return /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { className: "flex items-center gap-2", children: [
8260
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("label", { htmlFor, className: `${chartLabelClass} w-20 shrink-0`, children: label }),
8261
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("div", { className: "min-w-0 flex-1", children })
8673
+ return /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("div", { className: "flex items-center gap-2", children: [
8674
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("label", { htmlFor, className: `${chartLabelClass} w-20 shrink-0`, children: label }),
8675
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("div", { className: "min-w-0 flex-1", children })
8262
8676
  ] });
8263
8677
  }
8264
8678
  function Toggle({ label, checked, onChange, name }) {
8265
- return /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("label", { className: "flex cursor-pointer items-center gap-2 text-xs text-foreground", children: [
8266
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("input", { type: "checkbox", className: "h-3.5 w-3.5 accent-primary", checked, "data-option": name, onChange: (e) => onChange(e.currentTarget.checked) }),
8679
+ return /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("label", { className: "flex cursor-pointer items-center gap-2 text-xs text-foreground", children: [
8680
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("input", { type: "checkbox", className: "h-3.5 w-3.5 accent-primary", checked, "data-option": name, onChange: (e) => onChange(e.currentTarget.checked) }),
8267
8681
  label
8268
8682
  ] });
8269
8683
  }
8270
8684
  function Select({ id, value, options, onChange, name, labels }) {
8271
- return /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("select", { id, className: chartInputClass, value, "data-option": name, onChange: (e) => onChange(e.currentTarget.value), children: options.map((o) => /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("option", { value: o, children: labels?.[o] ?? cap(o) }, o)) });
8685
+ return /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("select", { id, className: chartInputClass, value, "data-option": name, onChange: (e) => onChange(e.currentTarget.value), children: options.map((o) => /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("option", { value: o, children: labels?.[o] ?? cap(o) }, o)) });
8272
8686
  }
8273
8687
  function AxisSection({ controller, axis, value, idBase, horizontal, numeric = false, scales = false }) {
8274
8688
  const set = (patch) => controller.setAxis(axis, patch);
8275
8689
  const setFormat = (patch) => set({ format: { ...value.format, ...patch } });
8276
- const bound = (key2) => /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
8690
+ const bound = (key2) => /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
8277
8691
  ChartCommitInput,
8278
8692
  {
8279
8693
  id: `${idBase}-${axis}-${key2}`,
@@ -8288,26 +8702,26 @@ function AxisSection({ controller, axis, value, idBase, horizontal, numeric = fa
8288
8702
  const name = axis === "x" ? numeric ? "X axis" : "Category axis" : axis === "y" ? "Value axis" : "Secondary axis";
8289
8703
  const valueAxis = axis !== "x" || numeric;
8290
8704
  const key = axis === "y2" ? "yAxis2" : `${axis}Axis`;
8291
- return /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { className: chartSectionClass, "data-section": `axis-${axis}`, children: [
8292
- /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { className: "flex items-center justify-between", children: [
8293
- /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("h4", { className: "text-xs font-semibold text-foreground", children: [
8705
+ return /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("div", { className: chartSectionClass, "data-section": `axis-${axis}`, children: [
8706
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("div", { className: "flex items-center justify-between", children: [
8707
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("h4", { className: "text-xs font-semibold text-foreground", children: [
8294
8708
  name,
8295
- /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("span", { className: "ml-1 font-normal text-muted-foreground", children: [
8709
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("span", { className: "ml-1 font-normal text-muted-foreground", children: [
8296
8710
  "(",
8297
8711
  axis === "x" ? horizontal ? "left" : "bottom" : axis === "y2" ? horizontal ? "top" : "right" : horizontal ? "bottom" : "left",
8298
8712
  ")"
8299
8713
  ] })
8300
8714
  ] }),
8301
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(Toggle, { label: "Show", name: `${axis}Axis.show`, checked: value.show, onChange: (show) => set({ show }) })
8715
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(Toggle, { label: "Show", name: `${axis}Axis.show`, checked: value.show, onChange: (show) => set({ show }) })
8302
8716
  ] }),
8303
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(Field, { label: "Title", htmlFor: `${idBase}-${axis}-title`, children: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(ChartCommitInput, { id: `${idBase}-${axis}-title`, value: value.title, "data-option": `${axis}Axis.title`, className: chartInputClass, onCommit: (title) => void set({ title }) }) }),
8304
- /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { className: "flex flex-wrap gap-x-4 gap-y-1", children: [
8305
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(Toggle, { label: "Grid lines", name: `${key}.gridlines`, checked: value.gridlines, onChange: (gridlines) => set({ gridlines }) }),
8306
- axis === "x" && !numeric && /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(Toggle, { label: "Dates", name: "xAxis.kind", checked: value.kind === "date", onChange: (dates) => set({ kind: dates ? "date" : void 0 }) }),
8307
- scales && /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(Toggle, { label: "Reverse", name: `${key}.reverse`, checked: !!value.reverse, onChange: (reverse) => set({ reverse }) }),
8308
- scales && valueAxis && /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(Toggle, { label: "Log scale", name: `${key}.scale`, checked: value.scale === "log", onChange: (log) => set({ scale: log ? "log" : "linear" }) })
8717
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(Field, { label: "Title", htmlFor: `${idBase}-${axis}-title`, children: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(ChartCommitInput, { id: `${idBase}-${axis}-title`, value: value.title, "data-option": `${axis}Axis.title`, className: chartInputClass, onCommit: (title) => void set({ title }) }) }),
8718
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("div", { className: "flex flex-wrap gap-x-4 gap-y-1", children: [
8719
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(Toggle, { label: "Grid lines", name: `${key}.gridlines`, checked: value.gridlines, onChange: (gridlines) => set({ gridlines }) }),
8720
+ axis === "x" && !numeric && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(Toggle, { label: "Dates", name: "xAxis.kind", checked: value.kind === "date", onChange: (dates) => set({ kind: dates ? "date" : void 0 }) }),
8721
+ scales && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(Toggle, { label: "Reverse", name: `${key}.reverse`, checked: !!value.reverse, onChange: (reverse) => set({ reverse }) }),
8722
+ scales && valueAxis && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(Toggle, { label: "Log scale", name: `${key}.scale`, checked: value.scale === "log", onChange: (log) => set({ scale: log ? "log" : "linear" }) })
8309
8723
  ] }),
8310
- scales && valueAxis && value.scale === "log" && /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(Field, { label: "Log base", htmlFor: `${idBase}-${axis}-log-base`, children: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
8724
+ scales && valueAxis && value.scale === "log" && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(Field, { label: "Log base", htmlFor: `${idBase}-${axis}-log-base`, children: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
8311
8725
  ChartCommitInput,
8312
8726
  {
8313
8727
  id: `${idBase}-${axis}-log-base`,
@@ -8318,7 +8732,7 @@ function AxisSection({ controller, axis, value, idBase, horizontal, numeric = fa
8318
8732
  onCommit: (text) => set({ logBase: text.trim() === "" ? void 0 : Number(text) }).ok
8319
8733
  }
8320
8734
  ) }),
8321
- valueAxis && value.scale !== "log" && /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(Field, { label: "Step", htmlFor: `${idBase}-${axis}-step`, children: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
8735
+ valueAxis && value.scale !== "log" && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(Field, { label: "Step", htmlFor: `${idBase}-${axis}-step`, children: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
8322
8736
  ChartCommitInput,
8323
8737
  {
8324
8738
  id: `${idBase}-${axis}-step`,
@@ -8330,14 +8744,14 @@ function AxisSection({ controller, axis, value, idBase, horizontal, numeric = fa
8330
8744
  onCommit: (text) => set({ step: text.trim() === "" ? null : Number(text) }).ok
8331
8745
  }
8332
8746
  ) }),
8333
- valueAxis && /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(import_jsx_runtime42.Fragment, { children: [
8334
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(Field, { label: "Min / max", children: /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { className: "flex gap-1", children: [
8747
+ valueAxis && /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)(import_jsx_runtime44.Fragment, { children: [
8748
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(Field, { label: "Min / max", children: /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("div", { className: "flex gap-1", children: [
8335
8749
  bound("min"),
8336
8750
  bound("max")
8337
8751
  ] }) }),
8338
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(Field, { label: "Format", htmlFor: `${idBase}-${axis}-format`, children: /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { className: "flex gap-1", children: [
8339
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(Select, { id: `${idBase}-${axis}-format`, name: `${key}.format.style`, value: value.format.style ?? "number", options: import_chart4.CHART_NUMBER_STYLES, onChange: (style) => setFormat({ style }) }),
8340
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
8752
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(Field, { label: "Format", htmlFor: `${idBase}-${axis}-format`, children: /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("div", { className: "flex gap-1", children: [
8753
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(Select, { id: `${idBase}-${axis}-format`, name: `${key}.format.style`, value: value.format.style ?? "number", options: import_chart4.CHART_NUMBER_STYLES, onChange: (style) => setFormat({ style }) }),
8754
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
8341
8755
  ChartCommitInput,
8342
8756
  {
8343
8757
  value: value.format.decimals === void 0 ? "" : String(value.format.decimals),
@@ -8356,8 +8770,8 @@ function AxisSection({ controller, axis, value, idBase, horizontal, numeric = fa
8356
8770
  }
8357
8771
  )
8358
8772
  ] }) }),
8359
- value.format.style === "currency" && /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(Field, { label: "Currency", htmlFor: `${idBase}-${axis}-currency`, children: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(ChartCommitInput, { id: `${idBase}-${axis}-currency`, value: value.format.currency ?? "USD", "data-option": `${key}.format.currency`, className: chartInputClass, onCommit: (currency) => void setFormat({ currency: currency.toUpperCase() }) }) }),
8360
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(Field, { label: "Code", htmlFor: `${idBase}-${axis}-code`, children: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
8773
+ value.format.style === "currency" && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(Field, { label: "Currency", htmlFor: `${idBase}-${axis}-currency`, children: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(ChartCommitInput, { id: `${idBase}-${axis}-currency`, value: value.format.currency ?? "USD", "data-option": `${key}.format.currency`, className: chartInputClass, onCommit: (currency) => void setFormat({ currency: currency.toUpperCase() }) }) }),
8774
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(Field, { label: "Code", htmlFor: `${idBase}-${axis}-code`, children: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
8361
8775
  ChartCommitInput,
8362
8776
  {
8363
8777
  id: `${idBase}-${axis}-code`,
@@ -8383,15 +8797,15 @@ function ErrorBarFields({ controller, series, which, idBase }) {
8383
8797
  const values = which === "xErrorBars" ? series.x ?? [] : series.values;
8384
8798
  const set = (next) => controller.updateSeries(series.id, { [which]: next });
8385
8799
  const patch = (p) => bars && set({ ...bars, ...p });
8386
- return /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { className: "space-y-1.5", "data-section": which === "errorBars" ? "error-bars" : "x-error-bars", children: [
8387
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(Field, { label: which === "errorBars" ? "Error bars" : "X error bars", htmlFor: `${id}-type`, children: /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("select", { id: `${id}-type`, className: chartInputClass, value: bars?.type ?? "", "data-option": `${key}.type`, onChange: (e) => set((0, import_chart4.chartErrorBarsOfType)(e.currentTarget.value, bars, values)), children: [
8388
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("option", { value: "", children: "None" }),
8389
- import_chart4.CHART_ERROR_BAR_TYPES.map((t) => /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("option", { value: t, children: import_chart4.CHART_ERROR_BAR_LABELS[t] }, t))
8800
+ return /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("div", { className: "space-y-1.5", "data-section": which === "errorBars" ? "error-bars" : "x-error-bars", children: [
8801
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(Field, { label: which === "errorBars" ? "Error bars" : "X error bars", htmlFor: `${id}-type`, children: /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("select", { id: `${id}-type`, className: chartInputClass, value: bars?.type ?? "", "data-option": `${key}.type`, onChange: (e) => set((0, import_chart4.chartErrorBarsOfType)(e.currentTarget.value, bars, values)), children: [
8802
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("option", { value: "", children: "None" }),
8803
+ import_chart4.CHART_ERROR_BAR_TYPES.map((t) => /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("option", { value: t, children: import_chart4.CHART_ERROR_BAR_LABELS[t] }, t))
8390
8804
  ] }) }),
8391
- bars && (bars.type === "fixed" || bars.type === "percent" || bars.type === "stddev") && /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(Field, { label: bars.type === "percent" ? "Percent" : bars.type === "stddev" ? "Deviations" : "Amount", htmlFor: `${id}-value`, children: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(ChartCommitInput, { id: `${id}-value`, value: String(bars.value ?? (0, import_chart4.defaultChartErrorBarValue)(bars.type)), inputMode: "decimal", "data-option": `${key}.value`, className: chartInputClass, onCommit: (text) => !!patch({ value: text.trim() === "" ? void 0 : Number(text) })?.ok }) }),
8392
- bars?.type === "custom" && /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(import_jsx_runtime42.Fragment, { children: [
8393
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(Field, { label: "Plus", htmlFor: `${id}-plus`, children: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(ChartCommitInput, { id: `${id}-plus`, value: (0, import_chart4.formatChartAmountList)(bars.plus), placeholder: "1, 2, -, 0.5", "data-option": `${key}.plus`, className: chartInputClass, onCommit: (text) => !!patch({ plus: (0, import_chart4.parseChartAmountList)(text) })?.ok }) }),
8394
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(Field, { label: "Minus", htmlFor: `${id}-minus`, children: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
8805
+ bars && (bars.type === "fixed" || bars.type === "percent" || bars.type === "stddev") && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(Field, { label: bars.type === "percent" ? "Percent" : bars.type === "stddev" ? "Deviations" : "Amount", htmlFor: `${id}-value`, children: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(ChartCommitInput, { id: `${id}-value`, value: String(bars.value ?? (0, import_chart4.defaultChartErrorBarValue)(bars.type)), inputMode: "decimal", "data-option": `${key}.value`, className: chartInputClass, onCommit: (text) => !!patch({ value: text.trim() === "" ? void 0 : Number(text) })?.ok }) }),
8806
+ bars?.type === "custom" && /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)(import_jsx_runtime44.Fragment, { children: [
8807
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(Field, { label: "Plus", htmlFor: `${id}-plus`, children: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(ChartCommitInput, { id: `${id}-plus`, value: (0, import_chart4.formatChartAmountList)(bars.plus), placeholder: "1, 2, -, 0.5", "data-option": `${key}.plus`, className: chartInputClass, onCommit: (text) => !!patch({ plus: (0, import_chart4.parseChartAmountList)(text) })?.ok }) }),
8808
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(Field, { label: "Minus", htmlFor: `${id}-minus`, children: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
8395
8809
  ChartCommitInput,
8396
8810
  {
8397
8811
  id: `${id}-minus`,
@@ -8408,15 +8822,15 @@ function ErrorBarFields({ controller, series, which, idBase }) {
8408
8822
  }
8409
8823
  ) })
8410
8824
  ] }),
8411
- bars && /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { className: "flex items-center gap-2", children: [
8412
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("select", { className: `${chartInputClass} flex-1`, "aria-label": "Error bar direction", value: bars.direction ?? "both", "data-option": `${key}.direction`, onChange: (e) => patch({ direction: e.currentTarget.value }), children: import_chart4.CHART_ERROR_BAR_DIRECTIONS.map((d) => /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("option", { value: d, children: d === "both" ? "Both ways" : d === "plus" ? "Plus only" : "Minus only" }, d)) }),
8413
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(Toggle, { label: "Caps", name: `${key}.cap`, checked: bars.cap !== false, onChange: (cap2) => patch({ cap: cap2 }) })
8825
+ bars && /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("div", { className: "flex items-center gap-2", children: [
8826
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("select", { className: `${chartInputClass} flex-1`, "aria-label": "Error bar direction", value: bars.direction ?? "both", "data-option": `${key}.direction`, onChange: (e) => patch({ direction: e.currentTarget.value }), children: import_chart4.CHART_ERROR_BAR_DIRECTIONS.map((d) => /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("option", { value: d, children: d === "both" ? "Both ways" : d === "plus" ? "Plus only" : "Minus only" }, d)) }),
8827
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(Toggle, { label: "Caps", name: `${key}.cap`, checked: bars.cap !== false, onChange: (cap2) => patch({ cap: cap2 }) })
8414
8828
  ] })
8415
8829
  ] });
8416
8830
  }
8417
8831
  function ChartInspector({ controller, sections, className }) {
8418
8832
  const { spec, selectedSeries } = useChartState(controller);
8419
- const idBase = (0, import_react62.useId)();
8833
+ const idBase = (0, import_react64.useId)();
8420
8834
  const show = (section) => !sections || sections.includes(section);
8421
8835
  const cartesian = (0, import_chart4.isCartesianChart)(spec.type);
8422
8836
  const family = (0, import_chart4.chartFamily)(spec);
@@ -8428,29 +8842,29 @@ function ChartInspector({ controller, sections, className }) {
8428
8842
  const hasLines = cartesian && types.some((t) => t === "line" || t === "area");
8429
8843
  const seriesIndex = selectedSeries ? spec.series.findIndex((s) => s.id === selectedSeries.id) : -1;
8430
8844
  const scales = family === "cartesian" || family === "xy";
8431
- return /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { className: `space-y-3 text-xs ${className ?? ""}`, "data-chart-inspector": "", children: [
8432
- show("type") && /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("div", { className: chartSectionClass, "data-section": "type", children: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("div", { className: "grid grid-cols-3 gap-0.5 rounded-md bg-muted p-0.5", role: "radiogroup", "aria-label": "Chart type", children: import_chart4.CHART_TYPES.map((type) => /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("button", { type: "button", role: "radio", "aria-checked": spec.type === type, "data-chart-type": type, className: chartSegmentClass(spec.type === type), onClick: () => controller.setType(type), children: import_chart4.CHART_TYPE_LABELS[type] }, type)) }) }),
8433
- show("style") && /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { className: chartSectionClass, "data-section": "style", children: [
8434
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(Field, { label: "Layout", htmlFor: `${idBase}-layout`, children: /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("select", { id: `${idBase}-layout`, className: chartInputClass, value: "", "data-option": "preset.layout", onChange: (e) => e.currentTarget.value && controller.applyPreset({ layout: e.currentTarget.value }), children: [
8435
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("option", { value: "", children: "Quick layout\u2026" }),
8436
- import_chart4.CHART_LAYOUT_PRESETS.map((p) => /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("option", { value: p.id, title: p.description, children: p.label }, p.id))
8845
+ return /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("div", { className: `space-y-3 text-xs ${className ?? ""}`, "data-chart-inspector": "", children: [
8846
+ show("type") && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("div", { className: chartSectionClass, "data-section": "type", children: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("div", { className: "grid grid-cols-3 gap-0.5 rounded-md bg-muted p-0.5", role: "radiogroup", "aria-label": "Chart type", children: import_chart4.CHART_TYPES.map((type) => /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("button", { type: "button", role: "radio", "aria-checked": spec.type === type, "data-chart-type": type, className: chartSegmentClass(spec.type === type), onClick: () => controller.setType(type), children: import_chart4.CHART_TYPE_LABELS[type] }, type)) }) }),
8847
+ show("style") && /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("div", { className: chartSectionClass, "data-section": "style", children: [
8848
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(Field, { label: "Layout", htmlFor: `${idBase}-layout`, children: /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("select", { id: `${idBase}-layout`, className: chartInputClass, value: "", "data-option": "preset.layout", onChange: (e) => e.currentTarget.value && controller.applyPreset({ layout: e.currentTarget.value }), children: [
8849
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("option", { value: "", children: "Quick layout\u2026" }),
8850
+ import_chart4.CHART_LAYOUT_PRESETS.map((p) => /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("option", { value: p.id, title: p.description, children: p.label }, p.id))
8437
8851
  ] }) }),
8438
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(Field, { label: "Colours", htmlFor: `${idBase}-colors`, children: /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("select", { id: `${idBase}-colors`, className: chartInputClass, value: import_chart4.CHART_COLOR_SCHEMES.find((c) => c.palette.join() === spec.palette.join())?.id ?? "", "data-option": "preset.colors", onChange: (e) => e.currentTarget.value && controller.applyPreset({ colors: e.currentTarget.value }), children: [
8439
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("option", { value: "", children: "Custom" }),
8440
- import_chart4.CHART_COLOR_SCHEMES.map((c) => /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("option", { value: c.id, title: c.description, children: c.label }, c.id))
8852
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(Field, { label: "Colours", htmlFor: `${idBase}-colors`, children: /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("select", { id: `${idBase}-colors`, className: chartInputClass, value: import_chart4.CHART_COLOR_SCHEMES.find((c) => c.palette.join() === spec.palette.join())?.id ?? "", "data-option": "preset.colors", onChange: (e) => e.currentTarget.value && controller.applyPreset({ colors: e.currentTarget.value }), children: [
8853
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("option", { value: "", children: "Custom" }),
8854
+ import_chart4.CHART_COLOR_SCHEMES.map((c) => /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("option", { value: c.id, title: c.description, children: c.label }, c.id))
8441
8855
  ] }) }),
8442
- !colorCheck.ok && /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("p", { className: "text-[11px] leading-snug text-amber-600 dark:text-amber-400", "data-chart-color-warning": "", children: colorCheck.issues[0] }),
8443
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("button", { type: "button", className: chartButtonClass, "data-action": "transpose", onClick: () => controller.transpose(), children: "Switch rows and columns" })
8856
+ !colorCheck.ok && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("p", { className: "text-[11px] leading-snug text-amber-600 dark:text-amber-400", "data-chart-color-warning": "", children: colorCheck.issues[0] }),
8857
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("button", { type: "button", className: chartButtonClass, "data-action": "transpose", onClick: () => controller.transpose(), children: "Switch rows and columns" })
8444
8858
  ] }),
8445
- show("titles") && /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { className: chartSectionClass, "data-section": "titles", children: [
8446
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(Field, { label: "Title", htmlFor: `${idBase}-title`, children: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(ChartCommitInput, { id: `${idBase}-title`, value: spec.title, "data-option": "title", className: chartInputClass, onCommit: (title) => void controller.setTitle(title) }) }),
8447
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(Field, { label: "Subtitle", htmlFor: `${idBase}-subtitle`, children: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(ChartCommitInput, { id: `${idBase}-subtitle`, value: spec.subtitle, "data-option": "subtitle", className: chartInputClass, onCommit: (subtitle) => void controller.setSubtitle(subtitle) }) })
8859
+ show("titles") && /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("div", { className: chartSectionClass, "data-section": "titles", children: [
8860
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(Field, { label: "Title", htmlFor: `${idBase}-title`, children: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(ChartCommitInput, { id: `${idBase}-title`, value: spec.title, "data-option": "title", className: chartInputClass, onCommit: (title) => void controller.setTitle(title) }) }),
8861
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(Field, { label: "Subtitle", htmlFor: `${idBase}-subtitle`, children: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(ChartCommitInput, { id: `${idBase}-subtitle`, value: spec.subtitle, "data-option": "subtitle", className: chartInputClass, onCommit: (subtitle) => void controller.setSubtitle(subtitle) }) })
8448
8862
  ] }),
8449
- show("options") && /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { className: chartSectionClass, "data-section": "options", children: [
8450
- cartesian && /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(Field, { label: "Stacking", htmlFor: `${idBase}-stacking`, children: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(Select, { id: `${idBase}-stacking`, name: "stacking", value: spec.stacking, options: import_chart4.CHART_STACKINGS, labels: { none: "None", stacked: "Stacked", percent: "100%" }, onChange: (stacking) => controller.update({ stacking }) }) }),
8451
- hasBars && /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(Field, { label: "Bars", children: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("div", { className: "flex gap-0.5 rounded-md bg-muted p-0.5", children: ["vertical", "horizontal"].map((o) => /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("button", { type: "button", "aria-pressed": spec.orientation === o, "data-option": `orientation.${o}`, className: chartSegmentClass(spec.orientation === o), onClick: () => controller.update({ orientation: o }), children: cap(o) }, o)) }) }),
8452
- hasLines && /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(Field, { label: "Lines", htmlFor: `${idBase}-curve`, children: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(Select, { id: `${idBase}-curve`, name: "curve", value: spec.curve, options: import_chart4.CHART_CURVES, onChange: (curve) => controller.update({ curve }) }) }),
8453
- spec.type === "donut" && /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(Field, { label: "Hole", htmlFor: `${idBase}-hole`, children: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
8863
+ show("options") && /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("div", { className: chartSectionClass, "data-section": "options", children: [
8864
+ cartesian && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(Field, { label: "Stacking", htmlFor: `${idBase}-stacking`, children: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(Select, { id: `${idBase}-stacking`, name: "stacking", value: spec.stacking, options: import_chart4.CHART_STACKINGS, labels: { none: "None", stacked: "Stacked", percent: "100%" }, onChange: (stacking) => controller.update({ stacking }) }) }),
8865
+ hasBars && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(Field, { label: "Bars", children: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("div", { className: "flex gap-0.5 rounded-md bg-muted p-0.5", children: ["vertical", "horizontal"].map((o) => /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("button", { type: "button", "aria-pressed": spec.orientation === o, "data-option": `orientation.${o}`, className: chartSegmentClass(spec.orientation === o), onClick: () => controller.update({ orientation: o }), children: cap(o) }, o)) }) }),
8866
+ hasLines && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(Field, { label: "Lines", htmlFor: `${idBase}-curve`, children: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(Select, { id: `${idBase}-curve`, name: "curve", value: spec.curve, options: import_chart4.CHART_CURVES, onChange: (curve) => controller.update({ curve }) }) }),
8867
+ spec.type === "donut" && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(Field, { label: "Hole", htmlFor: `${idBase}-hole`, children: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
8454
8868
  "input",
8455
8869
  {
8456
8870
  id: `${idBase}-hole`,
@@ -8466,7 +8880,7 @@ function ChartInspector({ controller, sections, className }) {
8466
8880
  onChange: (e) => controller.update({ innerRadius: Number(e.currentTarget.value) })
8467
8881
  }
8468
8882
  ) }),
8469
- hasBars && /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(Field, { label: "Spacing", htmlFor: `${idBase}-gap`, children: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
8883
+ hasBars && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(Field, { label: "Spacing", htmlFor: `${idBase}-gap`, children: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
8470
8884
  "input",
8471
8885
  {
8472
8886
  id: `${idBase}-gap`,
@@ -8482,31 +8896,31 @@ function ChartInspector({ controller, sections, className }) {
8482
8896
  onChange: (e) => controller.update({ gapWidth: Number(e.currentTarget.value) })
8483
8897
  }
8484
8898
  ) }),
8485
- spec.dataLabels && hasBars && /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(Field, { label: "Labels", htmlFor: `${idBase}-label-position`, children: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(Select, { id: `${idBase}-label-position`, name: "labelPosition", value: spec.labelPosition, options: import_chart4.CHART_LABEL_POSITIONS, onChange: (labelPosition) => controller.update({ labelPosition }) }) }),
8486
- spec.type === "histogram" && /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(Field, { label: "Bins", htmlFor: `${idBase}-bins`, children: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(ChartCommitInput, { id: `${idBase}-bins`, value: spec.bins === null ? "" : String(spec.bins), placeholder: "Auto", inputMode: "numeric", "data-option": "bins", className: chartInputClass, onCommit: (text) => controller.update({ bins: text.trim() === "" ? null : Number(text) }).ok }) }),
8487
- /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { className: "flex flex-wrap gap-x-4 gap-y-1", children: [
8488
- spec.type === "waterfall" && /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(Toggle, { label: "Total bar", name: "showTotal", checked: spec.showTotal, onChange: (showTotal) => controller.update({ showTotal }) }),
8489
- hasLines && /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(Toggle, { label: "Points", name: "points", checked: spec.points, onChange: (points) => controller.update({ points }) }),
8490
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(Toggle, { label: "Data labels", name: "dataLabels", checked: spec.dataLabels, onChange: (dataLabels) => controller.update({ dataLabels }) })
8899
+ spec.dataLabels && hasBars && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(Field, { label: "Labels", htmlFor: `${idBase}-label-position`, children: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(Select, { id: `${idBase}-label-position`, name: "labelPosition", value: spec.labelPosition, options: import_chart4.CHART_LABEL_POSITIONS, onChange: (labelPosition) => controller.update({ labelPosition }) }) }),
8900
+ spec.type === "histogram" && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(Field, { label: "Bins", htmlFor: `${idBase}-bins`, children: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(ChartCommitInput, { id: `${idBase}-bins`, value: spec.bins === null ? "" : String(spec.bins), placeholder: "Auto", inputMode: "numeric", "data-option": "bins", className: chartInputClass, onCommit: (text) => controller.update({ bins: text.trim() === "" ? null : Number(text) }).ok }) }),
8901
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("div", { className: "flex flex-wrap gap-x-4 gap-y-1", children: [
8902
+ spec.type === "waterfall" && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(Toggle, { label: "Total bar", name: "showTotal", checked: spec.showTotal, onChange: (showTotal) => controller.update({ showTotal }) }),
8903
+ hasLines && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(Toggle, { label: "Points", name: "points", checked: spec.points, onChange: (points) => controller.update({ points }) }),
8904
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(Toggle, { label: "Data labels", name: "dataLabels", checked: spec.dataLabels, onChange: (dataLabels) => controller.update({ dataLabels }) })
8491
8905
  ] })
8492
8906
  ] }),
8493
- show("legend") && /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { className: chartSectionClass, "data-section": "legend", children: [
8494
- /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { className: "flex items-center justify-between", children: [
8495
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("h4", { className: "text-xs font-semibold text-foreground", children: "Legend" }),
8496
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(Toggle, { label: "Show", name: "legend.show", checked: spec.legend.show, onChange: (v) => controller.setLegend({ show: v }) })
8907
+ show("legend") && /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("div", { className: chartSectionClass, "data-section": "legend", children: [
8908
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("div", { className: "flex items-center justify-between", children: [
8909
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("h4", { className: "text-xs font-semibold text-foreground", children: "Legend" }),
8910
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(Toggle, { label: "Show", name: "legend.show", checked: spec.legend.show, onChange: (v) => controller.setLegend({ show: v }) })
8497
8911
  ] }),
8498
- spec.legend.show && /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("div", { className: "flex gap-0.5 rounded-md bg-muted p-0.5", children: import_chart4.CHART_LEGEND_POSITIONS.map((p) => /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("button", { type: "button", "aria-pressed": spec.legend.position === p, "data-option": `legend.${p}`, className: chartSegmentClass(spec.legend.position === p), onClick: () => controller.setLegend({ position: p }), children: cap(p) }, p)) })
8912
+ spec.legend.show && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("div", { className: "flex gap-0.5 rounded-md bg-muted p-0.5", children: import_chart4.CHART_LEGEND_POSITIONS.map((p) => /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("button", { type: "button", "aria-pressed": spec.legend.position === p, "data-option": `legend.${p}`, className: chartSegmentClass(spec.legend.position === p), onClick: () => controller.setLegend({ position: p }), children: cap(p) }, p)) })
8499
8913
  ] }),
8500
- show("axes") && hasAxes && /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(import_jsx_runtime42.Fragment, { children: [
8501
- family !== "box" && family !== "histogram" && /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(AxisSection, { controller, axis: "x", value: spec.xAxis, idBase, horizontal: spec.orientation === "horizontal", numeric: family === "xy", scales }),
8502
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(AxisSection, { controller, axis: "y", value: spec.yAxis, idBase, horizontal: spec.orientation === "horizontal", scales }),
8503
- usesSecondary && /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(AxisSection, { controller, axis: "y2", value: spec.yAxis2, idBase, horizontal: spec.orientation === "horizontal", scales })
8914
+ show("axes") && hasAxes && /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)(import_jsx_runtime44.Fragment, { children: [
8915
+ family !== "box" && family !== "histogram" && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(AxisSection, { controller, axis: "x", value: spec.xAxis, idBase, horizontal: spec.orientation === "horizontal", numeric: family === "xy", scales }),
8916
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(AxisSection, { controller, axis: "y", value: spec.yAxis, idBase, horizontal: spec.orientation === "horizontal", scales }),
8917
+ usesSecondary && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(AxisSection, { controller, axis: "y2", value: spec.yAxis2, idBase, horizontal: spec.orientation === "horizontal", scales })
8504
8918
  ] }),
8505
- show("series") && selectedSeries && /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { className: chartSectionClass, "data-section": "series", "data-series-id": selectedSeries.id, children: [
8506
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("h4", { className: "text-xs font-semibold text-foreground", children: "Series" }),
8507
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(Field, { label: "Name", htmlFor: `${idBase}-series-name`, children: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(ChartCommitInput, { id: `${idBase}-series-name`, value: selectedSeries.name, "data-option": "series.name", className: chartInputClass, onCommit: (name) => void controller.updateSeries(selectedSeries.id, { name }) }) }),
8508
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(Field, { label: "Colour", htmlFor: `${idBase}-series-color`, children: /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { className: "flex items-center gap-1", children: [
8509
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
8919
+ show("series") && selectedSeries && /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("div", { className: chartSectionClass, "data-section": "series", "data-series-id": selectedSeries.id, children: [
8920
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("h4", { className: "text-xs font-semibold text-foreground", children: "Series" }),
8921
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(Field, { label: "Name", htmlFor: `${idBase}-series-name`, children: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(ChartCommitInput, { id: `${idBase}-series-name`, value: selectedSeries.name, "data-option": "series.name", className: chartInputClass, onCommit: (name) => void controller.updateSeries(selectedSeries.id, { name }) }) }),
8922
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(Field, { label: "Colour", htmlFor: `${idBase}-series-color`, children: /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("div", { className: "flex items-center gap-1", children: [
8923
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
8510
8924
  "input",
8511
8925
  {
8512
8926
  id: `${idBase}-series-color`,
@@ -8519,9 +8933,9 @@ function ChartInspector({ controller, sections, className }) {
8519
8933
  onChange: (e) => controller.updateSeries(selectedSeries.id, { color: e.currentTarget.value })
8520
8934
  }
8521
8935
  ),
8522
- selectedSeries.color && /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("button", { type: "button", className: chartButtonClass, "data-action": "reset-color", onClick: () => controller.updateSeries(selectedSeries.id, { color: null }), children: "Palette" })
8936
+ selectedSeries.color && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("button", { type: "button", className: chartButtonClass, "data-action": "reset-color", onClick: () => controller.updateSeries(selectedSeries.id, { color: null }), children: "Palette" })
8523
8937
  ] }) }),
8524
- cartesian && /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(Field, { label: "Draw as", htmlFor: `${idBase}-series-type`, children: /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(
8938
+ cartesian && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(Field, { label: "Draw as", htmlFor: `${idBase}-series-type`, children: /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)(
8525
8939
  "select",
8526
8940
  {
8527
8941
  id: `${idBase}-series-type`,
@@ -8530,12 +8944,12 @@ function ChartInspector({ controller, sections, className }) {
8530
8944
  "data-option": "series.type",
8531
8945
  onChange: (e) => controller.updateSeries(selectedSeries.id, { type: e.currentTarget.value || null }),
8532
8946
  children: [
8533
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("option", { value: "", children: "Chart type" }),
8534
- import_chart4.CHART_SERIES_TYPES.map((t) => /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("option", { value: t, children: import_chart4.CHART_TYPE_LABELS[t] }, t))
8947
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("option", { value: "", children: "Chart type" }),
8948
+ import_chart4.CHART_SERIES_TYPES.map((t) => /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("option", { value: t, children: import_chart4.CHART_TYPE_LABELS[t] }, t))
8535
8949
  ]
8536
8950
  }
8537
8951
  ) }),
8538
- (cartesian || family === "xy") && ["line", "area", "scatter"].includes((0, import_chart4.chartSeriesType)(spec, selectedSeries)) && /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(Field, { label: "Marker", htmlFor: `${idBase}-series-marker`, children: /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(
8952
+ (cartesian || family === "xy") && ["line", "area", "scatter"].includes((0, import_chart4.chartSeriesType)(spec, selectedSeries)) && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(Field, { label: "Marker", htmlFor: `${idBase}-series-marker`, children: /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)(
8539
8953
  "select",
8540
8954
  {
8541
8955
  id: `${idBase}-series-marker`,
@@ -8544,20 +8958,20 @@ function ChartInspector({ controller, sections, className }) {
8544
8958
  "data-option": "series.marker",
8545
8959
  onChange: (e) => controller.updateSeries(selectedSeries.id, { marker: e.currentTarget.value || null }),
8546
8960
  children: [
8547
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("option", { value: "", children: "Default" }),
8548
- import_chart4.CHART_MARKERS.map((m) => /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("option", { value: m, children: cap(m) }, m))
8961
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("option", { value: "", children: "Default" }),
8962
+ import_chart4.CHART_MARKERS.map((m) => /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("option", { value: m, children: cap(m) }, m))
8549
8963
  ]
8550
8964
  }
8551
8965
  ) }),
8552
- scales && /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(ErrorBarFields, { controller, series: selectedSeries, which: "errorBars", idBase }),
8553
- family === "xy" && /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(ErrorBarFields, { controller, series: selectedSeries, which: "xErrorBars", idBase }),
8554
- (0, import_chart4.chartSupportsTrendlines)(spec.type, spec.stacking) && /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { className: "space-y-1", "data-section": "trendlines", children: [
8555
- (selectedSeries.trendlines ?? []).map((t, i) => /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { className: "flex items-center gap-1", "data-trendline": i, children: [
8556
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("span", { className: "flex-1 truncate text-muted-foreground", children: (0, import_chart4.chartTrendlineName)(t, selectedSeries.name) }),
8557
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(Toggle, { label: "R\xB2", name: `trendline.${i}.showR2`, checked: !!t.showR2, onChange: (showR2) => controller.updateTrendline(selectedSeries.id, i, { showR2 }) }),
8558
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("button", { type: "button", className: chartButtonClass, "aria-label": "Delete trendline", "data-action": "trendline-delete", onClick: () => controller.removeTrendline(selectedSeries.id, i), children: "\xD7" })
8966
+ scales && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(ErrorBarFields, { controller, series: selectedSeries, which: "errorBars", idBase }),
8967
+ family === "xy" && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(ErrorBarFields, { controller, series: selectedSeries, which: "xErrorBars", idBase }),
8968
+ (0, import_chart4.chartSupportsTrendlines)(spec.type, spec.stacking) && /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("div", { className: "space-y-1", "data-section": "trendlines", children: [
8969
+ (selectedSeries.trendlines ?? []).map((t, i) => /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("div", { className: "flex items-center gap-1", "data-trendline": i, children: [
8970
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("span", { className: "flex-1 truncate text-muted-foreground", children: (0, import_chart4.chartTrendlineName)(t, selectedSeries.name) }),
8971
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(Toggle, { label: "R\xB2", name: `trendline.${i}.showR2`, checked: !!t.showR2, onChange: (showR2) => controller.updateTrendline(selectedSeries.id, i, { showR2 }) }),
8972
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("button", { type: "button", className: chartButtonClass, "aria-label": "Delete trendline", "data-action": "trendline-delete", onClick: () => controller.removeTrendline(selectedSeries.id, i), children: "\xD7" })
8559
8973
  ] }, i)),
8560
- /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(
8974
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)(
8561
8975
  "select",
8562
8976
  {
8563
8977
  className: chartInputClass,
@@ -8566,32 +8980,32 @@ function ChartInspector({ controller, sections, className }) {
8566
8980
  "data-option": "trendline.add",
8567
8981
  onChange: (e) => e.currentTarget.value && controller.addTrendline(selectedSeries.id, { type: e.currentTarget.value }),
8568
8982
  children: [
8569
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("option", { value: "", children: "Add trendline\u2026" }),
8570
- import_chart4.CHART_TRENDLINE_TYPES.map((t) => /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("option", { value: t, children: cap(t.replace("-", " ")) }, t))
8983
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("option", { value: "", children: "Add trendline\u2026" }),
8984
+ import_chart4.CHART_TRENDLINE_TYPES.map((t) => /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("option", { value: t, children: cap(t.replace("-", " ")) }, t))
8571
8985
  ]
8572
8986
  }
8573
8987
  )
8574
8988
  ] }),
8575
- /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { className: "flex flex-wrap items-center gap-1", children: [
8576
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(Toggle, { label: "Hidden", name: "series.hidden", checked: !!selectedSeries.hidden, onChange: (hidden) => controller.updateSeries(selectedSeries.id, { hidden }) }),
8577
- family === "cartesian" && /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(Toggle, { label: "Secondary axis", name: "series.axis", checked: selectedSeries.axis === "secondary", onChange: (secondary) => controller.updateSeries(selectedSeries.id, { axis: secondary ? "secondary" : null }) }),
8578
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("span", { className: "flex-1" }),
8579
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("button", { type: "button", className: chartButtonClass, disabled: seriesIndex <= 0, "aria-label": "Move series earlier", "data-action": "series-up", onClick: () => controller.moveSeries(selectedSeries.id, seriesIndex - 1), children: "\u2191" }),
8580
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("button", { type: "button", className: chartButtonClass, disabled: seriesIndex >= spec.series.length - 1, "aria-label": "Move series later", "data-action": "series-down", onClick: () => controller.moveSeries(selectedSeries.id, seriesIndex + 1), children: "\u2193" }),
8581
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("button", { type: "button", className: `${chartButtonClass} text-destructive`, "data-action": "series-delete", onClick: () => controller.removeSeries(selectedSeries.id), children: "Delete" })
8989
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("div", { className: "flex flex-wrap items-center gap-1", children: [
8990
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(Toggle, { label: "Hidden", name: "series.hidden", checked: !!selectedSeries.hidden, onChange: (hidden) => controller.updateSeries(selectedSeries.id, { hidden }) }),
8991
+ family === "cartesian" && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(Toggle, { label: "Secondary axis", name: "series.axis", checked: selectedSeries.axis === "secondary", onChange: (secondary) => controller.updateSeries(selectedSeries.id, { axis: secondary ? "secondary" : null }) }),
8992
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("span", { className: "flex-1" }),
8993
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("button", { type: "button", className: chartButtonClass, disabled: seriesIndex <= 0, "aria-label": "Move series earlier", "data-action": "series-up", onClick: () => controller.moveSeries(selectedSeries.id, seriesIndex - 1), children: "\u2191" }),
8994
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("button", { type: "button", className: chartButtonClass, disabled: seriesIndex >= spec.series.length - 1, "aria-label": "Move series later", "data-action": "series-down", onClick: () => controller.moveSeries(selectedSeries.id, seriesIndex + 1), children: "\u2193" }),
8995
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("button", { type: "button", className: `${chartButtonClass} text-destructive`, "data-action": "series-delete", onClick: () => controller.removeSeries(selectedSeries.id), children: "Delete" })
8582
8996
  ] })
8583
8997
  ] })
8584
8998
  ] });
8585
8999
  }
8586
9000
 
8587
9001
  // src/chart-node/ChartNodeView.tsx
8588
- var import_jsx_runtime43 = require("react/jsx-runtime");
9002
+ var import_jsx_runtime45 = require("react/jsx-runtime");
8589
9003
  function ChartNodeView({ state, theme, showDataGrid = true }) {
8590
9004
  const { controller, editing, editable, selected, width, height, setEditing } = state;
8591
9005
  const open = () => {
8592
9006
  if (editable && !editing) setEditing(true);
8593
9007
  };
8594
- return /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)(
9008
+ return /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)(
8595
9009
  "div",
8596
9010
  {
8597
9011
  className: `not-prose my-2 rounded-md border bg-background p-1 ${selected ? "border-primary ring-1 ring-primary" : "border-border"}`,
@@ -8604,24 +9018,24 @@ function ChartNodeView({ state, theme, showDataGrid = true }) {
8604
9018
  }
8605
9019
  },
8606
9020
  children: [
8607
- editing && /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("div", { className: "mb-1 flex items-center justify-between gap-2 px-1", "data-chart-node-toolbar": "", children: [
8608
- /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("span", { className: "text-xs text-muted-foreground", children: "Editing chart" }),
8609
- /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("button", { type: "button", className: chartButtonClass, "data-action": "done", onClick: () => setEditing(false), children: "Done" })
9021
+ editing && /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { className: "mb-1 flex items-center justify-between gap-2 px-1", "data-chart-node-toolbar": "", children: [
9022
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("span", { className: "text-xs text-muted-foreground", children: "Editing chart" }),
9023
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("button", { type: "button", className: chartButtonClass, "data-action": "done", onClick: () => setEditing(false), children: "Done" })
8610
9024
  ] }),
8611
- /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
9025
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
8612
9026
  "div",
8613
9027
  {
8614
9028
  className: editable && !editing ? "cursor-pointer" : void 0,
8615
9029
  onClick: open,
8616
9030
  title: editable && !editing ? "Click to edit the chart" : void 0,
8617
9031
  "data-chart-node-canvas": "",
8618
- children: /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(ChartCanvas, { controller, width: width ?? 640, height, responsive: width === null, editable: editing, theme })
9032
+ children: /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(ChartCanvas, { controller, width: width ?? 640, height, responsive: width === null, editable: editing, theme })
8619
9033
  }
8620
9034
  ),
8621
9035
  editing && // Wraps by the document's width, not the viewport's: side by side when there is room.
8622
- /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("div", { className: "mt-2 flex flex-wrap items-start gap-2", "data-chart-node-panel": "", children: [
8623
- showDataGrid && /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("div", { className: "min-w-0 flex-[1_1_20rem] overflow-x-auto", children: /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(ChartDataGrid, { controller }) }),
8624
- /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(ChartInspector, { controller, className: "max-h-96 min-w-0 flex-[1_1_16rem] overflow-y-auto rounded-md border border-border p-3" })
9036
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { className: "mt-2 flex flex-wrap items-start gap-2", "data-chart-node-panel": "", children: [
9037
+ showDataGrid && /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("div", { className: "min-w-0 flex-[1_1_20rem] overflow-x-auto", children: /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(ChartDataGrid, { controller }) }),
9038
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(ChartInspector, { controller, className: "max-h-96 min-w-0 flex-[1_1_16rem] overflow-y-auto rounded-md border border-border p-3" })
8625
9039
  ] })
8626
9040
  ]
8627
9041
  }
@@ -8639,7 +9053,7 @@ function createReactChartNodeView(options = {}) {
8639
9053
  scheduled = true;
8640
9054
  queueMicrotask(() => {
8641
9055
  scheduled = false;
8642
- if (!destroyed) root.render(/* @__PURE__ */ (0, import_jsx_runtime43.jsx)(Component, { ...props, state: latest }));
9056
+ if (!destroyed) root.render(/* @__PURE__ */ (0, import_jsx_runtime45.jsx)(Component, { ...props, state: latest }));
8643
9057
  });
8644
9058
  };
8645
9059
  schedule();
@@ -8673,6 +9087,7 @@ function createReactChartNodeView(options = {}) {
8673
9087
  ExportDocxButton,
8674
9088
  FindReplaceBar,
8675
9089
  FormulaHints,
9090
+ FreeTransformOptionsBar,
8676
9091
  GenerationHistory,
8677
9092
  GenerationSettings,
8678
9093
  Histogram,
@@ -8680,6 +9095,7 @@ function createReactChartNodeView(options = {}) {
8680
9095
  ImageEditor,
8681
9096
  InsertTableGrid,
8682
9097
  JobProgress,
9098
+ LayerEffectsPanel,
8683
9099
  LayersPanel,
8684
9100
  MergePreview,
8685
9101
  NameManager,