@pixerate/editor-react 0.68.0 → 0.69.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.
@@ -39,6 +39,8 @@ __export(vector_exports, {
39
39
  VectorGradientAnnotator: () => VectorGradientAnnotator,
40
40
  VectorGradientDefs: () => VectorGradientDefs,
41
41
  VectorGradientEditor: () => VectorGradientEditor,
42
+ VectorMaskPanel: () => VectorMaskPanel,
43
+ VectorMaskedNodes: () => VectorMaskedNodes,
42
44
  VectorSceneNodes: () => VectorSceneNodes,
43
45
  VectorStylePanel: () => VectorStylePanel,
44
46
  VectorToolbar: () => VectorToolbar,
@@ -228,7 +230,8 @@ function VectorGradientAnnotator({ annotator: g, accentColor = import_vector3.VE
228
230
  }
229
231
  function VectorFilterDefs({ filters }) {
230
232
  if (!filters.length) return null;
231
- const primitive = (p, key) => import_react3.default.createElement(p.tag, { key, ...p.attrs }, p.children?.map(primitive));
233
+ const props = (attrs) => Object.fromEntries(Object.entries(attrs).map(([k, v]) => [k.replace(/-([a-z])/g, (_, c) => c.toUpperCase()), v]));
234
+ const primitive = (p, key) => import_react3.default.createElement(p.tag, { key, ...props(p.attrs) }, p.children?.map(primitive));
232
235
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("defs", { children: filters.map((f) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("filter", { id: f.id, filterUnits: "userSpaceOnUse", x: f.x, y: f.y, width: f.width, height: f.height, colorInterpolationFilters: "sRGB", children: f.primitives.map(primitive) }, f.id)) });
233
236
  }
234
237
  var blendStyle = (blendMode) => blendMode ? { mixBlendMode: blendMode } : void 0;
@@ -238,11 +241,12 @@ function VectorSceneNodes({ nodes }) {
238
241
  "g",
239
242
  {
240
243
  "data-vector-id": node.id,
244
+ "data-vector-mask": node.mask?.type,
241
245
  transform: node.transform,
242
246
  opacity: node.opacity < 1 ? node.opacity : void 0,
243
247
  filter: node.filter,
244
248
  style: blendStyle(node.blendMode),
245
- children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(VectorSceneNodes, { nodes: node.children })
249
+ children: node.mask ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(VectorMaskedNodes, { mask: node.mask, nodes: node.children }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(VectorSceneNodes, { nodes: node.children })
246
250
  },
247
251
  node.id
248
252
  ) : node.layers ? (
@@ -274,6 +278,13 @@ function VectorSceneNodes({ nodes }) {
274
278
  )
275
279
  ) });
276
280
  }
281
+ function VectorMaskedNodes({ mask, nodes }) {
282
+ const b = mask.bounds;
283
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
284
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("defs", { children: mask.clip ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("clipPath", { id: mask.id, children: mask.clip.map((c, i) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: c.d, clipRule: c.clipRule }, i)) }) : b && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("mask", { id: mask.id, maskUnits: "userSpaceOnUse", x: b.x, y: b.y, width: b.width, height: b.height, style: mask.type === "alpha" ? { maskType: "alpha" } : void 0, children: mask.filter ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("g", { filter: mask.filter, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(VectorSceneNodes, { nodes: mask.nodes ?? [] }) }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(VectorSceneNodes, { nodes: mask.nodes ?? [] }) }) }),
285
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("g", { "data-vector-masked": "", clipPath: mask.clip ? `url(#${mask.id})` : void 0, mask: mask.clip ? void 0 : `url(#${mask.id})`, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(VectorSceneNodes, { nodes }) })
286
+ ] });
287
+ }
277
288
  function VectorPaintPath({
278
289
  paint,
279
290
  d,
@@ -501,6 +512,21 @@ function VectorCanvas({
501
512
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { cx: w.x, cy: w.y, r: import_vector3.VECTOR_OVERLAY_THEME.cornerWidgetRadius + (w.hovered ? 1 : 0), fill: import_vector3.VECTOR_OVERLAY_THEME.handleFill, stroke: accentColor }),
502
513
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { cx: w.x, cy: w.y, r: 1.5, fill: accentColor })
503
514
  ] }, `corner:${w.ref.nodeId}:${w.ref.subpath}:${w.ref.index}`)),
515
+ overlay.masks.map((m) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
516
+ "path",
517
+ {
518
+ "data-vector-mask-indicator": m.id,
519
+ "data-mask-type": m.type,
520
+ "data-inverted": m.invert || void 0,
521
+ d: m.d,
522
+ fill: "none",
523
+ stroke: import_vector3.VECTOR_OVERLAY_THEME.mask,
524
+ strokeWidth: m.editing ? 1.5 : 1,
525
+ strokeDasharray: m.editing ? void 0 : import_vector3.VECTOR_OVERLAY_THEME.maskDash,
526
+ opacity: m.enabled ? 1 : 0.5
527
+ },
528
+ `mask:${m.id}`
529
+ )),
504
530
  overlay.blend.spines.map((sp) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("g", { "data-vector-blend-spine": sp.id, "data-replaced": sp.replaced || void 0, children: [
505
531
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: sp.d, fill: "none", stroke: accentColor, strokeDasharray: "4 3" }),
506
532
  sp.keys.map((k, i) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { cx: k.x, cy: k.y, r: import_vector3.VECTOR_OVERLAY_THEME.handleRadius, fill: import_vector3.VECTOR_OVERLAY_THEME.handleFill, stroke: accentColor }, i))
@@ -629,7 +655,7 @@ function VectorCanvas({
629
655
 
630
656
  // src/vector/VectorToolbar.tsx
631
657
  var import_react5 = __toESM(require("react"), 1);
632
- var import_vector8 = require("@pixerate/editor/vector");
658
+ var import_vector9 = require("@pixerate/editor/vector");
633
659
 
634
660
  // src/vector/VectorEffectsPanel.tsx
635
661
  var import_vector4 = require("@pixerate/editor/vector");
@@ -1090,16 +1116,134 @@ function VectorEnvelopePanel({ controller, className }) {
1090
1116
  ] });
1091
1117
  }
1092
1118
 
1093
- // src/vector/VectorGradientEditor.tsx
1119
+ // src/vector/VectorMaskPanel.tsx
1094
1120
  var import_vector7 = require("@pixerate/editor/vector");
1095
1121
  var import_jsx_runtime6 = require("react/jsx-runtime");
1122
+ var buttonClass4 = "inline-flex h-7 min-w-7 items-center justify-center rounded-md border border-border bg-background px-1.5 text-xs font-medium text-foreground hover:bg-muted disabled:cursor-not-allowed disabled:opacity-40 aria-pressed:bg-primary aria-pressed:text-primary-foreground";
1096
1123
  var selectClass4 = "h-7 min-w-0 flex-1 rounded border border-border bg-background px-1 text-xs";
