@pixerate/editor-react 0.82.0 → 0.84.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.
@@ -42,8 +42,10 @@ __export(vector_exports, {
42
42
  VectorGradientAnnotator: () => VectorGradientAnnotator,
43
43
  VectorGradientDefs: () => VectorGradientDefs,
44
44
  VectorGradientEditor: () => VectorGradientEditor,
45
+ VectorLensPanel: () => VectorLensPanel,
45
46
  VectorMaskPanel: () => VectorMaskPanel,
46
47
  VectorMaskedNodes: () => VectorMaskedNodes,
48
+ VectorPerspectivePanel: () => VectorPerspectivePanel,
47
49
  VectorProviderEffectsPanel: () => VectorProviderEffectsPanel,
48
50
  VectorSceneNodes: () => VectorSceneNodes,
49
51
  VectorShadingPanel: () => VectorShadingPanel,
@@ -693,6 +695,11 @@ function VectorCanvas({
693
695
  sh.lines.map((d, i) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d, stroke: accentColor, strokeWidth: 1, opacity: 0.7 }, i)),
694
696
  sh.points.map((p) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { "data-vector-shading-point": p.index, "data-selected": p.selected || void 0, cx: p.x, cy: p.y, r: p.selected ? 6 : 4.5, fill: p.color, stroke: p.selected ? accentColor : "#ffffff", strokeWidth: p.selected ? 2 : 1.5 }, p.index))
695
697
  ] }, `shading:${sh.nodeId}`)),
698
+ overlay.perspective && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("g", { "data-vector-perspective-grid": "", fill: "none", pointerEvents: "none", children: [
699
+ overlay.perspective.planes.map((pl) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { "data-vector-perspective-plane": pl.plane, "data-active": pl.active || void 0, d: pl.d, stroke: pl.active ? accentColor : guideColor, strokeWidth: pl.active ? 1 : 0.75, opacity: pl.active ? 0.55 : 0.25 }, pl.plane)),
700
+ overlay.perspective.vanishingPoints.map((v, i) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { "data-vector-vanishing-point": i, cx: v.x, cy: v.y, r: 4, stroke: accentColor, strokeWidth: 1.5 }, i)),
701
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { "data-vector-perspective-origin": "", cx: overlay.perspective.origin.x, cy: overlay.perspective.origin.y, r: 3, fill: accentColor })
702
+ ] }),
696
703
  overlay.symmetry.map((sy) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("g", { "data-vector-symmetry-axes": sy.nodeId, stroke: accentColor, strokeDasharray: sy.drawing ? void 0 : "6 4", opacity: sy.drawing ? 0.9 : 0.6, children: sy.lines.map((l, i) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: l.x1, y1: l.y1, x2: l.x2, y2: l.y2 }, i)) }, `sym:${sy.nodeId}`)),
697
704
  overlay.trackingPoints.map((t, i) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { "data-vector-tracking-point": "", d: `M${t.x - 5} ${t.y}H${t.x + 5}M${t.x} ${t.y - 5}V${t.y + 5}`, stroke: guideColor, strokeWidth: 1.5 }, `track:${i}`)),
698
705
  overlay.typedEntry && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("g", { "data-vector-typed-entry": "", transform: `translate(${overlay.typedEntry.x + 14} ${overlay.typedEntry.y + 18})`, children: [
@@ -829,8 +836,8 @@ function VectorCanvas({
829
836
  }
830
837
 
831
838
  // src/vector/VectorToolbar.tsx
832
- var import_react10 = __toESM(require("react"), 1);
833
- var import_vector16 = require("@pixerate/editor/vector");
839
+ var import_react11 = __toESM(require("react"), 1);
840
+ var import_vector18 = require("@pixerate/editor/vector");
834
841
 
835
842
  // src/vector/VectorEffectsPanel.tsx
836
843
  var import_vector4 = require("@pixerate/editor/vector");
@@ -2081,16 +2088,114 @@ function VectorSymmetryPanel({ controller, className }) {
2081
2088
  ] });
2082
2089
  }
2083
2090
 
2084
- // src/vector/VectorGradientEditor.tsx
2091
+ // src/vector/VectorPerspectivePanel.tsx
2092
+ var import_react10 = __toESM(require("react"), 1);
2085
2093
  var import_vector15 = require("@pixerate/editor/vector");
2086
2094
  var import_jsx_runtime15 = require("react/jsx-runtime");
2095
+ var buttonClass13 = "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";
2087
2096
  var selectClass10 = "h-7 min-w-0 flex-1 rounded border border-border bg-background px-1 text-xs";
2088
- var numberClass9 = "h-7 w-14 rounded border border-border bg-background px-1.5 text-xs";
2089
- var buttonClass13 = "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";
2097
+ var PLANE_LABELS = { floor: "Floor", left: "Left wall", right: "Right wall", side: "Side wall", front: "Front" };
2098
+ function VectorPerspectivePanel({ controller, className }) {
2099
+ const state = useVectorEditorState(controller);
2100
+ const grid = state.document.perspective ?? null;
2101
+ const planes = grid ? (0, import_vector15.vectorPerspectivePlanes)(grid) : [];
2102
+ const [attachTo, setAttachTo] = import_react10.default.useState("");
2103
+ const target = (state.perspectivePlane ?? attachTo) || planes[0];
2104
+ const add = (n) => controller.setPerspective((0, import_vector15.vectorPerspectivePreset)(n, state.document.width, state.document.height));
2105
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: `space-y-2 ${className ?? ""}`, "data-vector-perspective-panel": "", "aria-label": "Perspective", children: [
2106
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex flex-wrap items-center gap-1", children: [
2107
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "w-16 text-[11px] font-medium uppercase tracking-wide text-muted-foreground", children: "Perspective" }),
2108
+ [1, 2, 3].map((n) => /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("button", { type: "button", className: buttonClass13, "aria-pressed": grid?.vanishingPoints.length === n, "data-vector-perspective-preset": n, title: `A ${n}-point perspective grid for the page`, onClick: () => add(n), children: [
2109
+ n,
2110
+ "-pt"
2111
+ ] }, n))
2112
+ ] }),
2113
+ grid && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex flex-wrap items-center gap-1", children: [
2114
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "w-16" }),
2115
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("select", { "aria-label": "Draw on plane", title: "New rectangles, ellipses, polygons and stars are drawn on this plane", className: selectClass10, value: state.perspectivePlane ?? "", onChange: (e) => controller.setPerspectivePlane(e.target.value || null), children: [
2116
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("option", { value: "", children: "Draw flat" }),
2117
+ planes.map((p) => /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("option", { value: p, children: [
2118
+ "Draw on ",
2119
+ PLANE_LABELS[p].toLowerCase()
2120
+ ] }, p))
2121
+ ] })
2122
+ ] }),
2123
+ grid && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex flex-wrap items-center gap-1", children: [
2124
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "w-16" }),
2125
+ !state.perspectivePlane && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("select", { "aria-label": "Attach to plane", className: "h-7 w-24 rounded border border-border bg-background px-1 text-xs", value: attachTo || planes[0], onChange: (e) => setAttachTo(e.target.value), children: planes.map((p) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("option", { value: p, children: PLANE_LABELS[p] }, p)) }),
2126
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("button", { type: "button", className: buttonClass13, disabled: !state.selection.length, title: "Lay the selection onto the plane (read flat from the grid's origin)", onClick: () => controller.attachToPerspective(target), children: "Attach" }),
2127
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("label", { className: "flex items-center gap-1 text-xs text-foreground", title: "Show the grid on the canvas", children: [
2128
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("input", { type: "checkbox", "aria-label": "Show perspective grid", checked: grid.visible !== false, onChange: (e) => controller.setPerspective({ ...grid, visible: e.target.checked }) }),
2129
+ "Show"
2130
+ ] }),
2131
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("button", { type: "button", className: buttonClass13, title: "Remove the grid (art on it stays)", onClick: () => controller.setPerspective(null), children: "Remove" })
2132
+ ] })
2133
+ ] });
2134
+ }
2135
+
2136
+ // src/vector/VectorLensPanel.tsx
2137
+ var import_vector16 = require("@pixerate/editor/vector");
2138
+ var import_jsx_runtime16 = require("react/jsx-runtime");
2139
+ var selectClass11 = "h-7 min-w-0 flex-1 rounded border border-border bg-background px-1 text-xs";
2140
+ var numberClass9 = "h-7 w-16 rounded border border-border bg-background px-1.5 text-xs";
2141
+ function VectorLensPanel({ controller, className }) {
2142
+ const state = useVectorEditorState(controller);
2143
+ const path = state.selection.map((id) => controller.getNode(id)).find((n) => n?.type === "path" && n.subpaths.some((sp) => sp.closed) && !n.connector);
2144
+ if (!path) return null;
2145
+ const lens = path.lens ?? null;
2146
+ const spec = lens ? import_vector16.VECTOR_LENS_SPECS[lens.type] : null;
2147
+ const ids = [path.id];
2148
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: `space-y-2 ${className ?? ""}`, "data-vector-lens-panel": "", "aria-label": "Lens", children: [
2149
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "flex flex-wrap items-center gap-1", children: [
2150
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "w-16 text-[11px] font-medium uppercase tracking-wide text-muted-foreground", children: "Lens" }),
2151
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
2152
+ "select",
2153
+ {
2154
+ "aria-label": "Lens type",
2155
+ className: selectClass11,
2156
+ value: lens?.type ?? "",
2157
+ onChange: (e) => e.target.value ? controller.setLens({ type: e.target.value }, { ids }) : controller.setLens(null, { ids }),
2158
+ children: [
2159
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("option", { value: "", children: "None" }),
2160
+ import_vector16.VECTOR_LENS_TYPES.map((t) => /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("option", { value: t, children: import_vector16.VECTOR_LENS_LABELS[t] }, t))
2161
+ ]
2162
+ }
2163
+ )
2164
+ ] }),
2165
+ lens && spec && /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "flex flex-wrap items-center gap-1", children: [
2166
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "w-16" }),
2167
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
2168
+ "input",
2169
+ {
2170
+ type: "number",
2171
+ "aria-label": "Lens amount",
2172
+ title: `Amount: ${spec.unit} (${spec.min} to ${spec.max})`,
2173
+ className: numberClass9,
2174
+ min: spec.min,
2175
+ max: spec.max,
2176
+ step: lens.type === "magnify" ? 0.1 : 1,
2177
+ value: lens.amount,
2178
+ onChange: (e) => {
2179
+ const v = parseFloat(e.target.value);
2180
+ if (Number.isFinite(v)) controller.setLens({ amount: Math.min(spec.max, Math.max(spec.min, v)) }, { ids, merge: `lens-amount-${path.id}` });
2181
+ }
2182
+ }
2183
+ ),
2184
+ spec.color !== null && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("input", { type: "color", "aria-label": "Lens colour", title: "Lens colour", className: "h-7 w-9 rounded border border-border bg-background", value: lens.color ?? spec.color, onChange: (e) => controller.setLens({ color: e.target.value }, { ids, merge: `lens-color-${path.id}` }) })
2185
+ ] })
2186
+ ] });
2187
+ }
2188
+
2189
+ // src/vector/VectorGradientEditor.tsx
2190
+ var import_vector17 = require("@pixerate/editor/vector");
2191
+ var import_jsx_runtime17 = require("react/jsx-runtime");
2192
+ var selectClass12 = "h-7 min-w-0 flex-1 rounded border border-border bg-background px-1 text-xs";
2193
+ var numberClass10 = "h-7 w-14 rounded border border-border bg-background px-1.5 text-xs";
2194
+ var buttonClass14 = "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";
2090
2195
  var percent = (v) => Math.round(v * 1e3) / 10;