1124
+ function VectorMaskPanel({ controller, className }) {
1125
+ const state = useVectorEditorState(controller);
1126
+ const panel = (0, import_vector7.getVectorMaskPanel)(state);
1127
+ if (!panel) return null;
1128
+ const g = panel.group;
1129
+ const ids = g ? [g.id] : void 0;
1130
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: `space-y-2 ${className ?? ""}`, "data-vector-mask-panel": "", "aria-label": "Mask", children: [
1131
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex flex-wrap items-center gap-1", children: [
1132
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "w-16 text-[11px] font-medium uppercase tracking-wide text-muted-foreground", children: "Mask" }),
1133
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1134
+ "button",
1135
+ {
1136
+ type: "button",
1137
+ "data-vector-mask-action": "clip",
1138
+ className: buttonClass4,
1139
+ title: panel.makeHint ?? "Make Clipping Mask: the topmost object clips the others",
1140
+ "aria-label": "Make Clipping Mask",
1141
+ disabled: !panel.canMake,
1142
+ onClick: () => controller.makeMask("clip"),
1143
+ children: "Clip"
1144
+ }
1145
+ ),
1146
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1147
+ "button",
1148
+ {
1149
+ type: "button",
1150
+ "data-vector-mask-action": "opacity",
1151
+ className: buttonClass4,
1152
+ title: panel.makeHint ?? "Make Opacity Mask: the topmost object fades the others by its brightness",
1153
+ "aria-label": "Make Opacity Mask",
1154
+ disabled: !panel.canMake,
1155
+ onClick: () => controller.makeMask("luminance"),
1156
+ children: "Opacity"
1157
+ }
1158
+ ),
1159
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1160
+ "button",
1161
+ {
1162
+ type: "button",
1163
+ "data-vector-mask-action": "release",
1164
+ className: buttonClass4,
1165
+ title: "Release: the mask goes back on top and draws as itself",
1166
+ "aria-label": "Release Mask",
1167
+ disabled: !g,
1168
+ onClick: () => controller.releaseMask({ ids }),
1169
+ children: "Release"
1170
+ }
1171
+ )
1172
+ ] }),
1173
+ g && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "space-y-2", "data-vector-mask-group": g.id, "data-mask-type": g.type, children: [
1174
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex items-center gap-1", children: [
1175
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1176
+ "select",
1177
+ {
1178
+ "aria-label": "Mask type",
1179
+ className: selectClass4,
1180
+ value: g.type,
1181
+ title: import_vector7.VECTOR_MASK_TYPE_OPTIONS.find((o) => o.value === g.type)?.title,
1182
+ onChange: (e) => controller.setMaskOptions({ type: e.target.value }, { ids }),
1183
+ children: import_vector7.VECTOR_MASK_TYPE_OPTIONS.map((o) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("option", { value: o.value, title: o.title, children: o.label }, o.value))
1184
+ }
1185
+ ),
1186
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("label", { className: "flex items-center gap-1 text-xs text-foreground", title: "Show the contents where the mask hides them", children: [
1187
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("input", { type: "checkbox", "aria-label": "Invert mask", checked: g.invert, onChange: (e) => controller.setMaskOptions({ invert: e.target.checked }, { ids }) }),
1188
+ "Invert"
1189
+ ] })
1190
+ ] }),
1191
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex flex-wrap items-center gap-1", children: [
1192
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1193
+ "button",
1194
+ {
1195
+ type: "button",
1196
+ "data-vector-mask-action": "edit-mask",
1197
+ className: buttonClass4,
1198
+ "aria-pressed": g.editing === "mask",
1199
+ title: "Edit Mask: select the mask (a path opens for anchor editing)",
1200
+ "aria-label": "Edit Mask",
1201
+ onClick: () => controller.editMask({ id: g.id }),
1202
+ children: "Edit mask"
1203
+ }
1204
+ ),
1205
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1206
+ "button",
1207
+ {
1208
+ type: "button",
1209
+ "data-vector-mask-action": "edit-contents",
1210
+ className: buttonClass4,
1211
+ "aria-pressed": g.editing === "contents",
1212
+ title: "Edit Contents: select what the mask masks",
1213
+ "aria-label": "Edit Contents",
1214
+ disabled: !g.contents.length,
1215
+ onClick: () => controller.editMaskContents({ id: g.id }),
1216
+ children: "Contents"
1217
+ }
1218
+ ),
1219
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1220
+ "button",
1221
+ {
1222
+ type: "button",
1223
+ "data-vector-mask-action": "toggle",
1224
+ className: buttonClass4,
1225
+ "aria-pressed": g.enabled,
1226
+ title: g.enabled ? "Turn the mask off (hide it): the contents draw whole" : "Turn the mask on",
1227
+ "aria-label": "Mask on",
1228
+ onClick: () => controller.setMaskOptions({ enabled: !g.enabled }, { ids }),
1229
+ children: g.enabled ? "On" : "Off"
1230
+ }
1231
+ )
1232
+ ] })
1233
+ ] })
1234
+ ] });
1235
+ }
1236
+
1237
+ // src/vector/VectorGradientEditor.tsx
1238
+ var import_vector8 = require("@pixerate/editor/vector");
1239
+ var import_jsx_runtime7 = require("react/jsx-runtime");
1240
+ var selectClass5 = "h-7 min-w-0 flex-1 rounded border border-border bg-background px-1 text-xs";
1097
1241
  var numberClass4 = "h-7 w-14 rounded border border-border bg-background px-1.5 text-xs";
1098
- var buttonClass4 = "inline-flex h-7 min-w-7 items-center justify-center rounded border border-border bg-background px-1.5 text-xs text-foreground hover:bg-muted disabled:cursor-not-allowed disabled:opacity-40 aria-pressed:bg-primary aria-pressed:text-primary-foreground";
1242
+ var buttonClass5 = "inline-flex h-7 min-w-7 items-center justify-center rounded border border-border bg-background px-1.5 text-xs text-foreground hover:bg-muted disabled:cursor-not-allowed disabled:opacity-40 aria-pressed:bg-primary aria-pressed:text-primary-foreground";
1099
1243
  var percent = (v) => Math.round(v * 1e3) / 10;
1100
1244
  function VectorGradientEditor({ controller, className }) {
1101
1245
  const state = useVectorEditorState(controller);
1102
- const ed = (0, import_vector7.getVectorGradientEditor)(state);
1246
+ const ed = (0, import_vector8.getVectorGradientEditor)(state);
1103
1247
  if (!ed) return null;
1104
1248
  const g = ed.gradient;
1105
1249
  const sel = ed.selected;
@@ -1107,26 +1251,26 @@ function VectorGradientEditor({ controller, className }) {
1107
1251
  const setType = (value) => {
1108
1252
  if (value === "solid") {
1109
1253
  const first = ed.stops[0];
1110
- controller.setStyle({ [ed.target]: (0, import_vector7.vectorPaintToHex)(first?.color) ?? first?.color ?? "#000000" }, void 0, { label: "Solid Colour" });
1254
+ controller.setStyle({ [ed.target]: (0, import_vector8.vectorPaintToHex)(first?.color) ?? first?.color ?? "#000000" }, void 0, { label: "Solid Colour" });
1111
1255
  } else controller.setGradient({ type: value });
1112
1256
  };
1113
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: `space-y-1.5 ${className ?? ""}`, "data-vector-gradient-editor": ed.target, "aria-label": "Gradient", children: [
1114
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex items-center gap-1.5", children: [
1115
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "w-16 text-xs text-foreground", children: "Gradient" }),
1116
- ["fill", "stroke"].map((t) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", className: buttonClass4, "aria-pressed": ed.target === t, onClick: () => controller.setGradientTarget(t), children: t === "fill" ? "Fill" : "Stroke" }, t)),
1117
- ed.mixed && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "text-[11px] text-muted-foreground", children: "Mixed" })
1257
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: `space-y-1.5 ${className ?? ""}`, "data-vector-gradient-editor": ed.target, "aria-label": "Gradient", children: [
1258
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex items-center gap-1.5", children: [
1259
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "w-16 text-xs text-foreground", children: "Gradient" }),
1260
+ ["fill", "stroke"].map((t) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("button", { type: "button", className: buttonClass5, "aria-pressed": ed.target === t, onClick: () => controller.setGradientTarget(t), children: t === "fill" ? "Fill" : "Stroke" }, t)),
1261
+ ed.mixed && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "text-[11px] text-muted-foreground", children: "Mixed" })
1118
1262
  ] }),
1119
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex items-center gap-1.5", children: [
1120
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "w-16 text-xs text-foreground", children: "Type" }),
1121
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("select", { "aria-label": "Gradient type", className: selectClass4, value: ed.type ?? "solid", onChange: (e) => setType(e.target.value), children: [
1122
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("option", { value: "solid", children: "Solid" }),
1123
- import_vector7.VECTOR_GRADIENT_TYPES.map((t) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("option", { value: t, children: import_vector7.VECTOR_GRADIENT_TYPE_LABELS[t] }, t))
1263
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex items-center gap-1.5", children: [
1264
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "w-16 text-xs text-foreground", children: "Type" }),
1265
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("select", { "aria-label": "Gradient type", className: selectClass5, value: ed.type ?? "solid", onChange: (e) => setType(e.target.value), children: [
1266
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("option", { value: "solid", children: "Solid" }),
1267
+ import_vector8.VECTOR_GRADIENT_TYPES.map((t) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("option", { value: t, children: import_vector8.VECTOR_GRADIENT_TYPE_LABELS[t] }, t))
1124
1268
  ] }),
1125
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", className: buttonClass4, "aria-label": "Reverse gradient", title: "Reverse gradient", disabled: !g, onClick: () => controller.reverseGradient(), children: "\u21C4" })
1269
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("button", { type: "button", className: buttonClass5, "aria-label": "Reverse gradient", title: "Reverse gradient", disabled: !g, onClick: () => controller.reverseGradient(), children: "\u21C4" })
1126
1270
  ] }),
1127
- g && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
1128
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "relative h-8", "data-vector-gradient-ramp": "", children: [
1129
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1271
+ g && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
1272
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "relative h-8", "data-vector-gradient-ramp": "", children: [
1273
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1130
1274
  "div",
1131
1275
  {
1132
1276
  role: "button",
@@ -1140,7 +1284,7 @@ function VectorGradientEditor({ controller, className }) {
1140
1284
  }
1141
1285
  }
1142
1286
  ),
1143
- ed.stops.map((s) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1287
+ ed.stops.map((s) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1144
1288
  "button",
1145
1289
  {
1146
1290
  type: "button",
@@ -1154,21 +1298,21 @@ function VectorGradientEditor({ controller, className }) {
1154
1298
  s.index
1155
1299
  ))
1156
1300
  ] }),
1157
- sel && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex flex-wrap items-center gap-1.5", "data-vector-gradient-selected-stop": sel.index, children: [
1158
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1301
+ sel && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex flex-wrap items-center gap-1.5", "data-vector-gradient-selected-stop": sel.index, children: [
1302
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1159
1303
  "input",
1160
1304
  {
1161
1305
  type: "color",
1162
1306
  "aria-label": "Stop colour",
1163
1307
  className: "h-7 w-9 cursor-pointer rounded border border-border bg-background",
1164
- value: (0, import_vector7.vectorPaintToHex)(sel.color) ?? "#000000",
1308
+ value: (0, import_vector8.vectorPaintToHex)(sel.color) ?? "#000000",
1165
1309
  onChange: (e) => controller.setGradientStop(sel.index, { color: e.target.value }, { merge: `gradient-stop-color-${sel.index}` }),
1166
1310
  onBlur: end
1167
1311
  }
1168
1312
  ),
1169
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("label", { className: "flex items-center gap-1 text-[11px] text-muted-foreground", children: [
1313
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("label", { className: "flex items-center gap-1 text-[11px] text-muted-foreground", children: [
1170
1314
  "Opacity",
1171
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1315
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1172
1316
  "input",
1173
1317
  {
1174
1318
  type: "number",
@@ -1185,9 +1329,9 @@ function VectorGradientEditor({ controller, className }) {
1185
1329
  }
1186
1330
  )
1187
1331
  ] }),
1188
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("label", { className: "flex items-center gap-1 text-[11px] text-muted-foreground", children: [
1332
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("label", { className: "flex items-center gap-1 text-[11px] text-muted-foreground", children: [
1189
1333
  "At",
1190
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1334
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1191
1335
  "input",
1192
1336
  {
1193
1337
  type: "number",
@@ -1204,9 +1348,9 @@ function VectorGradientEditor({ controller, className }) {
1204
1348
  }
1205
1349
  )
1206
1350
  ] }),
1207
- !sel.last && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("label", { className: "flex items-center gap-1 text-[11px] text-muted-foreground", children: [
1351
+ !sel.last && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("label", { className: "flex items-center gap-1 text-[11px] text-muted-foreground", children: [
1208
1352
  "Midpoint",
1209
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1353
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1210
1354
  "input",
1211
1355
  {
1212
1356
  type: "number",
@@ -1223,15 +1367,15 @@ function VectorGradientEditor({ controller, className }) {
1223
1367
  }
1224
1368
  )
1225
1369
  ] }),
1226
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", className: buttonClass4, "aria-label": "Delete stop", title: "Delete stop", disabled: ed.stops.length <= 2, onClick: () => controller.removeGradientStop(sel.index), children: "\u2715" })
1370
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("button", { type: "button", className: buttonClass5, "aria-label": "Delete stop", title: "Delete stop", disabled: ed.stops.length <= 2, onClick: () => controller.removeGradientStop(sel.index), children: "\u2715" })
1227
1371
  ] }),
1228
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("label", { className: "flex items-center gap-1.5 text-xs text-foreground", children: [
1229
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "w-16", children: "Colours" }),
1230
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("select", { "aria-label": "Gradient interpolation", className: selectClass4, value: ed.interpolation, onChange: (e) => controller.setGradient({ interpolation: e.target.value }), children: import_vector7.VECTOR_GRADIENT_INTERPOLATIONS.map((i) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("option", { value: i, children: import_vector7.VECTOR_GRADIENT_INTERPOLATION_LABELS[i] }, i)) })
1372
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("label", { className: "flex items-center gap-1.5 text-xs text-foreground", children: [
1373
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "w-16", children: "Colours" }),
1374
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("select", { "aria-label": "Gradient interpolation", className: selectClass5, value: ed.interpolation, onChange: (e) => controller.setGradient({ interpolation: e.target.value }), children: import_vector8.VECTOR_GRADIENT_INTERPOLATIONS.map((i) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("option", { value: i, children: import_vector8.VECTOR_GRADIENT_INTERPOLATION_LABELS[i] }, i)) })
1231
1375
  ] }),
1232
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("label", { className: "flex items-center gap-1.5 text-xs text-foreground", children: [
1233
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "w-16", children: "Smooth" }),
1234
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1376
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("label", { className: "flex items-center gap-1.5 text-xs text-foreground", children: [
1377
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "w-16", children: "Smooth" }),
1378
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1235
1379
  "input",
1236
1380
  {
1237
1381
  type: "range",
@@ -1247,26 +1391,26 @@ function VectorGradientEditor({ controller, className }) {
1247
1391
  }
1248
1392
  )
1249
1393
  ] }),
1250
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("label", { className: "flex items-center gap-1.5 text-xs text-foreground", children: [
1251
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "w-16", children: "Spread" }),
1252
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("select", { "aria-label": "Gradient spread", className: selectClass4, value: ed.spread, onChange: (e) => controller.setGradient({ spread: e.target.value }), children: import_vector7.VECTOR_GRADIENT_SPREADS.map((sp) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("option", { value: sp, children: import_vector7.VECTOR_GRADIENT_SPREAD_LABELS[sp] }, sp)) })
1394
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("label", { className: "flex items-center gap-1.5 text-xs text-foreground", children: [
1395
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "w-16", children: "Spread" }),
1396
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("select", { "aria-label": "Gradient spread", className: selectClass5, value: ed.spread, onChange: (e) => controller.setGradient({ spread: e.target.value }), children: import_vector8.VECTOR_GRADIENT_SPREADS.map((sp) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("option", { value: sp, children: import_vector8.VECTOR_GRADIENT_SPREAD_LABELS[sp] }, sp)) })
1253
1397
  ] }),
1254
- ed.target === "stroke" && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("label", { className: "flex items-center gap-1.5 text-xs text-foreground", children: [
1255
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "w-16", children: "On stroke" }),
1256
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("select", { "aria-label": "Gradient on stroke", className: selectClass4, value: ed.strokeMode, onChange: (e) => controller.setGradient({ strokeMode: e.target.value }), children: import_vector7.VECTOR_STROKE_GRADIENT_MODES.map((m) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("option", { value: m, children: import_vector7.VECTOR_STROKE_GRADIENT_MODE_LABELS[m] }, m)) })
1398
+ ed.target === "stroke" && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("label", { className: "flex items-center gap-1.5 text-xs text-foreground", children: [
1399
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "w-16", children: "On stroke" }),
1400
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("select", { "aria-label": "Gradient on stroke", className: selectClass5, value: ed.strokeMode, onChange: (e) => controller.setGradient({ strokeMode: e.target.value }), children: import_vector8.VECTOR_STROKE_GRADIENT_MODES.map((m) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("option", { value: m, children: import_vector8.VECTOR_STROKE_GRADIENT_MODE_LABELS[m] }, m)) })
1257
1401
  ] })
1258
1402
  ] })