2091
2196
  function VectorGradientEditor({ controller, className }) {
2092
2197
  const state = useVectorEditorState(controller);
2093
- const ed = (0, import_vector15.getVectorGradientEditor)(state);
2198
+ const ed = (0, import_vector17.getVectorGradientEditor)(state);
2094
2199
  if (!ed) return null;
2095
2200
  const g = ed.gradient;
2096
2201
  const sel = ed.selected;
@@ -2098,26 +2203,26 @@ function VectorGradientEditor({ controller, className }) {
2098
2203
  const setType = (value) => {
2099
2204
  if (value === "solid") {
2100
2205
  const first = ed.stops[0];
2101
- controller.setStyle({ [ed.target]: (0, import_vector15.vectorPaintToHex)(first?.color) ?? first?.color ?? "#000000" }, void 0, { label: "Solid Colour" });
2206
+ controller.setStyle({ [ed.target]: (0, import_vector17.vectorPaintToHex)(first?.color) ?? first?.color ?? "#000000" }, void 0, { label: "Solid Colour" });
2102
2207
  } else controller.setGradient({ type: value });
2103
2208
  };
2104
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: `space-y-1.5 ${className ?? ""}`, "data-vector-gradient-editor": ed.target, "aria-label": "Gradient", children: [
2105
- /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex items-center gap-1.5", children: [
2106
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "w-16 text-xs text-foreground", children: "Gradient" }),
2107
- ["fill", "stroke"].map((t) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("button", { type: "button", className: buttonClass13, "aria-pressed": ed.target === t, onClick: () => controller.setGradientTarget(t), children: t === "fill" ? "Fill" : "Stroke" }, t)),
2108
- ed.mixed && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "text-[11px] text-muted-foreground", children: "Mixed" })
2209
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: `space-y-1.5 ${className ?? ""}`, "data-vector-gradient-editor": ed.target, "aria-label": "Gradient", children: [
2210
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "flex items-center gap-1.5", children: [
2211
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "w-16 text-xs text-foreground", children: "Gradient" }),
2212
+ ["fill", "stroke"].map((t) => /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("button", { type: "button", className: buttonClass14, "aria-pressed": ed.target === t, onClick: () => controller.setGradientTarget(t), children: t === "fill" ? "Fill" : "Stroke" }, t)),
2213
+ ed.mixed && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "text-[11px] text-muted-foreground", children: "Mixed" })
2109
2214
  ] }),
2110
- /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex items-center gap-1.5", children: [
2111
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "w-16 text-xs text-foreground", children: "Type" }),
2112
- /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("select", { "aria-label": "Gradient type", className: selectClass10, value: ed.type ?? "solid", onChange: (e) => setType(e.target.value), children: [
2113
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("option", { value: "solid", children: "Solid" }),
2114
- import_vector15.VECTOR_GRADIENT_TYPES.map((t) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("option", { value: t, children: import_vector15.VECTOR_GRADIENT_TYPE_LABELS[t] }, t))
2215
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "flex items-center gap-1.5", children: [
2216
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "w-16 text-xs text-foreground", children: "Type" }),
2217
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("select", { "aria-label": "Gradient type", className: selectClass12, value: ed.type ?? "solid", onChange: (e) => setType(e.target.value), children: [
2218
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("option", { value: "solid", children: "Solid" }),
2219
+ import_vector17.VECTOR_GRADIENT_TYPES.map((t) => /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("option", { value: t, children: import_vector17.VECTOR_GRADIENT_TYPE_LABELS[t] }, t))
2115
2220
  ] }),
2116
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("button", { type: "button", className: buttonClass13, "aria-label": "Reverse gradient", title: "Reverse gradient", disabled: !g, onClick: () => controller.reverseGradient(), children: "\u21C4" })
2221
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("button", { type: "button", className: buttonClass14, "aria-label": "Reverse gradient", title: "Reverse gradient", disabled: !g, onClick: () => controller.reverseGradient(), children: "\u21C4" })
2117
2222
  ] }),