1259
1403
  ] });
1260
1404
  }
1261
1405
 
1262
1406
  // src/vector/VectorToolbar.tsx
1263
- var import_jsx_runtime7 = require("react/jsx-runtime");
1407
+ var import_jsx_runtime8 = require("react/jsx-runtime");
1264
1408
  var VECTOR_SNAP_TITLE = "Snap to objects";
1265
1409
  var modKeyLabel = () => typeof navigator !== "undefined" && /Mac|iPhone|iPad/.test(navigator.platform ?? "") ? "\u2318" : "Ctrl";
1266
- var buttonClass5 = "inline-flex h-8 min-w-8 items-center justify-center rounded-md border border-border bg-background px-2 text-xs font-medium text-foreground hover:bg-muted disabled:cursor-not-allowed disabled:opacity-40 aria-pressed:bg-primary aria-pressed:text-primary-foreground";
1410
+ var buttonClass6 = "inline-flex h-8 min-w-8 items-center justify-center rounded-md border border-border bg-background px-2 text-xs font-medium text-foreground hover:bg-muted disabled:cursor-not-allowed disabled:opacity-40 aria-pressed:bg-primary aria-pressed:text-primary-foreground";
1267
1411
  function VectorToolbar({
1268
1412
  controller,
1269
- tools = import_vector8.VECTOR_TOOLS,
1413
+ tools = import_vector9.VECTOR_TOOLS,
1270
1414
  showHistory = true,
1271
1415
  showArrange = true,
1272
1416
  showSnap = true,
@@ -1280,34 +1424,35 @@ function VectorToolbar({
1280
1424
  const points = state.tool === "direct" ? state.selectedAnchors.length : 0;
1281
1425
  const editing = state.editing;
1282
1426
  const groupSelected = state.selection.some((id) => controller.getNode(id)?.type === "group");
1427
+ const maskSelected = state.selection.some((id) => (0, import_vector9.isVectorMaskGroup)(controller.getNode(id)));
1283
1428
  const [live, setLive] = import_react5.default.useState(false);
1284
1429
  const booleanDisabled = live ? count < 1 : count < 2;
1285
- const act = (label, title, run, disabled = false) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("button", { type: "button", className: buttonClass5, title, "aria-label": title, disabled, onClick: () => void run(), children: label }, label);
1286
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { role: "toolbar", "aria-label": "Vector tools", className: `flex flex-wrap items-center gap-1 ${className ?? ""}`, children: [
1287
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "flex items-center gap-1", role: "group", "aria-label": "Tools", children: tools.map((tool) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1430
+ const act = (label, title, run, disabled = false) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("button", { type: "button", className: buttonClass6, title, "aria-label": title, disabled, onClick: () => void run(), children: label }, label);
1431
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { role: "toolbar", "aria-label": "Vector tools", className: `flex flex-wrap items-center gap-1 ${className ?? ""}`, children: [
1432
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "flex items-center gap-1", role: "group", "aria-label": "Tools", children: tools.map((tool) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1288
1433
  "button",
1289
1434
  {
1290
1435
  type: "button",
1291
1436
  "data-vector-tool": tool,
1292
- className: buttonClass5,
1437
+ className: buttonClass6,
1293
1438
  "aria-pressed": state.tool === tool,
1294
- title: `${(0, import_vector8.vectorToolLabel)(tool)} (${import_vector8.VECTOR_TOOL_SHORTCUTS[tool]})`,
1295
- "aria-label": (0, import_vector8.vectorToolLabel)(tool),
1439
+ title: `${(0, import_vector9.vectorToolLabel)(tool)} (${import_vector9.VECTOR_TOOL_SHORTCUTS[tool]})`,
1440
+ "aria-label": (0, import_vector9.vectorToolLabel)(tool),
1296
1441
  onClick: () => controller.setTool(tool),
1297
- children: import_vector8.VECTOR_TOOL_GLYPHS[tool]
1442
+ children: import_vector9.VECTOR_TOOL_GLYPHS[tool]
1298
1443
  },
1299
1444
  tool
1300
1445
  )) }),
1301
- showHistory && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "History", children: [
1446
+ showHistory && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "History", children: [
1302
1447
  act("\u21B6", state.canUndo ? `Undo ${state.undoLabel}` : "Undo", () => controller.undo(), !state.canUndo),
1303
1448
  act("\u21B7", state.canRedo ? `Redo ${state.redoLabel}` : "Redo", () => controller.redo(), !state.canRedo)
1304
1449
  ] }),
1305
- showSnap && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Snapping", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1450
+ showSnap && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Snapping", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1306
1451
  "button",
1307
1452
  {
1308
1453
  type: "button",
1309
1454
  "data-vector-snap": "",
1310
- className: buttonClass5,
1455
+ className: buttonClass6,
1311
1456
  "aria-pressed": state.snap.objects,
1312
1457
  title: `${VECTOR_SNAP_TITLE} (hold ${modKeyLabel()} to bypass)`,
1313
1458
  "aria-label": VECTOR_SNAP_TITLE,
@@ -1315,13 +1460,13 @@ function VectorToolbar({
1315
1460
  children: "Snap"
1316
1461
  }
1317
1462
  ) }),
1318
- showPathEdit && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Path editing", children: [
1319
- import_vector8.VECTOR_PATH_ACTIONS.map((a) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1463
+ showPathEdit && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Path editing", children: [
1464
+ import_vector9.VECTOR_PATH_ACTIONS.map((a) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1320
1465
  "button",
1321
1466
  {
1322
1467
  type: "button",
1323
1468
  "data-vector-path-action": a.id,
1324
- className: buttonClass5,
1469
+ className: buttonClass6,
1325
1470
  title: a.shortcut ? `${a.title} (${modKeyLabel()}+${a.shortcut})` : a.title,
1326
1471
  "aria-label": a.title,
1327
1472
  disabled: !a.enabled(count, points),
@@ -1330,12 +1475,12 @@ function VectorToolbar({
1330
1475
  },
1331
1476
  a.id
1332
1477
  )),
1333
- import_vector8.VECTOR_EDIT_TOGGLES.map((t) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1478
+ import_vector9.VECTOR_EDIT_TOGGLES.map((t) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1334
1479
  "button",
1335
1480
  {
1336
1481
  type: "button",
1337
1482
  "data-vector-edit": t.key,
1338
- className: buttonClass5,
1483
+ className: buttonClass6,
1339
1484
  "aria-pressed": editing[t.key],
1340
1485
  title: t.title,
1341
1486
  "aria-label": t.title,
@@ -1344,9 +1489,9 @@ function VectorToolbar({
1344
1489
  },
1345
1490
  t.key
1346
1491
  )),
1347
- state.tool === "pencil" && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("label", { className: "flex items-center gap-1 text-xs text-foreground", children: [
1492
+ state.tool === "pencil" && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("label", { className: "flex items-center gap-1 text-xs text-foreground", children: [
1348
1493
  "Smoothing",
1349
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1494
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1350
1495
  "input",
1351
1496
  {
1352
1497
  type: "range",
@@ -1360,9 +1505,9 @@ function VectorToolbar({
1360
1505
  }
1361
1506
  )
1362
1507
  ] }),
1363
- state.tool === "eraser" && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("label", { className: "flex items-center gap-1 text-xs text-foreground", children: [
1508
+ state.tool === "eraser" && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("label", { className: "flex items-center gap-1 text-xs text-foreground", children: [
1364
1509
  "Size",
1365
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1510
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1366
1511
  "input",
1367
1512
  {
1368
1513
  type: "number",
@@ -1378,26 +1523,26 @@ function VectorToolbar({
1378
1523
  }
1379
1524
  )
1380
1525
  ] }),
1381
- state.tool === "paintBucket" && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1526
+ state.tool === "paintBucket" && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1382
1527
  "button",
1383
1528
  {
1384
1529
  type: "button",
1385
1530
  "data-vector-fill-gap": "",
1386
- className: buttonClass5,
1531
+ className: buttonClass6,
1387
1532
  "aria-pressed": editing.fillGap > 0,
1388
1533
  title: "Close small gaps between path ends when filling",
1389
1534
  "aria-label": "Close gaps",
1390
- onClick: () => controller.setEditing({ fillGap: editing.fillGap > 0 ? 0 : import_vector8.VECTOR_FILL_GAP }),
1535
+ onClick: () => controller.setEditing({ fillGap: editing.fillGap > 0 ? 0 : import_vector9.VECTOR_FILL_GAP }),
1391
1536
  children: "Gaps"
1392
1537
  }
1393
1538
  )
1394
1539
  ] }),
1395
- showDistort && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Distort", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1540
+ showDistort && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Distort", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1396
1541
  "button",
1397
1542
  {
1398
1543
  type: "button",
1399
1544
  "data-vector-free-distort": "",
1400
- className: buttonClass5,
1545
+ className: buttonClass6,
1401
1546
  "aria-pressed": state.freeDistort,
1402
1547
  title: `Free Distort: drag a corner of the selection box to distort in perspective (Shift+Alt+${modKeyLabel()}: symmetric perspective; ${modKeyLabel()}-drag a side to skew)`,
1403
1548
  "aria-label": "Free Distort",
@@ -1405,15 +1550,15 @@ function VectorToolbar({
1405
1550
  children: "Distort"
1406
1551
  }
1407
1552
  ) }),
1408
- showBlend && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(VectorBlendControls, { controller }),
1409
- showArrange && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
1410
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Combine", children: [
1411
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1553
+ showBlend && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(VectorBlendControls, { controller }),
1554
+ showArrange && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
1555
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Combine", children: [
1556
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1412
1557
  "button",
1413
1558
  {
1414
1559
  type: "button",
1415
1560
  "data-vector-live-boolean": "",
1416
- className: buttonClass5,
1561
+ className: buttonClass6,
1417
1562
  "aria-pressed": live,
1418
1563
  title: "Live booleans: keep the shapes editable",
1419
1564
  "aria-label": "Live booleans",
@@ -1426,14 +1571,16 @@ function VectorToolbar({
1426
1571
  act("\u2229", "Intersect", () => controller.booleanOp("intersect", { live }), booleanDisabled),
1427
1572
  act("\u22BB", "Exclude", () => controller.booleanOp("exclude", { live }), booleanDisabled),
1428
1573
  act("G", "Group", () => controller.group(), count < 1),
1429
- act("U", "Ungroup", () => controller.ungroup(), !groupSelected)
1574
+ act("U", "Ungroup", () => controller.ungroup(), !groupSelected),
1575
+ act("\u25D0", `Make Clipping Mask (${modKeyLabel()}+7)`, () => controller.makeMask("clip"), count < 2),
1576
+ act("\u25D1", `Release Mask (${modKeyLabel()}+Alt+7)`, () => controller.releaseMask(), !maskSelected)
1430
1577
  ] }),
1431
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Pathfinder", children: import_vector8.VECTOR_PATHFINDER_ACTIONS.map((a) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1578
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Pathfinder", children: import_vector9.VECTOR_PATHFINDER_ACTIONS.map((a) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1432
1579
  "button",
1433
1580
  {
1434
1581
  type: "button",
1435
1582
  "data-vector-pathfinder": a.id,
1436
- className: buttonClass5,
1583
+ className: buttonClass6,
1437
1584
  title: a.title,
1438
1585
  "aria-label": a.title,
1439
1586
  disabled: count < a.min,
@@ -1442,7 +1589,7 @@ function VectorToolbar({
1442
1589
  },
1443
1590
  a.id
1444
1591
  )) }),
1445
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Arrange", children: [
1592
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Arrange", children: [
1446
1593
  act("\u21C8", "Bring to Front", () => controller.arrange("front"), count < 1),
1447
1594
  act("\u21CA", "Send to Back", () => controller.arrange("back"), count < 1),
1448
1595
  act("\u21E4", "Align Left", () => controller.align("left"), count < 1),
@@ -1457,22 +1604,22 @@ function VectorToolbar({
1457
1604
  }
1458
1605
  function VectorBlendControls({ controller }) {
1459
1606
  const state = useVectorEditorState(controller);
1460
- const blend = (0, import_vector8.getVectorBlendControls)(state);
1607
+ const blend = (0, import_vector9.getVectorBlendControls)(state);
1461
1608
  const o = blend.options;
1462
1609
  const selected = blend.ids.length > 0;
1463
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Blend", "data-vector-blend-controls": "", children: [
1464
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("button", { type: "button", "data-vector-blend-action": "make", className: buttonClass5, title: "Make Blend", "aria-label": "Make Blend", disabled: !blend.canBlend, onClick: () => controller.blend(), children: "\u29D3" }),
1465
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1610
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Blend", "data-vector-blend-controls": "", children: [
1611
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("button", { type: "button", "data-vector-blend-action": "make", className: buttonClass6, title: "Make Blend", "aria-label": "Make Blend", disabled: !blend.canBlend, onClick: () => controller.blend(), children: "\u29D3" }),
1612
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1466
1613
  "select",
1467
1614
  {
1468
1615
  "aria-label": "Blend spacing",
1469
1616
  className: "h-7 rounded border border-border bg-background px-1 text-xs",
1470
1617
  value: o.spacing,
1471
- onChange: (e) => (0, import_vector8.applyVectorBlendOptions)(controller, { spacing: e.target.value }),
1472
- children: import_vector8.VECTOR_BLEND_SPACINGS.map((sp) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("option", { value: sp, children: import_vector8.VECTOR_BLEND_SPACING_LABELS[sp] }, sp))
1618
+ onChange: (e) => (0, import_vector9.applyVectorBlendOptions)(controller, { spacing: e.target.value }),
1619
+ children: import_vector9.VECTOR_BLEND_SPACINGS.map((sp) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("option", { value: sp, children: import_vector9.VECTOR_BLEND_SPACING_LABELS[sp] }, sp))
1473
1620
  }
1474
1621
  ),
1475
- o.spacing !== "smoothColor" && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1622
+ o.spacing !== "smoothColor" && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1476
1623
  "input",
1477
1624
  {
1478
1625
  type: "number",
@@ -1484,29 +1631,29 @@ function VectorBlendControls({ controller }) {
1484
1631
  onChange: (e) => {
1485
1632
  const n = parseFloat(e.target.value);
1486
1633
  if (!Number.isFinite(n) || n <= 0) return;
1487
- (0, import_vector8.applyVectorBlendOptions)(controller, o.spacing === "steps" ? { steps: n } : { distance: n });
1634
+ (0, import_vector9.applyVectorBlendOptions)(controller, o.spacing === "steps" ? { steps: n } : { distance: n });
1488
1635
  }
1489
1636
  }
1490
1637
  ),
1491
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1638
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1492
1639
  "button",
1493
1640
  {
1494
1641
  type: "button",
1495
1642
  "data-vector-blend-action": "orient",
1496
- className: buttonClass5,
1643
+ className: buttonClass6,
1497
1644
  "aria-pressed": o.orientToSpine,
1498
1645
  title: "Orient steps to the spine",
1499
1646
  "aria-label": "Orient to spine",
1500
- onClick: () => (0, import_vector8.applyVectorBlendOptions)(controller, { orientToSpine: !o.orientToSpine }),
1647
+ onClick: () => (0, import_vector9.applyVectorBlendOptions)(controller, { orientToSpine: !o.orientToSpine }),
1501
1648
  children: "\u293F"
1502
1649
  }
1503
1650
  ),
1504
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1651
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1505
1652
  "button",
1506
1653
  {
1507
1654
  type: "button",
1508
1655
  "data-vector-blend-action": "spine",
1509
- className: buttonClass5,
1656
+ className: buttonClass6,
1510
1657
  title: "Replace Spine (select a blend and a path)",
1511
1658
  "aria-label": "Replace Spine",
1512
1659
  disabled: !blend.canReplaceSpine,
@@ -1514,15 +1661,15 @@ function VectorBlendControls({ controller }) {
1514
1661
  children: "\u2307"
1515
1662
  }
1516
1663
  ),
1517
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("button", { type: "button", "data-vector-blend-action": "expand", className: buttonClass5, title: "Expand Blend", "aria-label": "Expand Blend", disabled: !selected, onClick: () => controller.expandBlend(), children: "\u29C9" }),
1518
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("button", { type: "button", "data-vector-blend-action": "release", className: buttonClass5, title: "Release Blend", "aria-label": "Release Blend", disabled: !selected, onClick: () => controller.releaseBlend(), children: "\u29C4" })
1664
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("button", { type: "button", "data-vector-blend-action": "expand", className: buttonClass6, title: "Expand Blend", "aria-label": "Expand Blend", disabled: !selected, onClick: () => controller.expandBlend(), children: "\u29C9" }),
1665
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("button", { type: "button", "data-vector-blend-action": "release", className: buttonClass6, title: "Release Blend", "aria-label": "Release Blend", disabled: !selected, onClick: () => controller.releaseBlend(), children: "\u29C4" })
1519
1666
  ] });
1520
1667
  }
1521
- var selectClass5 = "h-7 min-w-0 flex-1 rounded border border-border bg-background px-1 text-xs";
1668
+ var selectClass6 = "h-7 min-w-0 flex-1 rounded border border-border bg-background px-1 text-xs";
1522
1669
  var numberClass5 = "h-7 w-16 rounded border border-border bg-background px-1.5 text-xs";
1523
1670
  function VectorStylePanel({ controller, className }) {
1524
1671
  const state = useVectorEditorState(controller);
1525
- const style = (0, import_vector8.getVectorSelectionStyle)(state);
1672
+ const style = (0, import_vector9.getVectorSelectionStyle)(state);
1526
1673
  const d = state.defaults;
1527
1674
  const source = style ?? {
1528
1675
  fill: d.fill ?? null,
@@ -1535,7 +1682,7 @@ function VectorStylePanel({ controller, className }) {
1535
1682
  arrowAlign: d.arrowAlign ?? "extend",
1536
1683
  startArrowScale: d.startArrowScale ?? 1,
1537
1684
  endArrowScale: d.endArrowScale ?? 1,
1538
- widthProfile: (0, import_vector8.widthProfilePresetId)(d.strokeWidthProfile),
1685
+ widthProfile: (0, import_vector9.widthProfilePresetId)(d.strokeWidthProfile),
1539
1686
  cornerRadius: d.cornerRadius,
1540
1687
  cornerKind: d.cornerKind ?? "round",
1541
1688
  corners: 1,
@@ -1548,19 +1695,19 @@ function VectorStylePanel({ controller, className }) {
1548
1695
  };
1549
1696
  const setProfile = (id) => {
1550
1697
  if (style) controller.setWidthProfile(id);
1551
- else controller.setDefaults({ strokeWidthProfile: (0, import_vector8.widthProfilePreset)(id)?.length ? (0, import_vector8.widthProfilePreset)(id) : null });
1698
+ else controller.setDefaults({ strokeWidthProfile: (0, import_vector9.widthProfilePreset)(id)?.length ? (0, import_vector9.widthProfilePreset)(id) : null });
1552
1699
  };
1553
- const mixed = (key) => style?.mixed[key] ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "text-[11px] text-muted-foreground", children: "Mixed" }) : null;
1700
+ const mixed = (key) => style?.mixed[key] ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "text-[11px] text-muted-foreground", children: "Mixed" }) : null;
1554
1701
  const arrowRow = (end, label) => {
1555
1702
  const key = end === "start" ? "startArrow" : "endArrow";
1556
1703
  const scaleKey = end === "start" ? "startArrowScale" : "endArrowScale";
1557
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex items-center gap-2", "data-vector-style": key, children: [
1558
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "w-16 text-xs text-foreground", children: label }),
1559
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("select", { "aria-label": `${label} arrowhead`, className: selectClass5, value: source[key] ?? "", onChange: (e) => apply({ [key]: e.target.value || null }), children: [
1560
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("option", { value: "", children: "None" }),
1561
- import_vector8.VECTOR_ARROWHEADS.map((h) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("option", { value: h, children: import_vector8.VECTOR_ARROWHEAD_LABELS[h] }, h))
1704
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex items-center gap-2", "data-vector-style": key, children: [
1705
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "w-16 text-xs text-foreground", children: label }),
1706
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("select", { "aria-label": `${label} arrowhead`, className: selectClass6, value: source[key] ?? "", onChange: (e) => apply({ [key]: e.target.value || null }), children: [
1707
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("option", { value: "", children: "None" }),
1708
+ import_vector9.VECTOR_ARROWHEADS.map((h) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("option", { value: h, children: import_vector9.VECTOR_ARROWHEAD_LABELS[h] }, h))
1562
1709
  ] }),
1563
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1710
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1564
1711
  "input",
1565
1712
  {
1566
1713
  type: "number",
@@ -1584,14 +1731,14 @@ function VectorStylePanel({ controller, className }) {
1584
1731
  };
1585
1732
  const paintRow = (key, label) => {
1586
1733
  const paint = source[key];
1587
- const hex = (0, import_vector8.vectorPaintToHex)(paint);
1734
+ const hex = (0, import_vector9.vectorPaintToHex)(paint);
1588
1735
  const on = paint !== null && paint !== "none";
1589
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex items-center gap-2", "data-vector-style": key, children: [
1590
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("label", { className: "flex w-16 items-center gap-1.5 text-xs text-foreground", children: [
1591
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("input", { type: "checkbox", checked: on, "aria-label": `${label} on`, onChange: (e) => apply({ [key]: e.target.checked ? hex ?? "#000000" : null }) }),
1736
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex items-center gap-2", "data-vector-style": key, children: [
1737
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("label", { className: "flex w-16 items-center gap-1.5 text-xs text-foreground", children: [
1738
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("input", { type: "checkbox", checked: on, "aria-label": `${label} on`, onChange: (e) => apply({ [key]: e.target.checked ? hex ?? "#000000" : null }) }),
1592
1739
  label
1593
1740
  ] }),
1594
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1741
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1595
1742
  "input",
1596
1743
  {
1597
1744
  type: "color",
@@ -1602,18 +1749,18 @@ function VectorStylePanel({ controller, className }) {
1602
1749
  onChange: (e) => apply({ [key]: e.target.value })
1603
1750
  }
1604
1751
  ),
1605
- on && !hex && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "truncate text-[11px] text-muted-foreground", children: typeof paint === "string" ? paint : "Gradient" }),
1606
- style?.mixed[key] && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "text-[11px] text-muted-foreground", children: "Mixed" })
1752
+ on && !hex && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "truncate text-[11px] text-muted-foreground", children: typeof paint === "string" ? paint : "Gradient" }),
1753
+ style?.mixed[key] && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "text-[11px] text-muted-foreground", children: "Mixed" })
1607
1754
  ] });
1608
1755
  };
1609
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: `space-y-2 ${className ?? ""}`, "aria-label": style ? "Selection style" : "New shape style", children: [
1610
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("p", { className: "text-[11px] font-medium uppercase tracking-wide text-muted-foreground", children: style ? "Selection" : "New shapes" }),
1756
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: `space-y-2 ${className ?? ""}`, "aria-label": style ? "Selection style" : "New shape style", children: [
1757
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { className: "text-[11px] font-medium uppercase tracking-wide text-muted-foreground", children: style ? "Selection" : "New shapes" }),
1611
1758
  paintRow("fill", "Fill"),
1612
1759
  paintRow("stroke", "Stroke"),
1613
- style && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(VectorGradientEditor, { controller }),
1614
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("label", { className: "flex items-center gap-2 text-xs text-foreground", children: [
1615
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "w-16", children: "Width" }),
1616
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1760
+ style && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(VectorGradientEditor, { controller }),
1761
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("label", { className: "flex items-center gap-2 text-xs text-foreground", children: [
1762
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "w-16", children: "Width" }),
1763
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1617
1764
  "input",
1618
1765
  {
1619
1766
  type: "number",
@@ -1629,33 +1776,33 @@ function VectorStylePanel({ controller, className }) {
1629
1776
  }
1630
1777
  )
1631
1778
  ] }),