2118
- g && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_jsx_runtime15.Fragment, { children: [
2119
- /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "relative h-8", "data-vector-gradient-ramp": "", children: [
2120
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
2223
+ g && /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_jsx_runtime17.Fragment, { children: [
2224
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "relative h-8", "data-vector-gradient-ramp": "", children: [
2225
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
2121
2226
  "div",
2122
2227
  {
2123
2228
  role: "button",
@@ -2131,7 +2236,7 @@ function VectorGradientEditor({ controller, className }) {
2131
2236
  }
2132
2237
  }
2133
2238
  ),
2134
- ed.stops.map((s) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
2239
+ ed.stops.map((s) => /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
2135
2240
  "button",
2136
2241
  {
2137
2242
  type: "button",
@@ -2145,21 +2250,21 @@ function VectorGradientEditor({ controller, className }) {
2145
2250
  s.index
2146
2251
  ))
2147
2252
  ] }),
2148
- sel && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex flex-wrap items-center gap-1.5", "data-vector-gradient-selected-stop": sel.index, children: [
2149
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
2253
+ sel && /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "flex flex-wrap items-center gap-1.5", "data-vector-gradient-selected-stop": sel.index, children: [
2254
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
2150
2255
  "input",
2151
2256
  {
2152
2257
  type: "color",
2153
2258
  "aria-label": "Stop colour",
2154
2259
  className: "h-7 w-9 cursor-pointer rounded border border-border bg-background",
2155
- value: (0, import_vector15.vectorPaintToHex)(sel.color) ?? "#000000",
2260
+ value: (0, import_vector17.vectorPaintToHex)(sel.color) ?? "#000000",
2156
2261
  onChange: (e) => controller.setGradientStop(sel.index, { color: e.target.value }, { merge: `gradient-stop-color-${sel.index}` }),
2157
2262
  onBlur: end
2158
2263
  }
2159
2264
  ),
2160
- /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("label", { className: "flex items-center gap-1 text-[11px] text-muted-foreground", children: [
2265
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("label", { className: "flex items-center gap-1 text-[11px] text-muted-foreground", children: [
2161
2266
  "Opacity",
2162
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
2267
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
2163
2268
  "input",
2164
2269
  {
2165
2270
  type: "number",
@@ -2167,7 +2272,7 @@ function VectorGradientEditor({ controller, className }) {
2167
2272
  max: 100,
2168
2273
  step: 5,
2169
2274
  "aria-label": "Stop opacity",
2170
- className: numberClass9,
2275
+ className: numberClass10,
2171
2276
  value: percent(sel.opacity ?? 1),
2172
2277
  onChange: (e) => {
2173
2278
  const n = parseFloat(e.target.value);
@@ -2176,9 +2281,9 @@ function VectorGradientEditor({ controller, className }) {
2176
2281
  }
2177
2282
  )
2178
2283
  ] }),
2179
- /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("label", { className: "flex items-center gap-1 text-[11px] text-muted-foreground", children: [
2284
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("label", { className: "flex items-center gap-1 text-[11px] text-muted-foreground", children: [
2180
2285
  "At",
2181
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
2286
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
2182
2287
  "input",
2183
2288
  {
2184
2289
  type: "number",
@@ -2186,7 +2291,7 @@ function VectorGradientEditor({ controller, className }) {
2186
2291
  max: 100,
2187
2292
  step: 1,
2188
2293
  "aria-label": "Stop position",
2189
- className: numberClass9,
2294
+ className: numberClass10,
2190
2295
  value: percent(sel.offset),
2191
2296
  onChange: (e) => {
2192
2297
  const n = parseFloat(e.target.value);
@@ -2195,9 +2300,9 @@ function VectorGradientEditor({ controller, className }) {
2195
2300
  }
2196
2301
  )
2197
2302
  ] }),
2198
- !sel.last && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("label", { className: "flex items-center gap-1 text-[11px] text-muted-foreground", children: [
2303
+ !sel.last && /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("label", { className: "flex items-center gap-1 text-[11px] text-muted-foreground", children: [
2199
2304
  "Midpoint",
2200
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
2305
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
2201
2306
  "input",
2202
2307
  {
2203
2308
  type: "number",
@@ -2205,7 +2310,7 @@ function VectorGradientEditor({ controller, className }) {
2205
2310
  max: 95,
2206
2311
  step: 1,
2207
2312
  "aria-label": "Stop midpoint",
2208
- className: numberClass9,
2313
+ className: numberClass10,
2209
2314
  value: percent(sel.midpoint ?? 0.5),
2210
2315
  onChange: (e) => {
2211
2316
  const n = parseFloat(e.target.value);
@@ -2214,15 +2319,15 @@ function VectorGradientEditor({ controller, className }) {
2214
2319
  }
2215
2320
  )
2216
2321
  ] }),
2217
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("button", { type: "button", className: buttonClass13, "aria-label": "Delete stop", title: "Delete stop", disabled: ed.stops.length <= 2, onClick: () => controller.removeGradientStop(sel.index), children: "\u2715" })
2322
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("button", { type: "button", className: buttonClass14, "aria-label": "Delete stop", title: "Delete stop", disabled: ed.stops.length <= 2, onClick: () => controller.removeGradientStop(sel.index), children: "\u2715" })
2218
2323
  ] }),
2219
- /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("label", { className: "flex items-center gap-1.5 text-xs text-foreground", children: [
2220
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "w-16", children: "Colours" }),
2221
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("select", { "aria-label": "Gradient interpolation", className: selectClass10, value: ed.interpolation, onChange: (e) => controller.setGradient({ interpolation: e.target.value }), children: import_vector15.VECTOR_GRADIENT_INTERPOLATIONS.map((i) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("option", { value: i, children: import_vector15.VECTOR_GRADIENT_INTERPOLATION_LABELS[i] }, i)) })
2324
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("label", { className: "flex items-center gap-1.5 text-xs text-foreground", children: [
2325
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "w-16", children: "Colours" }),
2326
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("select", { "aria-label": "Gradient interpolation", className: selectClass12, value: ed.interpolation, onChange: (e) => controller.setGradient({ interpolation: e.target.value }), children: import_vector17.VECTOR_GRADIENT_INTERPOLATIONS.map((i) => /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("option", { value: i, children: import_vector17.VECTOR_GRADIENT_INTERPOLATION_LABELS[i] }, i)) })
2222
2327
  ] }),
2223
- /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("label", { className: "flex items-center gap-1.5 text-xs text-foreground", children: [
2224
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "w-16", children: "Smooth" }),
2225
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
2328
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("label", { className: "flex items-center gap-1.5 text-xs text-foreground", children: [
2329
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "w-16", children: "Smooth" }),
2330
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
2226
2331
  "input",
2227
2332
  {
2228
2333
  type: "range",
@@ -2238,26 +2343,26 @@ function VectorGradientEditor({ controller, className }) {
2238
2343
  }
2239
2344
  )
2240
2345
  ] }),
2241
- /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("label", { className: "flex items-center gap-1.5 text-xs text-foreground", children: [
2242
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "w-16", children: "Spread" }),
2243
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("select", { "aria-label": "Gradient spread", className: selectClass10, value: ed.spread, onChange: (e) => controller.setGradient({ spread: e.target.value }), children: import_vector15.VECTOR_GRADIENT_SPREADS.map((sp) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("option", { value: sp, children: import_vector15.VECTOR_GRADIENT_SPREAD_LABELS[sp] }, sp)) })
2346
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("label", { className: "flex items-center gap-1.5 text-xs text-foreground", children: [
2347
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "w-16", children: "Spread" }),
2348
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("select", { "aria-label": "Gradient spread", className: selectClass12, value: ed.spread, onChange: (e) => controller.setGradient({ spread: e.target.value }), children: import_vector17.VECTOR_GRADIENT_SPREADS.map((sp) => /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("option", { value: sp, children: import_vector17.VECTOR_GRADIENT_SPREAD_LABELS[sp] }, sp)) })
2244
2349
  ] }),
2245
- ed.target === "stroke" && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("label", { className: "flex items-center gap-1.5 text-xs text-foreground", children: [
2246
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "w-16", children: "On stroke" }),
2247
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("select", { "aria-label": "Gradient on stroke", className: selectClass10, value: ed.strokeMode, onChange: (e) => controller.setGradient({ strokeMode: e.target.value }), children: import_vector15.VECTOR_STROKE_GRADIENT_MODES.map((m) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("option", { value: m, children: import_vector15.VECTOR_STROKE_GRADIENT_MODE_LABELS[m] }, m)) })
2350
+ ed.target === "stroke" && /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("label", { className: "flex items-center gap-1.5 text-xs text-foreground", children: [
2351
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "w-16", children: "On stroke" }),
2352
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("select", { "aria-label": "Gradient on stroke", className: selectClass12, value: ed.strokeMode, onChange: (e) => controller.setGradient({ strokeMode: e.target.value }), children: import_vector17.VECTOR_STROKE_GRADIENT_MODES.map((m) => /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("option", { value: m, children: import_vector17.VECTOR_STROKE_GRADIENT_MODE_LABELS[m] }, m)) })
2248
2353
  ] })
2249
2354
  ] })
2250
2355
  ] });
2251
2356
  }
2252
2357
 
2253
2358
  // src/vector/VectorToolbar.tsx
2254
- var import_jsx_runtime16 = require("react/jsx-runtime");
2359
+ var import_jsx_runtime18 = require("react/jsx-runtime");
2255
2360
  var VECTOR_SNAP_TITLE = "Snap to objects";
2256
2361
  var modKeyLabel = () => typeof navigator !== "undefined" && /Mac|iPhone|iPad/.test(navigator.platform ?? "") ? "\u2318" : "Ctrl";
2257
- var buttonClass14 = "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";
2362
+ var buttonClass15 = "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";
2258
2363
  function VectorToolbar({
2259
2364
  controller,
2260
- tools = import_vector16.VECTOR_TOOLS,
2365
+ tools = import_vector18.VECTOR_TOOLS,
2261
2366
  showHistory = true,
2262
2367
  showArrange = true,
2263
2368
  showSnap = true,
@@ -2271,36 +2376,36 @@ function VectorToolbar({
2271
2376
  const points = state.tool === "direct" ? state.selectedAnchors.length : 0;
2272
2377
  const editing = state.editing;
2273
2378
  const groupSelected = state.selection.some((id) => controller.getNode(id)?.type === "group");
2274
- const maskSelected = state.selection.some((id) => (0, import_vector16.isVectorMaskGroup)(controller.getNode(id)));
2275
- const [live, setLive] = import_react10.default.useState(false);
2379
+ const maskSelected = state.selection.some((id) => (0, import_vector18.isVectorMaskGroup)(controller.getNode(id)));
2380
+ const [live, setLive] = import_react11.default.useState(false);
2276
2381
  const booleanDisabled = live ? count < 1 : count < 2;
2277
- const act = (label, title, run, disabled = false) => /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("button", { type: "button", className: buttonClass14, title, "aria-label": title, disabled, onClick: () => void run(), children: label }, label);
2278
- return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { role: "toolbar", "aria-label": "Vector tools", className: `flex flex-wrap items-center gap-1 ${className ?? ""}`, children: [
2279
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "flex items-center gap-1", role: "group", "aria-label": "Tools", children: tools.map((tool) => /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
2382
+ const act = (label, title, run, disabled = false) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("button", { type: "button", className: buttonClass15, title, "aria-label": title, disabled, onClick: () => void run(), children: label }, label);
2383
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { role: "toolbar", "aria-label": "Vector tools", className: `flex flex-wrap items-center gap-1 ${className ?? ""}`, children: [
2384
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "flex items-center gap-1", role: "group", "aria-label": "Tools", children: tools.map((tool) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
2280
2385
  "button",
2281
2386
  {
2282
2387
  type: "button",
2283
2388
  "data-vector-tool": tool,
2284
- className: buttonClass14,
2389
+ className: buttonClass15,
2285
2390
  "aria-pressed": state.tool === tool,
2286
- title: `${(0, import_vector16.vectorToolLabel)(tool)} (${import_vector16.VECTOR_TOOL_SHORTCUTS[tool]})`,
2287
- "aria-label": (0, import_vector16.vectorToolLabel)(tool),
2391
+ title: `${(0, import_vector18.vectorToolLabel)(tool)} (${import_vector18.VECTOR_TOOL_SHORTCUTS[tool]})`,
2392
+ "aria-label": (0, import_vector18.vectorToolLabel)(tool),
2288
2393
  onClick: () => controller.setTool(tool),
2289
- children: import_vector16.VECTOR_TOOL_GLYPHS[tool]
2394
+ children: import_vector18.VECTOR_TOOL_GLYPHS[tool]
2290
2395
  },
2291
2396
  tool
2292
2397
  )) }),
2293
- showHistory && /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "History", children: [
2398
+ showHistory && /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "History", children: [
2294
2399
  act("\u21B6", state.canUndo ? `Undo ${state.undoLabel}` : "Undo", () => controller.undo(), !state.canUndo),
2295
2400
  act("\u21B7", state.canRedo ? `Redo ${state.redoLabel}` : "Redo", () => controller.redo(), !state.canRedo)
2296
2401
  ] }),
2297
- showSnap && /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Snapping", children: [
2298
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
2402
+ showSnap && /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Snapping", children: [
2403
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
2299
2404
  "button",
2300
2405
  {
2301
2406
  type: "button",
2302
2407
  "data-vector-snap": "",
2303
- className: buttonClass14,
2408
+ className: buttonClass15,
2304
2409
  "aria-pressed": state.snap.objects,
2305
2410
  title: `${VECTOR_SNAP_TITLE} (hold ${modKeyLabel()} to bypass)`,
2306
2411
  "aria-label": VECTOR_SNAP_TITLE,
@@ -2308,18 +2413,18 @@ function VectorToolbar({
2308
2413
  children: "Snap"
2309
2414
  }
2310
2415
  ),
2311
- /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
2416
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
2312
2417
  "select",
2313
2418
  {
2314
2419
  "data-vector-polar": "",
2315
2420
  "aria-label": "Polar tracking increment",
2316
2421
  title: "Polar tracking: the angles drawn points snap to from where they leave",
2317
2422
  className: "h-8 rounded-md border border-border bg-background px-1 text-xs",
2318
- value: String((0, import_vector16.vectorPolarIncrementOf)(state.snap.angles) ?? ""),
2319
- onChange: (e) => controller.setSnap({ angles: (0, import_vector16.vectorPolarAngles)(Number(e.target.value)) }),
2423
+ value: String((0, import_vector18.vectorPolarIncrementOf)(state.snap.angles) ?? ""),
2424
+ onChange: (e) => controller.setSnap({ angles: (0, import_vector18.vectorPolarAngles)(Number(e.target.value)) }),
2320
2425
  children: [
2321
- (0, import_vector16.vectorPolarIncrementOf)(state.snap.angles) === null && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("option", { value: "", children: "Custom" }),
2322
- [90, 45, 30, 15].map((n) => /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("option", { value: n, children: [
2426
+ (0, import_vector18.vectorPolarIncrementOf)(state.snap.angles) === null && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("option", { value: "", children: "Custom" }),
2427
+ [90, 45, 30, 15].map((n) => /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("option", { value: n, children: [
2323
2428
  n,
2324
2429
  "\xB0"
2325
2430
  ] }, n))
@@ -2327,13 +2432,13 @@ function VectorToolbar({
2327
2432
  }
2328
2433
  )
2329
2434
  ] }),
2330
- showPathEdit && /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Path editing", children: [
2331
- import_vector16.VECTOR_PATH_ACTIONS.map((a) => /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
2435
+ showPathEdit && /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Path editing", children: [
2436
+ import_vector18.VECTOR_PATH_ACTIONS.map((a) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
2332
2437
  "button",
2333
2438
  {
2334
2439
  type: "button",
2335
2440
  "data-vector-path-action": a.id,
2336
- className: buttonClass14,
2441
+ className: buttonClass15,
2337
2442
  title: a.shortcut ? `${a.title} (${modKeyLabel()}+${a.shortcut})` : a.title,
2338
2443
  "aria-label": a.title,
2339
2444
  disabled: !a.enabled(count, points),
@@ -2342,12 +2447,12 @@ function VectorToolbar({
2342
2447
  },
2343
2448
  a.id
2344
2449
  )),
2345
- import_vector16.VECTOR_EDIT_TOGGLES.map((t) => /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
2450
+ import_vector18.VECTOR_EDIT_TOGGLES.map((t) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
2346
2451
  "button",
2347
2452
  {
2348
2453
  type: "button",
2349
2454
  "data-vector-edit": t.key,
2350
- className: buttonClass14,
2455
+ className: buttonClass15,
2351
2456
  "aria-pressed": editing[t.key],
2352
2457
  title: t.title,
2353
2458
  "aria-label": t.title,
@@ -2356,9 +2461,9 @@ function VectorToolbar({
2356
2461
  },
2357
2462
  t.key
2358
2463
  )),
2359
- state.tool === "pencil" && /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("label", { className: "flex items-center gap-1 text-xs text-foreground", children: [
2464
+ state.tool === "pencil" && /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("label", { className: "flex items-center gap-1 text-xs text-foreground", children: [
2360
2465
  "Smoothing",
2361
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
2466
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
2362
2467
  "input",
2363
2468
  {
2364
2469
  type: "range",
@@ -2372,9 +2477,9 @@ function VectorToolbar({
2372
2477
  }
2373
2478
  )
2374
2479
  ] }),
2375
- state.tool === "eraser" && /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("label", { className: "flex items-center gap-1 text-xs text-foreground", children: [
2480
+ state.tool === "eraser" && /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("label", { className: "flex items-center gap-1 text-xs text-foreground", children: [
2376
2481
  "Size",
2377
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
2482
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
2378
2483
  "input",
2379
2484
  {
2380
2485
  type: "number",
@@ -2390,26 +2495,26 @@ function VectorToolbar({
2390
2495
  }
2391
2496
  )
2392
2497
  ] }),
2393
- state.tool === "paintBucket" && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
2498
+ state.tool === "paintBucket" && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
2394
2499
  "button",
2395
2500
  {
2396
2501
  type: "button",
2397
2502
  "data-vector-fill-gap": "",
2398
- className: buttonClass14,
2503
+ className: buttonClass15,
2399
2504
  "aria-pressed": editing.fillGap > 0,
2400
2505
  title: "Close small gaps between path ends when filling",
2401
2506
  "aria-label": "Close gaps",
2402
- onClick: () => controller.setEditing({ fillGap: editing.fillGap > 0 ? 0 : import_vector16.VECTOR_FILL_GAP }),
2507
+ onClick: () => controller.setEditing({ fillGap: editing.fillGap > 0 ? 0 : import_vector18.VECTOR_FILL_GAP }),
2403
2508
  children: "Gaps"
2404
2509
  }
2405
2510
  )
2406
2511
  ] }),
2407
- showDistort && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Distort", children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
2512
+ showDistort && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Distort", children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
2408
2513
  "button",
2409
2514
  {
2410
2515
  type: "button",
2411
2516
  "data-vector-free-distort": "",
2412
- className: buttonClass14,
2517
+ className: buttonClass15,
2413
2518
  "aria-pressed": state.freeDistort,
2414
2519
  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)`,
2415
2520
  "aria-label": "Free Distort",
@@ -2417,15 +2522,15 @@ function VectorToolbar({
2417
2522
  children: "Distort"
2418
2523
  }
2419
2524
  ) }),
2420
- showBlend && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(VectorBlendControls, { controller }),
2421
- showArrange && /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_jsx_runtime16.Fragment, { children: [
2422
- /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Combine", children: [
2423
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
2525
+ showBlend && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(VectorBlendControls, { controller }),
2526
+ showArrange && /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(import_jsx_runtime18.Fragment, { children: [
2527
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Combine", children: [
2528
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
2424
2529
  "button",
2425
2530
  {
2426
2531
  type: "button",
2427
2532
  "data-vector-live-boolean": "",
2428
- className: buttonClass14,
2533
+ className: buttonClass15,
2429
2534
  "aria-pressed": live,
2430
2535
  title: "Live booleans: keep the shapes editable",
2431
2536
  "aria-label": "Live booleans",
@@ -2442,12 +2547,12 @@ function VectorToolbar({
2442
2547
  act("\u25D0", `Make Clipping Mask (${modKeyLabel()}+7)`, () => controller.makeMask("clip"), count < 2),
2443
2548
  act("\u25D1", `Release Mask (${modKeyLabel()}+Alt+7)`, () => controller.releaseMask(), !maskSelected)
2444
2549
  ] }),
2445
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Pathfinder", children: import_vector16.VECTOR_PATHFINDER_ACTIONS.map((a) => /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
2550
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Pathfinder", children: import_vector18.VECTOR_PATHFINDER_ACTIONS.map((a) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
2446
2551
  "button",
2447
2552
  {
2448
2553
  type: "button",
2449
2554
  "data-vector-pathfinder": a.id,
2450
- className: buttonClass14,
2555
+ className: buttonClass15,
2451
2556
  title: a.title,
2452
2557
  "aria-label": a.title,
2453
2558
  disabled: count < a.min,
@@ -2456,7 +2561,7 @@ function VectorToolbar({
2456
2561
  },
2457
2562
  a.id
2458
2563
  )) }),
2459
- /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Arrange", children: [
2564
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Arrange", children: [
2460
2565
  act("\u21C8", "Bring to Front", () => controller.arrange("front"), count < 1),
2461
2566
  act("\u21CA", "Send to Back", () => controller.arrange("back"), count < 1),
2462
2567
  act("\u21E4", "Align Left", () => controller.align("left"), count < 1),
@@ -2471,22 +2576,22 @@ function VectorToolbar({
2471
2576
  }
2472
2577
  function VectorBlendControls({ controller }) {
2473
2578
  const state = useVectorEditorState(controller);
2474
- const blend = (0, import_vector16.getVectorBlendControls)(state);
2579
+ const blend = (0, import_vector18.getVectorBlendControls)(state);
2475
2580
  const o = blend.options;
2476
2581
  const selected = blend.ids.length > 0;
2477
- return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Blend", "data-vector-blend-controls": "", children: [
2478
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("button", { type: "button", "data-vector-blend-action": "make", className: buttonClass14, title: "Make Blend", "aria-label": "Make Blend", disabled: !blend.canBlend, onClick: () => controller.blend(), children: "\u29D3" }),
2479
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
2582
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Blend", "data-vector-blend-controls": "", children: [
2583
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("button", { type: "button", "data-vector-blend-action": "make", className: buttonClass15, title: "Make Blend", "aria-label": "Make Blend", disabled: !blend.canBlend, onClick: () => controller.blend(), children: "\u29D3" }),
2584
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
2480
2585
  "select",
2481
2586
  {
2482
2587
  "aria-label": "Blend spacing",
2483
2588
  className: "h-7 rounded border border-border bg-background px-1 text-xs",
2484
2589
  value: o.spacing,
2485
- onChange: (e) => (0, import_vector16.applyVectorBlendOptions)(controller, { spacing: e.target.value }),
2486
- children: import_vector16.VECTOR_BLEND_SPACINGS.map((sp) => /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("option", { value: sp, children: import_vector16.VECTOR_BLEND_SPACING_LABELS[sp] }, sp))
2590
+ onChange: (e) => (0, import_vector18.applyVectorBlendOptions)(controller, { spacing: e.target.value }),
2591
+ children: import_vector18.VECTOR_BLEND_SPACINGS.map((sp) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("option", { value: sp, children: import_vector18.VECTOR_BLEND_SPACING_LABELS[sp] }, sp))
2487
2592
  }
2488
2593
  ),
2489
- o.spacing !== "smoothColor" && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
2594
+ o.spacing !== "smoothColor" && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
2490
2595
  "input",
2491
2596
  {
2492
2597
  type: "number",
@@ -2498,29 +2603,29 @@ function VectorBlendControls({ controller }) {
2498
2603
  onChange: (e) => {
2499
2604
  const n = parseFloat(e.target.value);
2500
2605
  if (!Number.isFinite(n) || n <= 0) return;
2501
- (0, import_vector16.applyVectorBlendOptions)(controller, o.spacing === "steps" ? { steps: n } : { distance: n });
2606
+ (0, import_vector18.applyVectorBlendOptions)(controller, o.spacing === "steps" ? { steps: n } : { distance: n });
2502
2607
  }
2503
2608
  }
2504
2609
  ),
2505
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
2610
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
2506
2611
  "button",
2507
2612
  {
2508
2613
  type: "button",
2509
2614
  "data-vector-blend-action": "orient",
2510
- className: buttonClass14,
2615
+ className: buttonClass15,
2511
2616
  "aria-pressed": o.orientToSpine,
2512
2617
  title: "Orient steps to the spine",
2513
2618
  "aria-label": "Orient to spine",
2514
- onClick: () => (0, import_vector16.applyVectorBlendOptions)(controller, { orientToSpine: !o.orientToSpine }),
2619
+ onClick: () => (0, import_vector18.applyVectorBlendOptions)(controller, { orientToSpine: !o.orientToSpine }),
2515
2620
  children: "\u293F"
2516
2621
  }
2517
2622
  ),
2518
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
2623
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
2519
2624
  "button",
2520
2625
  {
2521
2626
  type: "button",
2522
2627
  "data-vector-blend-action": "spine",
2523
- className: buttonClass14,
2628
+ className: buttonClass15,
2524
2629
  title: "Replace Spine (select a blend and a path)",
2525
2630
  "aria-label": "Replace Spine",
2526
2631
  disabled: !blend.canReplaceSpine,
@@ -2528,15 +2633,15 @@ function VectorBlendControls({ controller }) {
2528
2633
  children: "\u2307"
2529
2634
  }
2530
2635
  ),
2531
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("button", { type: "button", "data-vector-blend-action": "expand", className: buttonClass14, title: "Expand Blend", "aria-label": "Expand Blend", disabled: !selected, onClick: () => controller.expandBlend(), children: "\u29C9" }),
2532
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("button", { type: "button", "data-vector-blend-action": "release", className: buttonClass14, title: "Release Blend", "aria-label": "Release Blend", disabled: !selected, onClick: () => controller.releaseBlend(), children: "\u29C4" })
2636
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("button", { type: "button", "data-vector-blend-action": "expand", className: buttonClass15, title: "Expand Blend", "aria-label": "Expand Blend", disabled: !selected, onClick: () => controller.expandBlend(), children: "\u29C9" }),
2637
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("button", { type: "button", "data-vector-blend-action": "release", className: buttonClass15, title: "Release Blend", "aria-label": "Release Blend", disabled: !selected, onClick: () => controller.releaseBlend(), children: "\u29C4" })
2533
2638
  ] });
2534
2639
  }
2535
- var selectClass11 = "h-7 min-w-0 flex-1 rounded border border-border bg-background px-1 text-xs";
2536
- var numberClass10 = "h-7 w-16 rounded border border-border bg-background px-1.5 text-xs";
2640
+ var selectClass13 = "h-7 min-w-0 flex-1 rounded border border-border bg-background px-1 text-xs";
2641
+ var numberClass11 = "h-7 w-16 rounded border border-border bg-background px-1.5 text-xs";
2537
2642
  function VectorStylePanel({ controller, className }) {
2538
2643
  const state = useVectorEditorState(controller);
2539
- const style = (0, import_vector16.getVectorSelectionStyle)(state);
2644
+ const style = (0, import_vector18.getVectorSelectionStyle)(state);
2540
2645
  const d = state.defaults;
2541
2646
  const source = style ?? {
2542
2647
  fill: d.fill ?? null,
@@ -2549,7 +2654,7 @@ function VectorStylePanel({ controller, className }) {
2549
2654
  arrowAlign: d.arrowAlign ?? "extend",
2550
2655
  startArrowScale: d.startArrowScale ?? 1,
2551
2656
  endArrowScale: d.endArrowScale ?? 1,
2552
- widthProfile: (0, import_vector16.widthProfilePresetId)(d.strokeWidthProfile),
2657
+ widthProfile: (0, import_vector18.widthProfilePresetId)(d.strokeWidthProfile),
2553
2658
  cornerRadius: d.cornerRadius,
2554
2659
  cornerKind: d.cornerKind ?? "round",
2555
2660
  cornerSmoothing: 0,
@@ -2563,19 +2668,19 @@ function VectorStylePanel({ controller, className }) {
2563
2668
  };
2564
2669
  const setProfile = (id) => {
2565
2670
  if (style) controller.setWidthProfile(id);
2566
- else controller.setDefaults({ strokeWidthProfile: (0, import_vector16.widthProfilePreset)(id)?.length ? (0, import_vector16.widthProfilePreset)(id) : null });
2671
+ else controller.setDefaults({ strokeWidthProfile: (0, import_vector18.widthProfilePreset)(id)?.length ? (0, import_vector18.widthProfilePreset)(id) : null });
2567
2672
  };
2568
- const mixed = (key) => style?.mixed[key] ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "text-[11px] text-muted-foreground", children: "Mixed" }) : null;
2673
+ const mixed = (key) => style?.mixed[key] ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "text-[11px] text-muted-foreground", children: "Mixed" }) : null;
2569
2674
  const arrowRow = (end, label) => {
2570
2675
  const key = end === "start" ? "startArrow" : "endArrow";
2571
2676
  const scaleKey = end === "start" ? "startArrowScale" : "endArrowScale";
2572
- return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "flex items-center gap-2", "data-vector-style": key, children: [
2573
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "w-16 text-xs text-foreground", children: label }),
2574
- /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("select", { "aria-label": `${label} arrowhead`, className: selectClass11, value: source[key] ?? "", onChange: (e) => apply({ [key]: e.target.value || null }), children: [
2575
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("option", { value: "", children: "None" }),
2576
- import_vector16.VECTOR_ARROWHEADS.map((h) => /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("option", { value: h, children: import_vector16.VECTOR_ARROWHEAD_LABELS[h] }, h))
2677
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "flex items-center gap-2", "data-vector-style": key, children: [
2678
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "w-16 text-xs text-foreground", children: label }),
2679
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("select", { "aria-label": `${label} arrowhead`, className: selectClass13, value: source[key] ?? "", onChange: (e) => apply({ [key]: e.target.value || null }), children: [
2680
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("option", { value: "", children: "None" }),
2681
+ import_vector18.VECTOR_ARROWHEADS.map((h) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("option", { value: h, children: import_vector18.VECTOR_ARROWHEAD_LABELS[h] }, h))
2577
2682
  ] }),
2578
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
2683
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
2579
2684
  "input",
2580
2685
  {
2581
2686
  type: "number",
@@ -2583,7 +2688,7 @@ function VectorStylePanel({ controller, className }) {
2583
2688
  step: 0.25,
2584
2689
  "aria-label": `${label} arrowhead scale`,
2585
2690
  title: "Scale (\xD7 stroke width)",
2586
- className: numberClass10,
2691
+ className: numberClass11,
2587
2692
  value: source[scaleKey],
2588
2693
  onChange: (e) => {
2589
2694
  const n = parseFloat(e.target.value);
@@ -2599,14 +2704,14 @@ function VectorStylePanel({ controller, className }) {
2599
2704
  };
2600
2705
  const paintRow = (key, label) => {
2601
2706
  const paint = source[key];
2602
- const hex = (0, import_vector16.vectorPaintToHex)(paint);
2707
+ const hex = (0, import_vector18.vectorPaintToHex)(paint);
2603
2708
  const on = paint !== null && paint !== "none";
2604
- return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "flex items-center gap-2", "data-vector-style": key, children: [
2605
- /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("label", { className: "flex w-16 items-center gap-1.5 text-xs text-foreground", children: [
2606
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("input", { type: "checkbox", checked: on, "aria-label": `${label} on`, onChange: (e) => apply({ [key]: e.target.checked ? hex ?? "#000000" : null }) }),
2709
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "flex items-center gap-2", "data-vector-style": key, children: [
2710
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("label", { className: "flex w-16 items-center gap-1.5 text-xs text-foreground", children: [
2711
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("input", { type: "checkbox", checked: on, "aria-label": `${label} on`, onChange: (e) => apply({ [key]: e.target.checked ? hex ?? "#000000" : null }) }),
2607
2712
  label
2608
2713
  ] }),
2609
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
2714
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
2610
2715
  "input",
2611
2716
  {
2612
2717
  type: "color",
@@ -2617,18 +2722,18 @@ function VectorStylePanel({ controller, className }) {
2617
2722
  onChange: (e) => apply({ [key]: e.target.value })
2618
2723
  }
2619
2724
  ),
2620
- on && !hex && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "truncate text-[11px] text-muted-foreground", children: typeof paint === "string" ? paint : "Gradient" }),
2621
- style?.mixed[key] && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "text-[11px] text-muted-foreground", children: "Mixed" })
2725
+ on && !hex && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "truncate text-[11px] text-muted-foreground", children: typeof paint === "string" ? paint : "Gradient" }),
2726
+ style?.mixed[key] && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "text-[11px] text-muted-foreground", children: "Mixed" })
2622
2727
  ] });
2623
2728
  };
2624
- return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: `space-y-2 ${className ?? ""}`, "aria-label": style ? "Selection style" : "New shape style", children: [
2625
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("p", { className: "text-[11px] font-medium uppercase tracking-wide text-muted-foreground", children: style ? "Selection" : "New shapes" }),
2729
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: `space-y-2 ${className ?? ""}`, "aria-label": style ? "Selection style" : "New shape style", children: [
2730
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("p", { className: "text-[11px] font-medium uppercase tracking-wide text-muted-foreground", children: style ? "Selection" : "New shapes" }),
2626
2731
  paintRow("fill", "Fill"),
2627
2732
  paintRow("stroke", "Stroke"),
2628
- style && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(VectorGradientEditor, { controller }),
2629
- /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("label", { className: "flex items-center gap-2 text-xs text-foreground", children: [
2630
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "w-16", children: "Width" }),
2631
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
2733
+ style && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(VectorGradientEditor, { controller }),
2734
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("label", { className: "flex items-center gap-2 text-xs text-foreground", children: [
2735
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "w-16", children: "Width" }),
2736
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
2632
2737
  "input",
2633
2738
  {
2634
2739
  type: "number",
@@ -2644,33 +2749,33 @@ function VectorStylePanel({ controller, className }) {
2644
2749
  }
2645
2750
  )
2646
2751
  ] }),
2647
- /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("label", { className: "flex items-center gap-2 text-xs text-foreground", "data-vector-style": "strokeAlign", children: [
2648
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "w-16", children: "Align" }),
2649
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("select", { "aria-label": "Stroke alignment", className: selectClass11, value: source.strokeAlign, onChange: (e) => apply({ strokeAlign: e.target.value }), children: Object.keys(import_vector16.VECTOR_STROKE_ALIGN_LABELS).map((a) => /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("option", { value: a, children: import_vector16.VECTOR_STROKE_ALIGN_LABELS[a] }, a)) }),
2752
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("label", { className: "flex items-center gap-2 text-xs text-foreground", "data-vector-style": "strokeAlign", children: [
2753
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "w-16", children: "Align" }),
2754
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("select", { "aria-label": "Stroke alignment", className: selectClass13, value: source.strokeAlign, onChange: (e) => apply({ strokeAlign: e.target.value }), children: Object.keys(import_vector18.VECTOR_STROKE_ALIGN_LABELS).map((a) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("option", { value: a, children: import_vector18.VECTOR_STROKE_ALIGN_LABELS[a] }, a)) }),
2650
2755
  mixed("strokeAlign")
2651
2756
  ] }),
2652
- /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "flex items-center gap-2 text-xs text-foreground", "data-vector-style": "widthProfile", children: [
2653
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "w-16", children: "Profile" }),
2654
- /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
2757
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "flex items-center gap-2 text-xs text-foreground", "data-vector-style": "widthProfile", children: [
2758
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "w-16", children: "Profile" }),
2759
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
2655
2760
  "select",
2656
2761
  {
2657
2762
  "aria-label": "Width profile",
2658
- className: selectClass11,
2763
+ className: selectClass13,
2659
2764
  value: source.widthProfile,
2660
2765
  onChange: (e) => {
2661
2766
  if (e.target.value !== "custom") setProfile(e.target.value);
2662
2767
  },
2663
2768
  children: [
2664
- import_vector16.VECTOR_WIDTH_PRESETS.map((p) => /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("option", { value: p.id, children: p.label }, p.id)),
2665
- source.widthProfile === "custom" && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("option", { value: "custom", disabled: true, children: "Custom" })
2769
+ import_vector18.VECTOR_WIDTH_PRESETS.map((p) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("option", { value: p.id, children: p.label }, p.id)),
2770
+ source.widthProfile === "custom" && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("option", { value: "custom", disabled: true, children: "Custom" })
2666
2771
  ]
2667
2772
  }
2668
2773
  ),
2669
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
2774
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
2670
2775
  "button",
2671
2776
  {
2672
2777
  type: "button",
2673
- className: buttonClass14,
2778
+ className: buttonClass15,
2674
2779
  title: "Reset to uniform width",
2675
2780
  "aria-label": "Reset to uniform width",
2676
2781
  disabled: source.widthProfile === "uniform" && !style?.mixed.widthProfile,
@@ -2680,27 +2785,27 @@ function VectorStylePanel({ controller, className }) {
2680
2785
  ),
2681
2786
  mixed("widthProfile")
2682
2787
  ] }),
2683
- source.hasOpenPath && /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_jsx_runtime16.Fragment, { children: [
2788
+ source.hasOpenPath && /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(import_jsx_runtime18.Fragment, { children: [
2684
2789
  arrowRow("start", "Start"),
2685
2790
  arrowRow("end", "End"),
2686
- (source.startArrow || source.endArrow) && /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("label", { className: "flex items-center gap-2 text-xs text-foreground", "data-vector-style": "arrowAlign", children: [
2687
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "w-16", children: "Heads" }),
2688
- /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("select", { "aria-label": "Arrowhead alignment", className: selectClass11, value: source.arrowAlign, onChange: (e) => apply({ arrowAlign: e.target.value }), children: [
2689
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("option", { value: "extend", children: "Tip past end" }),
2690
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("option", { value: "tip", children: "Tip at end" })
2791
+ (source.startArrow || source.endArrow) && /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("label", { className: "flex items-center gap-2 text-xs text-foreground", "data-vector-style": "arrowAlign", children: [
2792
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "w-16", children: "Heads" }),
2793
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("select", { "aria-label": "Arrowhead alignment", className: selectClass13, value: source.arrowAlign, onChange: (e) => apply({ arrowAlign: e.target.value }), children: [
2794
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("option", { value: "extend", children: "Tip past end" }),
2795
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("option", { value: "tip", children: "Tip at end" })
2691
2796
  ] })
2692
2797
  ] })
2693
2798
  ] }),
2694
- source.corners > 0 && /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "flex items-center gap-2", "data-vector-style": "corners", children: [
2695
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "w-16 text-xs text-foreground", children: "Corners" }),
2696
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
2799
+ source.corners > 0 && /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "flex items-center gap-2", "data-vector-style": "corners", children: [
2800
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "w-16 text-xs text-foreground", children: "Corners" }),
2801
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
2697
2802
  "input",
2698
2803
  {
2699
2804
  type: "number",
2700
2805
  min: 0,
2701
2806
  step: 1,
2702
2807
  "aria-label": "Corner radius",
2703
- className: numberClass10,
2808
+ className: numberClass11,
2704
2809
  value: Math.round(source.cornerRadius * 100) / 100,
2705
2810
  onChange: (e) => {
2706
2811
  const n = parseFloat(e.target.value);
@@ -2708,12 +2813,12 @@ function VectorStylePanel({ controller, className }) {
2708
2813
  }
2709
2814
  }
2710
2815
  ),
2711
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("select", { "aria-label": "Corner kind", className: selectClass11, value: source.cornerKind, onChange: (e) => setCorners({ kind: e.target.value }), children: import_vector16.VECTOR_CORNER_KINDS.map((k) => /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("option", { value: k, children: import_vector16.VECTOR_CORNER_KIND_LABELS[k] }, k)) }),
2816
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("select", { "aria-label": "Corner kind", className: selectClass13, value: source.cornerKind, onChange: (e) => setCorners({ kind: e.target.value }), children: import_vector18.VECTOR_CORNER_KINDS.map((k) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("option", { value: k, children: import_vector18.VECTOR_CORNER_KIND_LABELS[k] }, k)) }),
2712
2817
  mixed("cornerRadius")
2713
2818
  ] }),
2714
- style && source.corners > 0 && source.cornerKind === "round" && source.cornerRadius > 0 && /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("label", { className: "flex items-center gap-2 text-xs text-foreground", "data-vector-style": "corner-smoothing", title: "Corner smoothing: squircle corners that ease into the curve (0.6 is iOS-like)", children: [
2715
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "w-16", children: "Smoothing" }),
2716
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
2819
+ style && source.corners > 0 && source.cornerKind === "round" && source.cornerRadius > 0 && /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("label", { className: "flex items-center gap-2 text-xs text-foreground", "data-vector-style": "corner-smoothing", title: "Corner smoothing: squircle corners that ease into the curve (0.6 is iOS-like)", children: [
2820
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "w-16", children: "Smoothing" }),
2821
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
2717
2822
  "input",
2718
2823
  {
2719
2824
  type: "range",
@@ -2728,9 +2833,9 @@ function VectorStylePanel({ controller, className }) {
2728
2833
  }
2729
2834
  )
2730
2835
  ] }),
2731
- style && /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("label", { className: "flex items-center gap-2 text-xs text-foreground", children: [
2732
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "w-16", children: "Opacity" }),
2733
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
2836
+ style && /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("label", { className: "flex items-center gap-2 text-xs text-foreground", children: [
2837
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "w-16", children: "Opacity" }),
2838
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
2734
2839
  "input",
2735
2840
  {
2736
2841
  type: "range",
@@ -2744,33 +2849,35 @@ function VectorStylePanel({ controller, className }) {
2744
2849
  }
2745
2850
  )
2746
2851
  ] }),
2747
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(VectorTextPanel, { controller }),
2748
- style && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(VectorAppearancePanel, { controller }),
2749
- style && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(VectorEffectsPanel, { controller }),
2750
- style && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(VectorEnvelopePanel, { controller }),
2751
- style && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(VectorMaskPanel, { controller }),
2752
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(VectorFramePanel, { controller }),
2753
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(VectorConnectorPanel, { controller }),
2754
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(VectorShadingPanel, { controller }),
2755
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(VectorSymmetryPanel, { controller }),
2756
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(VectorTracePanel, { controller }),
2757
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(VectorConstraintsPanel, { controller }),
2758
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(VectorSymbolsPanel, { controller })
2852
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(VectorTextPanel, { controller }),
2853
+ style && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(VectorAppearancePanel, { controller }),
2854
+ style && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(VectorEffectsPanel, { controller }),
2855
+ style && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(VectorEnvelopePanel, { controller }),
2856
+ style && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(VectorMaskPanel, { controller }),
2857
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(VectorFramePanel, { controller }),
2858
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(VectorConnectorPanel, { controller }),
2859
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(VectorShadingPanel, { controller }),
2860
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(VectorSymmetryPanel, { controller }),
2861
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(VectorPerspectivePanel, { controller }),
2862
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(VectorLensPanel, { controller }),
2863
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(VectorTracePanel, { controller }),
2864
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(VectorConstraintsPanel, { controller }),
2865
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(VectorSymbolsPanel, { controller })
2759
2866
  ] });
2760
2867
  }
2761
2868
 
2762
2869
  // src/effects/useEffects.ts
2763
- var import_react11 = require("react");
2870
+ var import_react12 = require("react");
2764
2871
  function useEffects(host, provider) {
2765
- (0, import_react11.useEffect)(() => {
2872
+ (0, import_react12.useEffect)(() => {
2766
2873
  if (!host || !provider) return;
2767
2874
  return host.useEffects(provider);
2768
2875
  }, [host, provider]);
2769
2876
  }
2770
2877
  function useEffectDescription(provider, effect) {
2771
- const [state, setState] = (0, import_react11.useState)({ key: "", description: null, error: null });
2878
+ const [state, setState] = (0, import_react12.useState)({ key: "", description: null, error: null });
2772
2879
  const key = provider && effect ? `${provider.id}:${effect}` : "";
2773
- (0, import_react11.useEffect)(() => {
2880
+ (0, import_react12.useEffect)(() => {
2774
2881
  if (!provider || !effect) return;
2775
2882
  let live = true;
2776
2883
  Promise.resolve().then(() => provider.describe(effect)).then(
@@ -2785,13 +2892,13 @@ function useEffectDescription(provider, effect) {
2785
2892
  }
2786
2893
 
2787
2894
  // src/effects/EffectThumbnail.tsx
2788
- var import_react12 = require("react");
2895
+ var import_react13 = require("react");
2789
2896
  var import_effects = require("@pixerate/editor/effects");
2790
- var import_jsx_runtime17 = require("react/jsx-runtime");
2897
+ var import_jsx_runtime19 = require("react/jsx-runtime");
2791
2898
  function EffectThumbnail({ provider, effect, width = 96, height = 54, className = "" }) {
2792
- const ref = (0, import_react12.useRef)(null);
2793
- const [failed, setFailed] = (0, import_react12.useState)(effect.kind === "motion");
2794
- (0, import_react12.useEffect)(() => {
2899
+ const ref = (0, import_react13.useRef)(null);
2900
+ const [failed, setFailed] = (0, import_react13.useState)(effect.kind === "motion");
2901
+ (0, import_react13.useEffect)(() => {
2795
2902
  if (effect.kind === "motion") return;
2796
2903
  let live = true;
2797
2904
  (0, import_effects.renderEffectThumbnail)(provider, effect, { width, height }).then(
@@ -2806,28 +2913,28 @@ function EffectThumbnail({ provider, effect, width = 96, height = 54, className
2806
2913
  live = false;
2807
2914
  };
2808
2915
  }, [provider, effect, width, height]);
2809
- return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
2916
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
2810
2917
  "div",
2811
2918
  {
2812
2919
  className: `relative overflow-hidden rounded border border-border bg-[repeating-conic-gradient(#8884_0_25%,transparent_0_50%)] bg-[length:12px_12px] ${className}`,
2813
2920
  style: { aspectRatio: `${width} / ${height}` },
2814
2921
  "data-effect-thumbnail": effect.id,
2815
- children: failed ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "absolute inset-0 flex items-center justify-center text-[10px] text-muted-foreground", children: effect.kind === "motion" ? "Motion" : "No preview" }) : /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("canvas", { ref, width, height, className: "h-full w-full", "aria-hidden": "true" })
2922
+ children: failed ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "absolute inset-0 flex items-center justify-center text-[10px] text-muted-foreground", children: effect.kind === "motion" ? "Motion" : "No preview" }) : /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("canvas", { ref, width, height, className: "h-full w-full", "aria-hidden": "true" })
2816
2923
  }
2817
2924
  );
2818
2925
  }
2819
2926
 
2820
2927
  // src/effects/EffectsBrowser.tsx
2821
- var import_react13 = require("react");
2928
+ var import_react14 = require("react");
2822
2929
  var import_effects2 = require("@pixerate/editor/effects");
2823
- var import_jsx_runtime18 = require("react/jsx-runtime");
2930
+ var import_jsx_runtime20 = require("react/jsx-runtime");
2824
2931
  function EffectsBrowser({ provider, uses, kinds, onPick, thumbnails = true, title = "Effects", className = "" }) {
2825
- const [all, setAll] = (0, import_react13.useState)([]);
2826
- const [query, setQuery] = (0, import_react13.useState)("");
2827
- const [category, setCategory] = (0, import_react13.useState)("");
2828
- const [use, setUse] = (0, import_react13.useState)("");
2829
- const [error, setError] = (0, import_react13.useState)(null);
2830
- (0, import_react13.useEffect)(() => {
2932
+ const [all, setAll] = (0, import_react14.useState)([]);
2933
+ const [query, setQuery] = (0, import_react14.useState)("");
2934
+ const [category, setCategory] = (0, import_react14.useState)("");
2935
+ const [use, setUse] = (0, import_react14.useState)("");
2936
+ const [error, setError] = (0, import_react14.useState)(null);
2937
+ (0, import_react14.useEffect)(() => {
2831
2938
  if (!provider) return setAll([]);
2832
2939
  let live = true;
2833
2940
  Promise.resolve().then(() => provider.list()).then(
@@ -2838,8 +2945,8 @@ function EffectsBrowser({ provider, uses, kinds, onPick, thumbnails = true, titl
2838
2945
  live = false;
2839
2946
  };
2840
2947
  }, [provider]);
2841
- const [found, setFound] = (0, import_react13.useState)([]);
2842
- (0, import_react13.useEffect)(() => {
2948
+ const [found, setFound] = (0, import_react14.useState)([]);
2949
+ (0, import_react14.useEffect)(() => {
2843
2950
  if (!provider) return setFound([]);
2844
2951
  let live = true;
2845
2952
  Promise.resolve().then(() => provider.search(query, { ...category ? { category } : {}, ...use ? { use } : {} })).then((list) => live && setFound(list), () => live && setFound([]));
@@ -2848,14 +2955,14 @@ function EffectsBrowser({ provider, uses, kinds, onPick, thumbnails = true, titl
2848
2955
  };
2849
2956
  }, [provider, query, category, use]);
2850
2957
  const allowed = (e) => (!kinds || kinds.includes(e.kind)) && (uses ? e.kind !== "motion" || uses.includes("motion") : e.kind !== "motion") && (!uses || e.uses.some((u) => uses.includes(u)));
2851
- const categories = (0, import_react13.useMemo)(() => [...new Set(all.filter(allowed).map((e) => e.category))], [all, uses, kinds]);
2958
+ const categories = (0, import_react14.useMemo)(() => [...new Set(all.filter(allowed).map((e) => e.category))], [all, uses, kinds]);
2852
2959
  const shown = found.filter(allowed);
2853
2960
  if (!provider)
2854
- return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: `rounded-md border border-dashed border-border p-3 text-xs text-muted-foreground ${className}`, "data-effects-browser": "", "data-effects-unavailable": "", children: "No effects provider is registered, so effects cannot be added. Effects already in the document are kept." });
2855
- return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("section", { className: `flex flex-col gap-2 ${className}`, "data-effects-browser": "", "aria-label": "Effects browser", children: [
2856
- title && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("h3", { className: "text-xs font-semibold", children: title }),
2857
- /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "flex flex-wrap gap-1", children: [
2858
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
2961
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: `rounded-md border border-dashed border-border p-3 text-xs text-muted-foreground ${className}`, "data-effects-browser": "", "data-effects-unavailable": "", children: "No effects provider is registered, so effects cannot be added. Effects already in the document are kept." });
2962
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("section", { className: `flex flex-col gap-2 ${className}`, "data-effects-browser": "", "aria-label": "Effects browser", children: [
2963
+ title && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("h3", { className: "text-xs font-semibold", children: title }),
2964
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "flex flex-wrap gap-1", children: [
2965
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
2859
2966
  "input",
2860
2967
  {
2861
2968
  type: "search",
@@ -2866,17 +2973,17 @@ function EffectsBrowser({ provider, uses, kinds, onPick, thumbnails = true, titl
2866
2973
  onChange: (e) => setQuery(e.currentTarget.value)
2867
2974
  }
2868
2975
  ),
2869
- /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("select", { className: "rounded border border-border bg-transparent px-1 py-0.5 text-xs", "aria-label": "Category", value: category, onChange: (e) => setCategory(e.currentTarget.value), children: [
2870
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("option", { value: "", children: "All categories" }),
2871
- categories.map((c) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("option", { value: c, children: c }, c))
2976
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("select", { className: "rounded border border-border bg-transparent px-1 py-0.5 text-xs", "aria-label": "Category", value: category, onChange: (e) => setCategory(e.currentTarget.value), children: [
2977
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("option", { value: "", children: "All categories" }),
2978
+ categories.map((c) => /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("option", { value: c, children: c }, c))
2872
2979
  ] }),
2873
- uses && uses.length > 1 && /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("select", { className: "rounded border border-border bg-transparent px-1 py-0.5 text-xs", "aria-label": "Use", value: use, onChange: (e) => setUse(e.currentTarget.value), children: [
2874
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("option", { value: "", children: "Any use" }),
2875
- uses.map((u) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("option", { value: u, children: import_effects2.EFFECT_USE_LABELS[u] }, u))
2980
+ uses && uses.length > 1 && /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("select", { className: "rounded border border-border bg-transparent px-1 py-0.5 text-xs", "aria-label": "Use", value: use, onChange: (e) => setUse(e.currentTarget.value), children: [
2981
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("option", { value: "", children: "Any use" }),
2982
+ uses.map((u) => /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("option", { value: u, children: import_effects2.EFFECT_USE_LABELS[u] }, u))
2876
2983
  ] })
2877
2984
  ] }),
2878
- error && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("p", { role: "alert", className: "text-[11px] text-destructive", children: error }),
2879
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("ul", { className: "grid max-h-96 grid-cols-[repeat(auto-fill,minmax(6.5rem,1fr))] gap-1.5 overflow-y-auto", role: "list", children: shown.map((e) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
2985
+ error && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("p", { role: "alert", className: "text-[11px] text-destructive", children: error }),
2986
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("ul", { className: "grid max-h-96 grid-cols-[repeat(auto-fill,minmax(6.5rem,1fr))] gap-1.5 overflow-y-auto", role: "list", children: shown.map((e) => /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
2880
2987
  "button",
2881
2988
  {
2882
2989
  type: "button",
@@ -2885,25 +2992,25 @@ function EffectsBrowser({ provider, uses, kinds, onPick, thumbnails = true, titl
2885
2992
  "data-effect-id": e.id,
2886
2993
  onClick: () => onPick(e),
2887
2994
  children: [
2888
- thumbnails && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(EffectThumbnail, { provider, effect: e }),
2889
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "truncate text-[11px] font-medium", children: e.name }),
2890
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "truncate text-[10px] text-muted-foreground", children: e.uses.map((u) => import_effects2.EFFECT_USE_LABELS[u]).join(" \xB7 ") })
2995
+ thumbnails && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(EffectThumbnail, { provider, effect: e }),
2996
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "truncate text-[11px] font-medium", children: e.name }),
2997
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "truncate text-[10px] text-muted-foreground", children: e.uses.map((u) => import_effects2.EFFECT_USE_LABELS[u]).join(" \xB7 ") })
2891
2998
  ]
2892
2999
  }
2893
3000
  ) }, e.id)) }),
2894
- !shown.length && !error && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("p", { className: "text-[11px] text-muted-foreground", children: "No effects match." })
3001
+ !shown.length && !error && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("p", { className: "text-[11px] text-muted-foreground", children: "No effects match." })
2895
3002
  ] });
2896
3003
  }
2897
3004
 
2898
3005
  // src/effects/EffectParameters.tsx
2899
- var import_react15 = __toESM(require("react"), 1);
3006
+ var import_react16 = __toESM(require("react"), 1);
2900
3007
  var import_effects3 = require("@pixerate/editor/effects");
2901
3008
  var import_parameters2 = require("@pixerate/editor/parameters");
2902
3009
 
2903
3010
  // src/parameters/fields.tsx
2904
- var import_react14 = require("react");
3011
+ var import_react15 = require("react");
2905
3012
  var import_parameters = require("@pixerate/editor/parameters");
2906
- var import_jsx_runtime19 = require("react/jsx-runtime");
3013
+ var import_jsx_runtime21 = require("react/jsx-runtime");
2907
3014
  var parameterInputClass = "h-7 w-full min-w-0 rounded-md border border-input bg-background px-2 text-xs text-foreground tabular-nums focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-primary disabled:cursor-not-allowed disabled:opacity-50";
2908
3015
  var parameterButtonClass = "inline-flex h-7 items-center justify-center gap-1 rounded-md border border-input bg-background px-2 text-xs text-foreground hover:bg-muted focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-primary disabled:pointer-events-none disabled:opacity-50";
2909
3016
  var rangeClass = "h-4 w-full min-w-0 flex-1 cursor-pointer accent-primary disabled:cursor-not-allowed disabled:opacity-50";
@@ -2927,7 +3034,7 @@ function ParameterCommitInput({ value, onCommit, multiline, className, onKeyDown
2927
3034
  onKeyDown: keyDown,
2928
3035
  ...rest
2929
3036
  };
2930
- return multiline ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("textarea", { ...props, className: `${props.className} h-auto min-h-[3.5rem] py-1` }, value) : /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("input", { type: "text", ...props }, value);
3037
+ return multiline ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("textarea", { ...props, className: `${props.className} h-auto min-h-[3.5rem] py-1` }, value) : /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("input", { type: "text", ...props }, value);
2931
3038
  }
2932
3039
  var plainNumber = (field, value) => (0, import_parameters.formatParameterNumber)({ ...field, unit: void 0 }, value);
2933
3040
  function ParameterNumberInput({
@@ -2938,8 +3045,8 @@ function ParameterNumberInput({
2938
3045
  id,
2939
3046
  "aria-label": ariaLabel
2940
3047
  }) {
2941
- return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "relative min-w-0 flex-1", children: [
2942
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
3048
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "relative min-w-0 flex-1", children: [
3049
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
2943
3050
  ParameterCommitInput,
2944
3051
  {
2945
3052
  id,
@@ -2961,21 +3068,21 @@ function ParameterNumberInput({
2961
3068
  }
2962
3069
  }
2963
3070
  ),
2964
- field.unit && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "pointer-events-none absolute right-2 top-1/2 -translate-y-1/2 text-[10px] text-muted-foreground", children: field.unit })
3071
+ field.unit && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "pointer-events-none absolute right-2 top-1/2 -translate-y-1/2 text-[10px] text-muted-foreground", children: field.unit })
2965
3072
  ] });
2966
3073
  }
2967
3074
  function ParameterSlider(props) {
2968
3075
  const { field, value, onChange, onGestureStart, onGestureEnd, disabled } = props;
2969
- const dragging = (0, import_react14.useRef)(false);
3076
+ const dragging = (0, import_react15.useRef)(false);
2970
3077
  const end = () => {
2971
3078
  if (!dragging.current) return;
2972
3079
  dragging.current = false;
2973
3080
  onGestureEnd?.();
2974
3081
  };
2975
- if (!(0, import_parameters.isSliderField)(field)) return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(ParameterNumberInput, { ...props });
3082
+ if (!(0, import_parameters.isSliderField)(field)) return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ParameterNumberInput, { ...props });
2976
3083
  const step = (0, import_parameters.parameterStep)(field);
2977
- return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "flex items-center gap-2", children: [
2978
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
3084
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "flex items-center gap-2", children: [
3085
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
2979
3086
  "input",
2980
3087
  {
2981
3088
  type: "range",
@@ -2998,11 +3105,11 @@ function ParameterSlider(props) {
2998
3105
  onChange: (e) => onChange((0, import_parameters.clampParameterNumber)(field, Number(e.target.value)))
2999
3106
  }
3000
3107
  ),
3001
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { className: "w-20 shrink-0", children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(ParameterNumberInput, { ...props, id: props.id, "aria-label": `${(0, import_parameters.parameterLabel)(field)} value` }) })
3108
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "w-20 shrink-0", children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ParameterNumberInput, { ...props, id: props.id, "aria-label": `${(0, import_parameters.parameterLabel)(field)} value` }) })
3002
3109
  ] });
3003
3110
  }
3004
3111
  function ParameterToggle({ field, value, onChange, disabled, id }) {
3005
- return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
3112
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
3006
3113
  "button",
3007
3114
  {
3008
3115
  type: "button",
@@ -3013,7 +3120,7 @@ function ParameterToggle({ field, value, onChange, disabled, id }) {
3013
3120
  disabled: disabled || field.readOnly,
3014
3121
  onClick: () => onChange(!value),
3015
3122
  className: `relative inline-flex h-5 w-9 shrink-0 items-center rounded-full border border-transparent transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-primary disabled:opacity-50 ${value ? "bg-primary" : "bg-muted"}`,
3016
- children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
3123
+ children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
3017
3124
  "span",
3018
3125
  {
3019
3126
  className: `pointer-events-none block h-4 w-4 rounded-full bg-background shadow transition-transform ${value ? "translate-x-4" : "translate-x-0.5"}`
@@ -3026,7 +3133,7 @@ function ParameterSelect({ field, value, onChange, disabled, id }) {
3026
3133
  const options = (0, import_parameters.getChoiceOptions)(field);
3027
3134
  const off = disabled || field.readOnly;
3028
3135
  if (field.control === "segmented") {
3029
- return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { role: "radiogroup", "aria-label": (0, import_parameters.parameterLabel)(field), className: "inline-flex flex-wrap rounded-md border border-input bg-muted p-0.5", children: options.map((o) => /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
3136
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { role: "radiogroup", "aria-label": (0, import_parameters.parameterLabel)(field), className: "inline-flex flex-wrap rounded-md border border-input bg-muted p-0.5", children: options.map((o) => /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
3030
3137
  "button",
3031
3138
  {
3032
3139
  type: "button",
@@ -3042,7 +3149,7 @@ function ParameterSelect({ field, value, onChange, disabled, id }) {
3042
3149
  )) });
3043
3150
  }
3044
3151
  const index = options.findIndex((o) => o.value === value);
3045
- return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
3152
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
3046
3153
  "select",
3047
3154
  {
3048
3155
  id,
@@ -3054,12 +3161,12 @@ function ParameterSelect({ field, value, onChange, disabled, id }) {
3054
3161
  if (option) onChange(option.value);
3055
3162
  },
3056
3163
  className: parameterInputClass,
3057
- children: options.map((o, i) => /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("option", { value: String(i), title: o.description, children: (0, import_parameters.choiceOptionLabel)(o) }, String(o.value)))
3164
+ children: options.map((o, i) => /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("option", { value: String(i), title: o.description, children: (0, import_parameters.choiceOptionLabel)(o) }, String(o.value)))
3058
3165
  }
3059
3166
  );
3060
3167
  }
3061
3168
  function ParameterColor({ field, value, onChange, onGestureStart, onGestureEnd, disabled, id }) {
3062
- const picking = (0, import_react14.useRef)(false);
3169
+ const picking = (0, import_react15.useRef)(false);
3063
3170
  const alpha = !!field.alpha;
3064
3171
  const hex = (0, import_parameters.normalizeHexColor)(value, true) ?? "#000000ff";
3065
3172
  const rgb = hex.slice(0, 7);
@@ -3075,11 +3182,11 @@ function ParameterColor({ field, value, onChange, onGestureStart, onGestureEnd,
3075
3182
  picking.current = false;
3076
3183
  onGestureEnd?.();
3077
3184
  };
3078
- return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "space-y-1.5", children: [
3079
- /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "flex items-center gap-2", children: [
3080
- /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("span", { className: "relative h-7 w-9 shrink-0 overflow-hidden rounded-md border border-input bg-[repeating-conic-gradient(#d4d4d8_0_25%,#fff_0_50%)] bg-[length:8px_8px]", children: [
3081
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "absolute inset-0", style: { background: alpha ? hex : rgb } }),
3082
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
3185
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "space-y-1.5", children: [
3186
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "flex items-center gap-2", children: [
3187
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("span", { className: "relative h-7 w-9 shrink-0 overflow-hidden rounded-md border border-input bg-[repeating-conic-gradient(#d4d4d8_0_25%,#fff_0_50%)] bg-[length:8px_8px]", children: [
3188
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "absolute inset-0", style: { background: alpha ? hex : rgb } }),
3189
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
3083
3190
  "input",
3084
3191
  {
3085
3192
  type: "color",
@@ -3095,7 +3202,7 @@ function ParameterColor({ field, value, onChange, onGestureStart, onGestureEnd,
3095
3202
  }
3096
3203
  )
3097
3204
  ] }),
3098
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
3205
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
3099
3206
  ParameterCommitInput,
3100
3207
  {
3101
3208
  id,
@@ -3111,7 +3218,7 @@ function ParameterColor({ field, value, onChange, onGestureStart, onGestureEnd,
3111
3218
  }
3112
3219
  )
3113
3220
  ] }),
3114
- alpha && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
3221
+ alpha && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
3115
3222
  "input",
3116
3223
  {
3117
3224
  type: "range",
@@ -3129,10 +3236,10 @@ function ParameterColor({ field, value, onChange, onGestureStart, onGestureEnd,
3129
3236
  onChange: (e) => onChange(`${rgb}${Number(e.target.value).toString(16).padStart(2, "0")}`)
3130
3237
  }
3131
3238
  ),
3132
- field.swatches && field.swatches.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { className: "flex flex-wrap gap-1", children: field.swatches.map((s) => {
3239
+ field.swatches && field.swatches.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "flex flex-wrap gap-1", children: field.swatches.map((s) => {
3133
3240
  const swatch = (0, import_parameters.normalizeHexColor)(s, alpha);
3134
3241
  if (!swatch) return null;
3135
- return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
3242
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
3136
3243
  "button",
3137
3244
  {
3138
3245
  type: "button",
@@ -3151,11 +3258,11 @@ function ParameterColor({ field, value, onChange, onGestureStart, onGestureEnd,
3151
3258
  }
3152
3259
  function ParameterVector({ field, value, onChange, disabled, id }) {
3153
3260
  const size = (0, import_parameters.vectorSize)(field);
3154
- return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { className: "grid gap-1.5", style: { gridTemplateColumns: `repeat(${size}, minmax(0, 1fr))` }, children: Array.from({ length: size }, (_, i) => {
3261
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "grid gap-1.5", style: { gridTemplateColumns: `repeat(${size}, minmax(0, 1fr))` }, children: Array.from({ length: size }, (_, i) => {
3155
3262
  const component = (0, import_parameters.vectorComponentField)(field, i);
3156
- return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("label", { className: "flex min-w-0 items-center gap-1", children: [
3157
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "w-3 shrink-0 text-[10px] uppercase text-muted-foreground", children: component.label }),
3158
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
3263
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("label", { className: "flex min-w-0 items-center gap-1", children: [
3264
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "w-3 shrink-0 text-[10px] uppercase text-muted-foreground", children: component.label }),
3265
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
3159
3266
  ParameterNumberInput,
3160
3267
  {
3161
3268
  field: component,
@@ -3172,11 +3279,11 @@ function ParameterVector({ field, value, onChange, disabled, id }) {
3172
3279
  function ParameterImage({ field, value, onChange, disabled, id, resolveFile }) {
3173
3280
  const off = disabled || field.readOnly;
3174
3281
  const name = (0, import_parameters.parameterLabel)(field);
3175
- return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "space-y-1.5", children: [
3176
- /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "flex items-center gap-2", children: [
3177
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { className: "flex h-12 w-16 shrink-0 items-center justify-center overflow-hidden rounded-md border border-input bg-muted", children: value ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("img", { src: value, alt: name, className: "h-full w-full object-cover" }) : /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "text-[10px] text-muted-foreground", children: "None" }) }),
3178
- /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "flex min-w-0 flex-1 flex-col gap-1", children: [
3179
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
3282
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "space-y-1.5", children: [
3283
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "flex items-center gap-2", children: [
3284
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "flex h-12 w-16 shrink-0 items-center justify-center overflow-hidden rounded-md border border-input bg-muted", children: value ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("img", { src: value, alt: name, className: "h-full w-full object-cover" }) : /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "text-[10px] text-muted-foreground", children: "None" }) }),
3285
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "flex min-w-0 flex-1 flex-col gap-1", children: [
3286
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
3180
3287
  ParameterCommitInput,
3181
3288
  {
3182
3289
  id,
@@ -3187,10 +3294,10 @@ function ParameterImage({ field, value, onChange, disabled, id, resolveFile }) {
3187
3294
  onCommit: (text) => onChange(text.trim())
3188
3295
  }
3189
3296
  ),
3190
- /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "flex gap-1", children: [
3191
- resolveFile && /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("label", { className: `${parameterButtonClass} cursor-pointer ${off ? "pointer-events-none opacity-50" : ""}`, children: [
3297
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "flex gap-1", children: [
3298
+ resolveFile && /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("label", { className: `${parameterButtonClass} cursor-pointer ${off ? "pointer-events-none opacity-50" : ""}`, children: [
3192
3299
  "Choose file\u2026",
3193
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
3300
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
3194
3301
  "input",
3195
3302
  {
3196
3303
  type: "file",
@@ -3206,11 +3313,11 @@ function ParameterImage({ field, value, onChange, disabled, id, resolveFile }) {
3206
3313
  }
3207
3314
  )
3208
3315
  ] }),
3209
- value && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("button", { type: "button", className: parameterButtonClass, disabled: off, onClick: () => onChange(""), "aria-label": `Clear ${name}`, children: "Clear" })
3316
+ value && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("button", { type: "button", className: parameterButtonClass, disabled: off, onClick: () => onChange(""), "aria-label": `Clear ${name}`, children: "Clear" })
3210
3317
  ] })
3211
3318
  ] })
3212
3319
  ] }),
3213
- field.options && field.options.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { className: "flex flex-wrap gap-1", children: field.options.map((o) => /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
3320
+ field.options && field.options.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "flex flex-wrap gap-1", children: field.options.map((o) => /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
3214
3321
  "button",
3215
3322
  {
3216
3323
  type: "button",
@@ -3220,14 +3327,14 @@ function ParameterImage({ field, value, onChange, disabled, id, resolveFile }) {
3220
3327
  disabled: off,
3221
3328
  onClick: () => onChange(o.url),
3222
3329
  className: "h-9 w-12 overflow-hidden rounded border border-input aria-pressed:ring-2 aria-pressed:ring-primary",
3223
- children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("img", { src: o.url, alt: "", className: "h-full w-full object-cover" })
3330
+ children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("img", { src: o.url, alt: "", className: "h-full w-full object-cover" })
3224
3331
  },
3225
3332
  o.url
3226
3333
  )) })
3227
3334
  ] });
3228
3335
  }
3229
3336
  function ParameterText({ field, value, onChange, disabled, id }) {
3230
- return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
3337
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
3231
3338
  ParameterCommitInput,
3232
3339
  {
3233
3340
  id,
@@ -3246,31 +3353,31 @@ function ParameterFieldControl({ field, value, onChange, resolveFile, ...rest })
3246
3353
  switch (field.type) {
3247
3354
  case "number":
3248
3355
  case "int":
3249
- return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(ParameterSlider, { field, value, onChange: set, ...rest });
3356
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ParameterSlider, { field, value, onChange: set, ...rest });
3250
3357
  case "boolean":
3251
- return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(ParameterToggle, { field, value, onChange: set, ...rest });
3358
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ParameterToggle, { field, value, onChange: set, ...rest });
3252
3359
  case "choice":
3253
- return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(ParameterSelect, { field, value, onChange: set, ...rest });
3360
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ParameterSelect, { field, value, onChange: set, ...rest });
3254
3361
  case "color":
3255
- return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(ParameterColor, { field, value, onChange: set, ...rest });
3362
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ParameterColor, { field, value, onChange: set, ...rest });
3256
3363
  case "vector":
3257
- return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(ParameterVector, { field, value, onChange: set, ...rest });
3364
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ParameterVector, { field, value, onChange: set, ...rest });
3258
3365
  case "image":
3259
- return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(ParameterImage, { field, value, onChange: set, resolveFile, ...rest });
3366
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ParameterImage, { field, value, onChange: set, resolveFile, ...rest });
3260
3367
  case "text":
3261
- return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(ParameterText, { field, value, onChange: set, ...rest });
3368
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ParameterText, { field, value, onChange: set, ...rest });
3262
3369
  }
3263
3370
  }
3264
3371
 
3265
3372
  // src/effects/EffectParameters.tsx
3266
- var import_jsx_runtime20 = require("react/jsx-runtime");
3373
+ var import_jsx_runtime22 = require("react/jsx-runtime");
3267
3374
  function EffectParameters({ description, values, onChange, onReset, keyframed, onPreset, disabled = false, className = "" }) {
3268
- const fields = (0, import_react15.useMemo)(() => (0, import_effects3.effectParameterFields)(description), [description]);
3269
- const idBase = (0, import_react15.useId)();
3270
- const gesture = import_react15.default.useRef(null);
3271
- const count = import_react15.default.useRef(0);
3272
- return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: `space-y-2 ${className}`, "data-effect-parameters": description.id, children: [
3273
- onPreset && description.presets.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
3375
+ const fields = (0, import_react16.useMemo)(() => (0, import_effects3.effectParameterFields)(description), [description]);
3376
+ const idBase = (0, import_react16.useId)();
3377
+ const gesture = import_react16.default.useRef(null);
3378
+ const count = import_react16.default.useRef(0);
3379
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: `space-y-2 ${className}`, "data-effect-parameters": description.id, children: [
3380
+ onPreset && description.presets.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
3274
3381
  "select",
3275
3382
  {
3276
3383
  className: "w-full rounded border border-border bg-transparent px-1 py-0.5 text-xs",
@@ -3282,8 +3389,8 @@ function EffectParameters({ description, values, onChange, onReset, keyframed, o
3282
3389
  if (preset) onPreset({ ...description.defaults, ...preset.values });
3283
3390
  },
3284
3391
  children: [
3285
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("option", { value: "", children: "Presets\u2026" }),
3286
- description.presets.map((p) => /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("option", { value: p.name, children: p.name }, p.name))
3392
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("option", { value: "", children: "Presets\u2026" }),
3393
+ description.presets.map((p) => /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("option", { value: p.name, children: p.name }, p.name))
3287
3394
  ]
3288
3395
  }
3289
3396
  ),
@@ -3293,13 +3400,13 @@ function EffectParameters({ description, values, onChange, onReset, keyframed, o
3293
3400
  const keyed = keyframed?.has(field.id) ?? false;
3294
3401
  const value = (0, import_effects3.effectFieldValue)(field, values[field.id]);
3295
3402
  const changed = !keyed && !(0, import_parameters2.parameterValuesEqual)(value, field.default);
3296
- return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "space-y-1", "data-effect-control": field.id, children: [
3297
- /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "flex items-center gap-1.5", children: [
3298
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("label", { htmlFor: inputId, className: "min-w-0 flex-1 truncate text-xs font-medium", title: field.description, children: label }),
3299
- keyed && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "text-[10px] text-muted-foreground", children: "keyframed" }),
3300
- onReset && changed && !disabled && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("button", { type: "button", className: "rounded px-1 text-[11px] text-muted-foreground hover:text-foreground", "aria-label": `Reset ${label}`, onClick: () => onReset(field.id), children: "\u21BA" })
3403
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: "space-y-1", "data-effect-control": field.id, children: [
3404
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: "flex items-center gap-1.5", children: [
3405
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("label", { htmlFor: inputId, className: "min-w-0 flex-1 truncate text-xs font-medium", title: field.description, children: label }),
3406
+ keyed && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: "text-[10px] text-muted-foreground", children: "keyframed" }),
3407
+ onReset && changed && !disabled && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("button", { type: "button", className: "rounded px-1 text-[11px] text-muted-foreground hover:text-foreground", "aria-label": `Reset ${label}`, onClick: () => onReset(field.id), children: "\u21BA" })
3301
3408
  ] }),
3302
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
3409
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
3303
3410
  ParameterFieldControl,
3304
3411
  {
3305
3412
  field,
@@ -3313,25 +3420,25 @@ function EffectParameters({ description, values, onChange, onReset, keyframed, o
3313
3420
  )
3314
3421
  ] }, field.id);
3315
3422
  }),
3316
- !fields.length && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("p", { className: "text-[11px] text-muted-foreground", children: "No controls." })
3423
+ !fields.length && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("p", { className: "text-[11px] text-muted-foreground", children: "No controls." })
3317
3424
  ] });
3318
3425
  }
3319
3426
 
3320
3427
  // src/vector/VectorProviderEffectsPanel.tsx
3321
- var import_react16 = require("react");
3322
- var import_jsx_runtime21 = require("react/jsx-runtime");
3428
+ var import_react17 = require("react");
3429
+ var import_jsx_runtime23 = require("react/jsx-runtime");
3323
3430
  var btn = "rounded border border-border px-1.5 py-0.5 text-[11px] disabled:opacity-40";
3324
3431
  function Controls({ controller, group }) {
3325
3432
  const provider = controller.effectsProvider;
3326
3433
  const tag = group.providerEffect;
3327
3434
  const { description, error } = useEffectDescription(provider, tag.effect);
3328
- const [message, setMessage] = (0, import_react16.useState)(null);
3435
+ const [message, setMessage] = (0, import_react17.useState)(null);
3329
3436
  const run = (work) => void work.then(() => setMessage(null), (e) => setMessage(e instanceof Error ? e.message : String(e)));
3330
- if (!provider) return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("p", { className: "text-[11px] text-muted-foreground", children: "No effects provider: the shapes are kept as they are." });
3331
- if (error) return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("p", { className: "text-[11px] text-amber-500", children: error });
3332
- if (!description) return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("p", { className: "text-[11px] text-muted-foreground", children: "Loading\u2026" });
3333
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_jsx_runtime21.Fragment, { children: [
3334
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
3437
+ if (!provider) return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("p", { className: "text-[11px] text-muted-foreground", children: "No effects provider: the shapes are kept as they are." });
3438
+ if (error) return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("p", { className: "text-[11px] text-amber-500", children: error });
3439
+ if (!description) return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("p", { className: "text-[11px] text-muted-foreground", children: "Loading\u2026" });
3440
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(import_jsx_runtime23.Fragment, { children: [
3441
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
3335
3442
  EffectParameters,
3336
3443
  {
3337
3444
  description,
@@ -3341,18 +3448,18 @@ function Controls({ controller, group }) {
3341
3448
  onPreset: (v) => run(controller.updateVectorEffect(group.id, v))
3342
3449
  }
3343
3450
  ),
3344
- message && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("p", { role: "alert", className: "text-[11px] text-destructive", children: message })
3451
+ message && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("p", { role: "alert", className: "text-[11px] text-destructive", children: message })
3345
3452
  ] });
3346
3453
  }
3347
3454
  function VectorProviderEffectsPanel({ controller, className = "" }) {
3348
3455
  const state = useVectorEditorState(controller);
3349
- const provider = (0, import_react16.useSyncExternalStore)(
3456
+ const provider = (0, import_react17.useSyncExternalStore)(
3350
3457
  (cb) => controller.effects.subscribe(cb),
3351
3458
  () => controller.effects.provider,
3352
3459
  () => controller.effects.provider
3353
3460
  );
3354
- const [browse, setBrowse] = (0, import_react16.useState)(false);
3355
- const [message, setMessage] = (0, import_react16.useState)(null);
3461
+ const [browse, setBrowse] = (0, import_react17.useState)(false);
3462
+ const [message, setMessage] = (0, import_react17.useState)(null);
3356
3463
  void state;
3357
3464
  const groups = controller.vectorEffectGroups();
3358
3465
  const selected = groups.filter((g) => state.selection.includes(g.id));
@@ -3360,41 +3467,41 @@ function VectorProviderEffectsPanel({ controller, className = "" }) {
3360
3467
  setMessage(null);
3361
3468
  Promise.resolve().then(work).catch((e) => setMessage(e instanceof Error ? e.message : String(e)));
3362
3469
  };
3363
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("section", { className: `flex flex-col gap-2 text-xs ${className}`, "aria-label": "Vector effects", "data-vector-provider-effects": "", children: [
3364
- /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "flex items-center gap-1", children: [
3365
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "flex-1 font-semibold", children: "Effects" }),
3366
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("button", { type: "button", className: btn, disabled: !provider, "aria-pressed": browse, onClick: () => setBrowse(!browse), children: "Add effect\u2026" })
3470
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("section", { className: `flex flex-col gap-2 text-xs ${className}`, "aria-label": "Vector effects", "data-vector-provider-effects": "", children: [
3471
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "flex items-center gap-1", children: [
3472
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { className: "flex-1 font-semibold", children: "Effects" }),
3473
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("button", { type: "button", className: btn, disabled: !provider, "aria-pressed": browse, onClick: () => setBrowse(!browse), children: "Add effect\u2026" })
3367
3474
  ] }),
3368
- !provider && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("p", { className: "text-[11px] text-muted-foreground", children: "No effects provider is registered. Effect groups are kept as shapes." }),
3369
- browse && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(EffectsBrowser, { provider, kinds: ["vector"], title: "Vector effects", onPick: (e) => (setBrowse(false), run(() => controller.addVectorEffect(e.id))) }),
3370
- selected.map((g) => /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "space-y-1 rounded border border-border p-1", "data-vector-effect-group": g.id, children: [
3371
- /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "flex items-center gap-1", children: [
3372
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "min-w-0 flex-1 truncate font-medium", children: g.providerEffect.name ?? g.providerEffect.effect }),
3373
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("button", { type: "button", className: btn, title: "Keep the shapes as a plain group", onClick: () => run(() => controller.detachVectorEffect(g.id)), children: "Detach" })
3475
+ !provider && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("p", { className: "text-[11px] text-muted-foreground", children: "No effects provider is registered. Effect groups are kept as shapes." }),
3476
+ browse && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(EffectsBrowser, { provider, kinds: ["vector"], title: "Vector effects", onPick: (e) => (setBrowse(false), run(() => controller.addVectorEffect(e.id))) }),
3477
+ selected.map((g) => /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "space-y-1 rounded border border-border p-1", "data-vector-effect-group": g.id, children: [
3478
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "flex items-center gap-1", children: [
3479
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { className: "min-w-0 flex-1 truncate font-medium", children: g.providerEffect.name ?? g.providerEffect.effect }),
3480
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("button", { type: "button", className: btn, title: "Keep the shapes as a plain group", onClick: () => run(() => controller.detachVectorEffect(g.id)), children: "Detach" })
3374
3481
  ] }),
3375
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(Controls, { controller, group: g })
3482
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(Controls, { controller, group: g })
3376
3483
  ] }, g.id)),
3377
- groups.length > 0 && !selected.length && /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("p", { className: "text-[11px] text-muted-foreground", children: [
3484
+ groups.length > 0 && !selected.length && /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("p", { className: "text-[11px] text-muted-foreground", children: [
3378
3485
  "Select an effect group (",
3379
3486
  groups.length,
3380
3487
  " in the drawing) to change it."
3381
3488
  ] }),
3382
- message && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("p", { role: "alert", className: "text-[11px] text-destructive", children: message })
3489
+ message && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("p", { role: "alert", className: "text-[11px] text-destructive", children: message })
3383
3490
  ] });
3384
3491
  }
3385
3492
 
3386
3493
  // src/vector/VectorEditor.tsx
3387
- var import_jsx_runtime22 = require("react/jsx-runtime");
3494
+ var import_jsx_runtime24 = require("react/jsx-runtime");
3388
3495
  function VectorEditor({ controller, tools, showStylePanel = true, height = 520, canvasProps, effects, className }) {
3389
3496
  useEffects(controller, effects);
3390
3497
  const effectsShown = !!effects || controller.vectorEffectGroups().length > 0;
3391
- return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: `flex flex-col gap-2 ${className ?? ""}`, "data-vector-editor": "", children: [
3392
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(VectorToolbar, { controller, tools }),
3393
- /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: "flex min-h-0 flex-col gap-2 md:flex-row", children: [
3394
- /* @__PURE__ */ (0, import_jsx_runtime22.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_runtime22.jsx)(VectorCanvas, { controller, ...canvasProps }) }),
3395
- showStylePanel && /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: "flex w-full shrink-0 flex-col gap-2 md:w-56", children: [
3396
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(VectorStylePanel, { controller, className: "rounded-md border border-border p-3" }),
3397
- effectsShown && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(VectorProviderEffectsPanel, { controller, className: "rounded-md border border-border p-3" })
3498
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className: `flex flex-col gap-2 ${className ?? ""}`, "data-vector-editor": "", children: [
3499
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(VectorToolbar, { controller, tools }),
3500
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className: "flex min-h-0 flex-col gap-2 md:flex-row", children: [
3501
+ /* @__PURE__ */ (0, import_jsx_runtime24.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_runtime24.jsx)(VectorCanvas, { controller, ...canvasProps }) }),
3502
+ showStylePanel && /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className: "flex w-full shrink-0 flex-col gap-2 md:w-56", children: [
3503
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(VectorStylePanel, { controller, className: "rounded-md border border-border p-3" }),
3504
+ effectsShown && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(VectorProviderEffectsPanel, { controller, className: "rounded-md border border-border p-3" })
3398
3505
  ] })
3399
3506
  ] })
3400
3507
  ] });
@@ -3413,8 +3520,10 @@ function VectorEditor({ controller, tools, showStylePanel = true, height = 520,
3413
3520
  VectorGradientAnnotator,
3414
3521
  VectorGradientDefs,
3415
3522
  VectorGradientEditor,
3523
+ VectorLensPanel,
3416
3524
  VectorMaskPanel,
3417
3525
  VectorMaskedNodes,
3526
+ VectorPerspectivePanel,
3418
3527
  VectorProviderEffectsPanel,
3419
3528
  VectorSceneNodes,
3420
3529
  VectorShadingPanel,