1632
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("label", { className: "flex items-center gap-2 text-xs text-foreground", "data-vector-style": "strokeAlign", children: [
1633
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "w-16", children: "Align" }),
1634
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("select", { "aria-label": "Stroke alignment", className: selectClass5, value: source.strokeAlign, onChange: (e) => apply({ strokeAlign: e.target.value }), children: Object.keys(import_vector8.VECTOR_STROKE_ALIGN_LABELS).map((a) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("option", { value: a, children: import_vector8.VECTOR_STROKE_ALIGN_LABELS[a] }, a)) }),
1779
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("label", { className: "flex items-center gap-2 text-xs text-foreground", "data-vector-style": "strokeAlign", children: [
1780
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "w-16", children: "Align" }),
1781
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("select", { "aria-label": "Stroke alignment", className: selectClass6, value: source.strokeAlign, onChange: (e) => apply({ strokeAlign: e.target.value }), children: Object.keys(import_vector9.VECTOR_STROKE_ALIGN_LABELS).map((a) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("option", { value: a, children: import_vector9.VECTOR_STROKE_ALIGN_LABELS[a] }, a)) }),
1635
1782
  mixed("strokeAlign")
1636
1783
  ] }),
1637
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex items-center gap-2 text-xs text-foreground", "data-vector-style": "widthProfile", children: [
1638
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "w-16", children: "Profile" }),
1639
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
1784
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex items-center gap-2 text-xs text-foreground", "data-vector-style": "widthProfile", children: [
1785
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "w-16", children: "Profile" }),
1786
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
1640
1787
  "select",
1641
1788
  {
1642
1789
  "aria-label": "Width profile",
1643
- className: selectClass5,
1790
+ className: selectClass6,
1644
1791
  value: source.widthProfile,
1645
1792
  onChange: (e) => {
1646
1793
  if (e.target.value !== "custom") setProfile(e.target.value);
1647
1794
  },
1648
1795
  children: [
1649
- import_vector8.VECTOR_WIDTH_PRESETS.map((p) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("option", { value: p.id, children: p.label }, p.id)),
1650
- source.widthProfile === "custom" && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("option", { value: "custom", disabled: true, children: "Custom" })
1796
+ import_vector9.VECTOR_WIDTH_PRESETS.map((p) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("option", { value: p.id, children: p.label }, p.id)),
1797
+ source.widthProfile === "custom" && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("option", { value: "custom", disabled: true, children: "Custom" })
1651
1798
  ]
1652
1799
  }
1653
1800
  ),
1654
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1801
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1655
1802
  "button",
1656
1803
  {
1657
1804
  type: "button",
1658
- className: buttonClass5,
1805
+ className: buttonClass6,
1659
1806
  title: "Reset to uniform width",
1660
1807
  "aria-label": "Reset to uniform width",
1661
1808
  disabled: source.widthProfile === "uniform" && !style?.mixed.widthProfile,
@@ -1665,20 +1812,20 @@ function VectorStylePanel({ controller, className }) {
1665
1812
  ),
1666
1813
  mixed("widthProfile")
1667
1814
  ] }),
1668
- source.hasOpenPath && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
1815
+ source.hasOpenPath && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
1669
1816
  arrowRow("start", "Start"),
1670
1817
  arrowRow("end", "End"),
1671
- (source.startArrow || source.endArrow) && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("label", { className: "flex items-center gap-2 text-xs text-foreground", "data-vector-style": "arrowAlign", children: [
1672
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "w-16", children: "Heads" }),
1673
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("select", { "aria-label": "Arrowhead alignment", className: selectClass5, value: source.arrowAlign, onChange: (e) => apply({ arrowAlign: e.target.value }), children: [
1674
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("option", { value: "extend", children: "Tip past end" }),
1675
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("option", { value: "tip", children: "Tip at end" })
1818
+ (source.startArrow || source.endArrow) && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("label", { className: "flex items-center gap-2 text-xs text-foreground", "data-vector-style": "arrowAlign", children: [
1819
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "w-16", children: "Heads" }),
1820
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("select", { "aria-label": "Arrowhead alignment", className: selectClass6, value: source.arrowAlign, onChange: (e) => apply({ arrowAlign: e.target.value }), children: [
1821
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("option", { value: "extend", children: "Tip past end" }),
1822
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("option", { value: "tip", children: "Tip at end" })
1676
1823
  ] })
1677
1824
  ] })
1678
1825
  ] }),
1679
- source.corners > 0 && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex items-center gap-2", "data-vector-style": "corners", children: [
1680
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "w-16 text-xs text-foreground", children: "Corners" }),
1681
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1826
+ source.corners > 0 && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex items-center gap-2", "data-vector-style": "corners", children: [
1827
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "w-16 text-xs text-foreground", children: "Corners" }),
1828
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1682
1829
  "input",
1683
1830
  {
1684
1831
  type: "number",
@@ -1693,12 +1840,12 @@ function VectorStylePanel({ controller, className }) {
1693
1840
  }
1694
1841
  }
1695
1842
  ),
1696
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("select", { "aria-label": "Corner kind", className: selectClass5, value: source.cornerKind, onChange: (e) => setCorners({ kind: e.target.value }), children: import_vector8.VECTOR_CORNER_KINDS.map((k) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("option", { value: k, children: import_vector8.VECTOR_CORNER_KIND_LABELS[k] }, k)) }),
1843
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("select", { "aria-label": "Corner kind", className: selectClass6, value: source.cornerKind, onChange: (e) => setCorners({ kind: e.target.value }), children: import_vector9.VECTOR_CORNER_KINDS.map((k) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("option", { value: k, children: import_vector9.VECTOR_CORNER_KIND_LABELS[k] }, k)) }),
1697
1844
  mixed("cornerRadius")
1698
1845
  ] }),
1699
- style && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("label", { className: "flex items-center gap-2 text-xs text-foreground", children: [
1700
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "w-16", children: "Opacity" }),
1701
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1846
+ style && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("label", { className: "flex items-center gap-2 text-xs text-foreground", children: [
1847
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "w-16", children: "Opacity" }),
1848
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1702
1849
  "input",
1703
1850
  {
1704
1851
  type: "range",
@@ -1712,20 +1859,21 @@ function VectorStylePanel({ controller, className }) {
1712
1859
  }
1713
1860
  )
1714
1861
  ] }),
1715
- style && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(VectorAppearancePanel, { controller }),
1716
- style && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(VectorEffectsPanel, { controller }),
1717
- style && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(VectorEnvelopePanel, { controller })
1862
+ style && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(VectorAppearancePanel, { controller }),
1863
+ style && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(VectorEffectsPanel, { controller }),
1864
+ style && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(VectorEnvelopePanel, { controller }),
1865
+ style && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(VectorMaskPanel, { controller })
1718
1866
  ] });
1719
1867
  }
1720
1868
 
1721
1869
  // src/vector/VectorEditor.tsx
1722
- var import_jsx_runtime8 = require("react/jsx-runtime");
1870
+ var import_jsx_runtime9 = require("react/jsx-runtime");
1723
1871
  function VectorEditor({ controller, tools, showStylePanel = true, height = 520, canvasProps, className }) {
1724
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: `flex flex-col gap-2 ${className ?? ""}`, "data-vector-editor": "", children: [
1725
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(VectorToolbar, { controller, tools }),
1726
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex min-h-0 flex-col gap-2 md:flex-row", children: [
1727
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "relative min-h-[320px] flex-1 overflow-hidden rounded-md border border-border bg-muted/40", style: { height }, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(VectorCanvas, { controller, ...canvasProps }) }),
1728
- showStylePanel && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(VectorStylePanel, { controller, className: "w-full shrink-0 rounded-md border border-border p-3 md:w-56" })
1872
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: `flex flex-col gap-2 ${className ?? ""}`, "data-vector-editor": "", children: [
1873
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(VectorToolbar, { controller, tools }),
1874
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "flex min-h-0 flex-col gap-2 md:flex-row", children: [
1875
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "relative min-h-[320px] flex-1 overflow-hidden rounded-md border border-border bg-muted/40", style: { height }, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(VectorCanvas, { controller, ...canvasProps }) }),
1876
+ showStylePanel && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(VectorStylePanel, { controller, className: "w-full shrink-0 rounded-md border border-border p-3 md:w-56" })
1729
1877
  ] })
1730
1878
  ] });
1731
1879
  }
@@ -1740,6 +1888,8 @@ function VectorEditor({ controller, tools, showStylePanel = true, height = 520,
1740
1888
  VectorGradientAnnotator,
1741
1889
  VectorGradientDefs,
1742
1890
  VectorGradientEditor,
1891
+ VectorMaskPanel,
1892
+ VectorMaskedNodes,
1743
1893
  VectorSceneNodes,
1744
1894
  VectorStylePanel,
1745
1895
  VectorToolbar,