@pixerate/editor-react 0.84.0 → 0.85.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.
@@ -1886,6 +1886,35 @@ function VectorFramePanel({ controller, className }) {
1886
1886
  ] });
1887
1887
  }
1888
1888
 
1889
+ // src/vector/VectorPagesPanel.tsx
1890
+ import { isVectorFrame, vectorMasterPages, vectorPages } from "@pixerate/editor/vector";
1891
+ import { jsx as jsx11, jsxs as jsxs11 } from "react/jsx-runtime";
1892
+ var buttonClass9 = "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-[current=true]:bg-primary aria-[current=true]:text-primary-foreground";
1893
+ var selectClass7 = "h-7 min-w-0 flex-1 rounded border border-border bg-background px-1 text-xs";
1894
+ function VectorPagesPanel({ controller, className }) {
1895
+ const state = useVectorEditorState(controller);
1896
+ const pages = vectorPages(state.document);
1897
+ const masters = vectorMasterPages(state.document);
1898
+ const selected = state.selection.map((id) => state.document.nodes.find((n) => n.id === id)).find((n) => isVectorFrame(n));
1899
+ const page = selected && !selected.frame.master ? selected : null;
1900
+ return /* @__PURE__ */ jsxs11("div", { className: `space-y-2 ${className ?? ""}`, "data-vector-pages-panel": "", "aria-label": "Pages", children: [
1901
+ /* @__PURE__ */ jsxs11("div", { className: "flex flex-wrap items-center gap-1", children: [
1902
+ /* @__PURE__ */ jsx11("span", { className: "w-16 text-[11px] font-medium uppercase tracking-wide text-muted-foreground", children: "Pages" }),
1903
+ pages.map((p, i) => /* @__PURE__ */ jsx11("button", { type: "button", className: buttonClass9, "aria-current": state.selection.includes(p.id), "data-vector-page": p.id, title: `Go to ${p.name ?? `page ${i + 1}`}`, onClick: () => controller.goToPage(p.id), children: i + 1 }, p.id)),
1904
+ masters.map((m) => /* @__PURE__ */ jsx11("button", { type: "button", className: buttonClass9, "aria-current": state.selection.includes(m.id), "data-vector-master-page": m.id, title: `Go to master page ${m.name ?? ""}`, onClick: () => controller.goToPage(m.id), children: m.name ?? "Master" }, m.id)),
1905
+ /* @__PURE__ */ jsx11("button", { type: "button", className: buttonClass9, "data-vector-pages-action": "add", title: "Add a page after the last", onClick: () => controller.addPage(), children: "+ Page" }),
1906
+ /* @__PURE__ */ jsx11("button", { type: "button", className: buttonClass9, "data-vector-pages-action": "master", title: "Add a master page used by every page (headers, footers, {page} numbers)", onClick: () => controller.addMasterPage({ assignTo: "all" }), children: "+ Master" })
1907
+ ] }),
1908
+ page && masters.length > 0 && /* @__PURE__ */ jsxs11("div", { className: "flex flex-wrap items-center gap-1", children: [
1909
+ /* @__PURE__ */ jsx11("span", { className: "w-16" }),
1910
+ /* @__PURE__ */ jsxs11("select", { "aria-label": "Master page", className: selectClass7, value: page.frame.masterId ?? "", onChange: (e) => controller.setPageMaster(e.target.value || null, { ids: [page.id] }), children: [
1911
+ /* @__PURE__ */ jsx11("option", { value: "", children: "No master" }),
1912
+ masters.map((m) => /* @__PURE__ */ jsx11("option", { value: m.id, children: m.name ?? "Master" }, m.id))
1913
+ ] })
1914
+ ] })
1915
+ ] });
1916
+ }
1917
+
1889
1918
  // src/vector/VectorConnectorPanel.tsx
1890
1919
  import {
1891
1920
  VECTOR_CONNECTOR_ROUTES,
@@ -1894,36 +1923,36 @@ import {
1894
1923
  VECTOR_CONNECTOR_SIDE_LABELS,
1895
1924
  getVectorConnectorPanel
1896
1925
  } from "@pixerate/editor/vector";
1897
- import { Fragment as Fragment2, jsx as jsx11, jsxs as jsxs11 } from "react/jsx-runtime";
1898
- var buttonClass9 = "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";
1899
- var selectClass7 = "h-7 min-w-0 flex-1 rounded border border-border bg-background px-1 text-xs";
1926
+ import { Fragment as Fragment2, jsx as jsx12, jsxs as jsxs12 } from "react/jsx-runtime";
1927
+ var buttonClass10 = "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";
1928
+ var selectClass8 = "h-7 min-w-0 flex-1 rounded border border-border bg-background px-1 text-xs";
1900
1929
  var numberClass5 = "h-7 w-16 rounded border border-border bg-background px-1.5 text-xs";
1901
1930
  function VectorConnectorPanel({ controller, className }) {
1902
1931
  const state = useVectorEditorState(controller);
1903
1932
  const panel = getVectorConnectorPanel(state);
1904
1933
  if (!panel) return null;
1905
1934
  const ids = [panel.id];
1906
- const end = (key, label, e) => /* @__PURE__ */ jsxs11("div", { className: "flex items-center gap-1", "data-vector-connector-end": key, children: [
1907
- /* @__PURE__ */ jsx11("span", { className: "w-16 text-xs text-foreground", children: label }),
1908
- e ? /* @__PURE__ */ jsxs11(Fragment2, { children: [
1909
- /* @__PURE__ */ jsx11(
1935
+ const end = (key, label, e) => /* @__PURE__ */ jsxs12("div", { className: "flex items-center gap-1", "data-vector-connector-end": key, children: [
1936
+ /* @__PURE__ */ jsx12("span", { className: "w-16 text-xs text-foreground", children: label }),
1937
+ e ? /* @__PURE__ */ jsxs12(Fragment2, { children: [
1938
+ /* @__PURE__ */ jsx12(
1910
1939
  "select",
1911
1940
  {
1912
1941
  "aria-label": `${label} side`,
1913
- className: selectClass7,
1942
+ className: selectClass8,
1914
1943
  value: e.side ?? "auto",
1915
1944
  onChange: (ev) => controller.setConnector({ [key]: { nodeId: e.nodeId, side: ev.target.value } }, { ids }),
1916
- children: VECTOR_CONNECTOR_SIDES.map((s) => /* @__PURE__ */ jsx11("option", { value: s, children: VECTOR_CONNECTOR_SIDE_LABELS[s] }, s))
1945
+ children: VECTOR_CONNECTOR_SIDES.map((s) => /* @__PURE__ */ jsx12("option", { value: s, children: VECTOR_CONNECTOR_SIDE_LABELS[s] }, s))
1917
1946
  }
1918
1947
  ),
1919
- /* @__PURE__ */ jsx11("button", { type: "button", className: buttonClass9, title: "Unglue: the end stays where it is", "aria-label": `Unglue ${label.toLowerCase()}`, onClick: () => controller.setConnector({ [key]: null }, { ids }), children: "Unglue" })
1920
- ] }) : /* @__PURE__ */ jsx11("span", { className: "text-[11px] text-muted-foreground", children: "Free" })
1948
+ /* @__PURE__ */ jsx12("button", { type: "button", className: buttonClass10, title: "Unglue: the end stays where it is", "aria-label": `Unglue ${label.toLowerCase()}`, onClick: () => controller.setConnector({ [key]: null }, { ids }), children: "Unglue" })
1949
+ ] }) : /* @__PURE__ */ jsx12("span", { className: "text-[11px] text-muted-foreground", children: "Free" })
1921
1950
  ] });
1922
- return /* @__PURE__ */ jsxs11("div", { className: `space-y-2 ${className ?? ""}`, "data-vector-connector-panel": panel.id, "aria-label": "Connector", children: [
1923
- /* @__PURE__ */ jsxs11("div", { className: "flex items-center gap-1", children: [
1924
- /* @__PURE__ */ jsx11("span", { className: "w-16 text-[11px] font-medium uppercase tracking-wide text-muted-foreground", children: "Connector" }),
1925
- /* @__PURE__ */ jsx11("select", { "aria-label": "Connector route", className: selectClass7, value: panel.route, onChange: (e) => controller.setConnector({ route: e.target.value }, { ids }), children: VECTOR_CONNECTOR_ROUTES.map((r) => /* @__PURE__ */ jsx11("option", { value: r, children: VECTOR_CONNECTOR_ROUTE_LABELS[r] }, r)) }),
1926
- panel.route === "elbow" && /* @__PURE__ */ jsx11(
1951
+ return /* @__PURE__ */ jsxs12("div", { className: `space-y-2 ${className ?? ""}`, "data-vector-connector-panel": panel.id, "aria-label": "Connector", children: [
1952
+ /* @__PURE__ */ jsxs12("div", { className: "flex items-center gap-1", children: [
1953
+ /* @__PURE__ */ jsx12("span", { className: "w-16 text-[11px] font-medium uppercase tracking-wide text-muted-foreground", children: "Connector" }),
1954
+ /* @__PURE__ */ jsx12("select", { "aria-label": "Connector route", className: selectClass8, value: panel.route, onChange: (e) => controller.setConnector({ route: e.target.value }, { ids }), children: VECTOR_CONNECTOR_ROUTES.map((r) => /* @__PURE__ */ jsx12("option", { value: r, children: VECTOR_CONNECTOR_ROUTE_LABELS[r] }, r)) }),
1955
+ panel.route === "elbow" && /* @__PURE__ */ jsx12(
1927
1956
  "input",
1928
1957
  {
1929
1958
  type: "number",
@@ -1948,8 +1977,8 @@ function VectorConnectorPanel({ controller, className }) {
1948
1977
  // src/vector/VectorShadingPanel.tsx
1949
1978
  import { useState as useState5 } from "react";
1950
1979
  import { isVectorFreeformGradient, isVectorMeshGradient, vectorPaintToHex as vectorPaintToHex4 } from "@pixerate/editor/vector";
1951
- import { Fragment as Fragment3, jsx as jsx12, jsxs as jsxs12 } from "react/jsx-runtime";
1952
- var buttonClass10 = "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";
1980
+ import { Fragment as Fragment3, jsx as jsx13, jsxs as jsxs13 } from "react/jsx-runtime";
1981
+ var buttonClass11 = "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";
1953
1982
  var numberClass6 = "h-7 w-12 rounded border border-border bg-background px-1.5 text-xs";
1954
1983
  function VectorShadingPanel({ controller, className }) {
1955
1984
  const state = useVectorEditorState(controller);
@@ -1969,29 +1998,29 @@ function VectorShadingPanel({ controller, className }) {
1969
1998
  if (mesh) controller.setMeshPoint(path.id, sel.index, patch, merge ? { merge } : {});
1970
1999
  else controller.setFreeformPoint(path.id, sel.index, patch, merge ? { merge } : {});
1971
2000
  };
1972
- return /* @__PURE__ */ jsxs12("div", { className: `space-y-2 ${className ?? ""}`, "data-vector-shading-panel": path.id, "aria-label": "Mesh and freeform gradient", children: [
1973
- /* @__PURE__ */ jsxs12("div", { className: "flex flex-wrap items-center gap-1", children: [
1974
- /* @__PURE__ */ jsx12("span", { className: "w-16 text-[11px] font-medium uppercase tracking-wide text-muted-foreground", children: "Shading" }),
1975
- /* @__PURE__ */ jsx12("input", { type: "number", min: 1, max: 32, "aria-label": "Mesh rows", title: "Mesh rows", className: numberClass6, value: rows, onChange: (e) => setRows(Math.max(1, Math.min(32, parseInt(e.target.value, 10) || 1))) }),
1976
- /* @__PURE__ */ jsx12("span", { className: "text-xs text-muted-foreground", children: "\xD7" }),
1977
- /* @__PURE__ */ jsx12("input", { type: "number", min: 1, max: 32, "aria-label": "Mesh columns", title: "Mesh columns", className: numberClass6, value: columns, onChange: (e) => setColumns(Math.max(1, Math.min(32, parseInt(e.target.value, 10) || 1))) }),
1978
- /* @__PURE__ */ jsx12("button", { type: "button", className: buttonClass10, "data-vector-shading-action": "mesh", title: "Create Gradient Mesh: smooth patches whose points blend colours (edit them with the Gradient tool)", onClick: () => controller.setMeshGradient({ rows, columns }, { ids: [path.id] }), children: "Mesh" }),
1979
- /* @__PURE__ */ jsx12("button", { type: "button", className: buttonClass10, "data-vector-shading-action": "freeform", title: "Freeform Gradient: colour points blended across the shape (click inside with the Gradient tool to add one)", onClick: () => controller.setFreeformGradient({}, { ids: [path.id] }), children: "Freeform" })
2001
+ return /* @__PURE__ */ jsxs13("div", { className: `space-y-2 ${className ?? ""}`, "data-vector-shading-panel": path.id, "aria-label": "Mesh and freeform gradient", children: [
2002
+ /* @__PURE__ */ jsxs13("div", { className: "flex flex-wrap items-center gap-1", children: [
2003
+ /* @__PURE__ */ jsx13("span", { className: "w-16 text-[11px] font-medium uppercase tracking-wide text-muted-foreground", children: "Shading" }),
2004
+ /* @__PURE__ */ jsx13("input", { type: "number", min: 1, max: 32, "aria-label": "Mesh rows", title: "Mesh rows", className: numberClass6, value: rows, onChange: (e) => setRows(Math.max(1, Math.min(32, parseInt(e.target.value, 10) || 1))) }),
2005
+ /* @__PURE__ */ jsx13("span", { className: "text-xs text-muted-foreground", children: "\xD7" }),
2006
+ /* @__PURE__ */ jsx13("input", { type: "number", min: 1, max: 32, "aria-label": "Mesh columns", title: "Mesh columns", className: numberClass6, value: columns, onChange: (e) => setColumns(Math.max(1, Math.min(32, parseInt(e.target.value, 10) || 1))) }),
2007
+ /* @__PURE__ */ jsx13("button", { type: "button", className: buttonClass11, "data-vector-shading-action": "mesh", title: "Create Gradient Mesh: smooth patches whose points blend colours (edit them with the Gradient tool)", onClick: () => controller.setMeshGradient({ rows, columns }, { ids: [path.id] }), children: "Mesh" }),
2008
+ /* @__PURE__ */ jsx13("button", { type: "button", className: buttonClass11, "data-vector-shading-action": "freeform", title: "Freeform Gradient: colour points blended across the shape (click inside with the Gradient tool to add one)", onClick: () => controller.setFreeformGradient({}, { ids: [path.id] }), children: "Freeform" })
1980
2009
  ] }),
1981
- (mesh || free) && !sel && /* @__PURE__ */ jsxs12("p", { className: "text-[11px] text-muted-foreground", children: [
2010
+ (mesh || free) && !sel && /* @__PURE__ */ jsxs13("p", { className: "text-[11px] text-muted-foreground", children: [
1982
2011
  "Gradient tool (G): drag points; ",
1983
2012
  free ? "click inside to add one, Alt-click to remove." : "pick one to colour it."
1984
2013
  ] }),
1985
- sel && /* @__PURE__ */ jsxs12("div", { className: "flex flex-wrap items-center gap-2", "data-vector-shading-point": sel.index, children: [
1986
- /* @__PURE__ */ jsxs12("span", { className: "w-16 text-xs text-foreground", children: [
2014
+ sel && /* @__PURE__ */ jsxs13("div", { className: "flex flex-wrap items-center gap-2", "data-vector-shading-point": sel.index, children: [
2015
+ /* @__PURE__ */ jsxs13("span", { className: "w-16 text-xs text-foreground", children: [
1987
2016
  "Point ",
1988
2017
  sel.index
1989
2018
  ] }),
1990
- /* @__PURE__ */ jsx12("input", { type: "color", "aria-label": "Point colour", className: "h-7 w-10 cursor-pointer rounded border border-border bg-background", value: vectorPaintToHex4(color ?? "#000000") ?? "#000000", onChange: (e) => edit({ color: e.target.value }, "shading-color") }),
1991
- /* @__PURE__ */ jsx12("input", { type: "range", min: 0, max: 1, step: 0.05, "aria-label": "Point opacity", title: "Opacity", className: "h-4 w-20 accent-primary", value: opacity, onChange: (e) => edit({ opacity: parseFloat(e.target.value) }, "shading-opacity") }),
1992
- free && /* @__PURE__ */ jsxs12(Fragment3, { children: [
1993
- /* @__PURE__ */ jsx12("input", { type: "range", min: 0, max: 1, step: 0.05, "aria-label": "Point spread", title: "Spread: pure colour round the point", className: "h-4 w-20 accent-primary", value: spread, onChange: (e) => edit({ spread: parseFloat(e.target.value) }, "shading-spread") }),
1994
- /* @__PURE__ */ jsx12("button", { type: "button", className: buttonClass10, "aria-label": "Remove point", onClick: () => controller.removeFreeformPoint(path.id, sel.index), children: "Remove" })
2019
+ /* @__PURE__ */ jsx13("input", { type: "color", "aria-label": "Point colour", className: "h-7 w-10 cursor-pointer rounded border border-border bg-background", value: vectorPaintToHex4(color ?? "#000000") ?? "#000000", onChange: (e) => edit({ color: e.target.value }, "shading-color") }),
2020
+ /* @__PURE__ */ jsx13("input", { type: "range", min: 0, max: 1, step: 0.05, "aria-label": "Point opacity", title: "Opacity", className: "h-4 w-20 accent-primary", value: opacity, onChange: (e) => edit({ opacity: parseFloat(e.target.value) }, "shading-opacity") }),
2021
+ free && /* @__PURE__ */ jsxs13(Fragment3, { children: [
2022
+ /* @__PURE__ */ jsx13("input", { type: "range", min: 0, max: 1, step: 0.05, "aria-label": "Point spread", title: "Spread: pure colour round the point", className: "h-4 w-20 accent-primary", value: spread, onChange: (e) => edit({ spread: parseFloat(e.target.value) }, "shading-spread") }),
2023
+ /* @__PURE__ */ jsx13("button", { type: "button", className: buttonClass11, "aria-label": "Remove point", onClick: () => controller.removeFreeformPoint(path.id, sel.index), children: "Remove" })
1995
2024
  ] })
1996
2025
  ] })
1997
2026
  ] });
@@ -1999,9 +2028,9 @@ function VectorShadingPanel({ controller, className }) {
1999
2028
 
2000
2029
  // src/vector/VectorTracePanel.tsx
2001
2030
  import { useRef as useRef4, useState as useState6 } from "react";
2002
- import { jsx as jsx13, jsxs as jsxs13 } from "react/jsx-runtime";
2003
- var buttonClass11 = "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";
2004
- var selectClass8 = "h-7 min-w-0 flex-1 rounded border border-border bg-background px-1 text-xs";
2031
+ import { jsx as jsx14, jsxs as jsxs14 } from "react/jsx-runtime";
2032
+ var buttonClass12 = "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";
2033
+ var selectClass9 = "h-7 min-w-0 flex-1 rounded border border-border bg-background px-1 text-xs";
2005
2034
  var numberClass7 = "h-7 w-12 rounded border border-border bg-background px-1.5 text-xs";
2006
2035
  async function filePixels(file, max = 1e6) {
2007
2036
  const bitmap = await createImageBitmap(file);
@@ -2031,16 +2060,16 @@ function VectorTracePanel({ controller, className }) {
2031
2060
  setMessage("That file could not be read as an image.");
2032
2061
  }
2033
2062
  };
2034
- return /* @__PURE__ */ jsxs13("div", { className: `space-y-1 ${className ?? ""}`, "data-vector-trace-panel": "", "aria-label": "Image trace", children: [
2035
- /* @__PURE__ */ jsxs13("div", { className: "flex items-center gap-1", children: [
2036
- /* @__PURE__ */ jsx13("span", { className: "w-16 text-[11px] font-medium uppercase tracking-wide text-muted-foreground", children: "Trace" }),
2037
- /* @__PURE__ */ jsxs13("select", { "aria-label": "Trace mode", className: selectClass8, value: mode, onChange: (e) => setMode(e.target.value), children: [
2038
- /* @__PURE__ */ jsx13("option", { value: "threshold", children: "Black & white" }),
2039
- /* @__PURE__ */ jsx13("option", { value: "color", children: "Colours" })
2063
+ return /* @__PURE__ */ jsxs14("div", { className: `space-y-1 ${className ?? ""}`, "data-vector-trace-panel": "", "aria-label": "Image trace", children: [
2064
+ /* @__PURE__ */ jsxs14("div", { className: "flex items-center gap-1", children: [
2065
+ /* @__PURE__ */ jsx14("span", { className: "w-16 text-[11px] font-medium uppercase tracking-wide text-muted-foreground", children: "Trace" }),
2066
+ /* @__PURE__ */ jsxs14("select", { "aria-label": "Trace mode", className: selectClass9, value: mode, onChange: (e) => setMode(e.target.value), children: [
2067
+ /* @__PURE__ */ jsx14("option", { value: "threshold", children: "Black & white" }),
2068
+ /* @__PURE__ */ jsx14("option", { value: "color", children: "Colours" })
2040
2069
  ] }),
2041
- mode === "color" && /* @__PURE__ */ jsx13("input", { type: "number", min: 2, max: 32, "aria-label": "Trace colours", title: "Colours", className: numberClass7, value: colors, onChange: (e) => setColors(Math.max(2, Math.min(32, parseInt(e.target.value, 10) || 2))) }),
2042
- /* @__PURE__ */ jsx13("button", { type: "button", className: buttonClass11, "data-vector-trace-action": "", title: "Image Trace: pick an image to turn into paths", onClick: () => input.current?.click(), children: "Image\u2026" }),
2043
- /* @__PURE__ */ jsx13(
2070
+ mode === "color" && /* @__PURE__ */ jsx14("input", { type: "number", min: 2, max: 32, "aria-label": "Trace colours", title: "Colours", className: numberClass7, value: colors, onChange: (e) => setColors(Math.max(2, Math.min(32, parseInt(e.target.value, 10) || 2))) }),
2071
+ /* @__PURE__ */ jsx14("button", { type: "button", className: buttonClass12, "data-vector-trace-action": "", title: "Image Trace: pick an image to turn into paths", onClick: () => input.current?.click(), children: "Image\u2026" }),
2072
+ /* @__PURE__ */ jsx14(
2044
2073
  "input",
2045
2074
  {
2046
2075
  ref: input,
@@ -2055,15 +2084,15 @@ function VectorTracePanel({ controller, className }) {
2055
2084
  }
2056
2085
  )
2057
2086
  ] }),
2058
- message && /* @__PURE__ */ jsx13("p", { className: "text-[11px] text-red-600", children: message })
2087
+ message && /* @__PURE__ */ jsx14("p", { className: "text-[11px] text-red-600", children: message })
2059
2088
  ] });
2060
2089
  }
2061
2090
 
2062
2091
  // src/vector/VectorSymmetryPanel.tsx
2063
2092
  import { VECTOR_SYMMETRY_MODES, VECTOR_SYMMETRY_MODE_LABELS, isVectorSymmetryGroup } from "@pixerate/editor/vector";
2064
- import { Fragment as Fragment4, jsx as jsx14, jsxs as jsxs14 } from "react/jsx-runtime";
2065
- var buttonClass12 = "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";
2066
- var selectClass9 = "h-7 min-w-0 flex-1 rounded border border-border bg-background px-1 text-xs";
2093
+ import { Fragment as Fragment4, jsx as jsx15, jsxs as jsxs15 } from "react/jsx-runtime";
2094
+ 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";
2095
+ var selectClass10 = "h-7 min-w-0 flex-1 rounded border border-border bg-background px-1 text-xs";
2067
2096
  var numberClass8 = "h-7 w-12 rounded border border-border bg-background px-1.5 text-xs";
2068
2097
  function VectorSymmetryPanel({ controller, className }) {
2069
2098
  const state = useVectorEditorState(controller);
@@ -2071,27 +2100,27 @@ function VectorSymmetryPanel({ controller, className }) {
2071
2100
  const s = group?.symmetry;
2072
2101
  const ids = group ? [group.id] : void 0;
2073
2102
  const drawing = !!group && state.symmetryTarget === group.id;
2074
- return /* @__PURE__ */ jsxs14("div", { className: `space-y-2 ${className ?? ""}`, "data-vector-symmetry-panel": "", "aria-label": "Symmetry", children: [
2075
- /* @__PURE__ */ jsxs14("div", { className: "flex flex-wrap items-center gap-1", children: [
2076
- /* @__PURE__ */ jsx14("span", { className: "w-16 text-[11px] font-medium uppercase tracking-wide text-muted-foreground", children: "Symmetry" }),
2077
- !group && /* @__PURE__ */ jsx14("button", { type: "button", className: buttonClass12, "data-vector-symmetry-action": "make", title: "Make Symmetry: the selection (or nothing yet) mirrored live across a vertical axis", onClick: () => controller.makeSymmetry({ mode: "vertical" }), children: "Mirror" }),
2078
- !group && /* @__PURE__ */ jsx14("button", { type: "button", className: buttonClass12, "data-vector-symmetry-action": "radial", title: "Make Symmetry: the selection repeated round its centre", onClick: () => controller.makeSymmetry({ mode: "radial", copies: 6 }), children: "Radial" }),
2079
- group && s && /* @__PURE__ */ jsxs14(Fragment4, { children: [
2080
- /* @__PURE__ */ jsx14("select", { "aria-label": "Symmetry mode", className: selectClass9, value: s.mode, onChange: (e) => controller.setSymmetry({ mode: e.target.value }, { ids }), children: VECTOR_SYMMETRY_MODES.map((m) => /* @__PURE__ */ jsx14("option", { value: m, children: VECTOR_SYMMETRY_MODE_LABELS[m] }, m)) }),
2081
- s.mode === "radial" && /* @__PURE__ */ jsxs14(Fragment4, { children: [
2082
- /* @__PURE__ */ jsx14("input", { type: "number", min: 2, max: 64, "aria-label": "Symmetry copies", title: "Copies round the circle", className: numberClass8, value: s.copies ?? 6, onChange: (e) => controller.setSymmetry({ copies: Math.max(2, Math.min(64, parseInt(e.target.value, 10) || 2)) }, { ids }) }),
2083
- /* @__PURE__ */ jsxs14("label", { className: "flex items-center gap-1 text-xs text-foreground", title: "Mirror each copy too (kaleidoscope)", children: [
2084
- /* @__PURE__ */ jsx14("input", { type: "checkbox", "aria-label": "Mirror copies", checked: !!s.mirror, onChange: (e) => controller.setSymmetry({ mirror: e.target.checked }, { ids }) }),
2103
+ return /* @__PURE__ */ jsxs15("div", { className: `space-y-2 ${className ?? ""}`, "data-vector-symmetry-panel": "", "aria-label": "Symmetry", children: [
2104
+ /* @__PURE__ */ jsxs15("div", { className: "flex flex-wrap items-center gap-1", children: [
2105
+ /* @__PURE__ */ jsx15("span", { className: "w-16 text-[11px] font-medium uppercase tracking-wide text-muted-foreground", children: "Symmetry" }),
2106
+ !group && /* @__PURE__ */ jsx15("button", { type: "button", className: buttonClass13, "data-vector-symmetry-action": "make", title: "Make Symmetry: the selection (or nothing yet) mirrored live across a vertical axis", onClick: () => controller.makeSymmetry({ mode: "vertical" }), children: "Mirror" }),
2107
+ !group && /* @__PURE__ */ jsx15("button", { type: "button", className: buttonClass13, "data-vector-symmetry-action": "radial", title: "Make Symmetry: the selection repeated round its centre", onClick: () => controller.makeSymmetry({ mode: "radial", copies: 6 }), children: "Radial" }),
2108
+ group && s && /* @__PURE__ */ jsxs15(Fragment4, { children: [
2109
+ /* @__PURE__ */ jsx15("select", { "aria-label": "Symmetry mode", className: selectClass10, value: s.mode, onChange: (e) => controller.setSymmetry({ mode: e.target.value }, { ids }), children: VECTOR_SYMMETRY_MODES.map((m) => /* @__PURE__ */ jsx15("option", { value: m, children: VECTOR_SYMMETRY_MODE_LABELS[m] }, m)) }),
2110
+ s.mode === "radial" && /* @__PURE__ */ jsxs15(Fragment4, { children: [
2111
+ /* @__PURE__ */ jsx15("input", { type: "number", min: 2, max: 64, "aria-label": "Symmetry copies", title: "Copies round the circle", className: numberClass8, value: s.copies ?? 6, onChange: (e) => controller.setSymmetry({ copies: Math.max(2, Math.min(64, parseInt(e.target.value, 10) || 2)) }, { ids }) }),
2112
+ /* @__PURE__ */ jsxs15("label", { className: "flex items-center gap-1 text-xs text-foreground", title: "Mirror each copy too (kaleidoscope)", children: [
2113
+ /* @__PURE__ */ jsx15("input", { type: "checkbox", "aria-label": "Mirror copies", checked: !!s.mirror, onChange: (e) => controller.setSymmetry({ mirror: e.target.checked }, { ids }) }),
2085
2114
  "Mirror"
2086
2115
  ] })
2087
2116
  ] })
2088
2117
  ] })
2089
2118
  ] }),
2090
- group && /* @__PURE__ */ jsxs14("div", { className: "flex flex-wrap items-center gap-1", children: [
2091
- /* @__PURE__ */ jsx14("span", { className: "w-16" }),
2092
- /* @__PURE__ */ jsx14("button", { type: "button", className: buttonClass12, "aria-pressed": drawing, "data-vector-symmetry-action": "draw", title: "Draw inside: new pen, pencil, shape and text art goes into the group, mirrored as you draw (Escape stops)", onClick: () => drawing ? controller.exitSymmetry() : controller.enterSymmetry(group.id), children: "Draw inside" }),
2093
- /* @__PURE__ */ jsx14("button", { type: "button", className: buttonClass12, title: "Expand: keep the copies as real objects", onClick: () => controller.releaseSymmetry({ ids, expand: true }), children: "Expand" }),
2094
- /* @__PURE__ */ jsx14("button", { type: "button", className: buttonClass12, title: "Release: stop repeating (the copies go)", onClick: () => controller.releaseSymmetry({ ids }), children: "Release" })
2119
+ group && /* @__PURE__ */ jsxs15("div", { className: "flex flex-wrap items-center gap-1", children: [
2120
+ /* @__PURE__ */ jsx15("span", { className: "w-16" }),
2121
+ /* @__PURE__ */ jsx15("button", { type: "button", className: buttonClass13, "aria-pressed": drawing, "data-vector-symmetry-action": "draw", title: "Draw inside: new pen, pencil, shape and text art goes into the group, mirrored as you draw (Escape stops)", onClick: () => drawing ? controller.exitSymmetry() : controller.enterSymmetry(group.id), children: "Draw inside" }),
2122
+ /* @__PURE__ */ jsx15("button", { type: "button", className: buttonClass13, title: "Expand: keep the copies as real objects", onClick: () => controller.releaseSymmetry({ ids, expand: true }), children: "Expand" }),
2123
+ /* @__PURE__ */ jsx15("button", { type: "button", className: buttonClass13, title: "Release: stop repeating (the copies go)", onClick: () => controller.releaseSymmetry({ ids }), children: "Release" })
2095
2124
  ] })
2096
2125
  ] });
2097
2126
  }
@@ -2099,9 +2128,9 @@ function VectorSymmetryPanel({ controller, className }) {
2099
2128
  // src/vector/VectorPerspectivePanel.tsx
2100
2129
  import React9 from "react";
2101
2130
  import { vectorPerspectivePlanes, vectorPerspectivePreset } from "@pixerate/editor/vector";
2102
- import { jsx as jsx15, jsxs as jsxs15 } from "react/jsx-runtime";
2103
- 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";
2104
- var selectClass10 = "h-7 min-w-0 flex-1 rounded border border-border bg-background px-1 text-xs";
2131
+ import { jsx as jsx16, jsxs as jsxs16 } from "react/jsx-runtime";
2132
+ var buttonClass14 = "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";
2133
+ var selectClass11 = "h-7 min-w-0 flex-1 rounded border border-border bg-background px-1 text-xs";
2105
2134
  var PLANE_LABELS = { floor: "Floor", left: "Left wall", right: "Right wall", side: "Side wall", front: "Front" };
2106
2135
  function VectorPerspectivePanel({ controller, className }) {
2107
2136
  const state = useVectorEditorState(controller);
@@ -2110,41 +2139,41 @@ function VectorPerspectivePanel({ controller, className }) {
2110
2139
  const [attachTo, setAttachTo] = React9.useState("");
2111
2140
  const target = (state.perspectivePlane ?? attachTo) || planes[0];
2112
2141
  const add = (n) => controller.setPerspective(vectorPerspectivePreset(n, state.document.width, state.document.height));
2113
- return /* @__PURE__ */ jsxs15("div", { className: `space-y-2 ${className ?? ""}`, "data-vector-perspective-panel": "", "aria-label": "Perspective", children: [
2114
- /* @__PURE__ */ jsxs15("div", { className: "flex flex-wrap items-center gap-1", children: [
2115
- /* @__PURE__ */ jsx15("span", { className: "w-16 text-[11px] font-medium uppercase tracking-wide text-muted-foreground", children: "Perspective" }),
2116
- [1, 2, 3].map((n) => /* @__PURE__ */ jsxs15("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: [
2142
+ return /* @__PURE__ */ jsxs16("div", { className: `space-y-2 ${className ?? ""}`, "data-vector-perspective-panel": "", "aria-label": "Perspective", children: [
2143
+ /* @__PURE__ */ jsxs16("div", { className: "flex flex-wrap items-center gap-1", children: [
2144
+ /* @__PURE__ */ jsx16("span", { className: "w-16 text-[11px] font-medium uppercase tracking-wide text-muted-foreground", children: "Perspective" }),
2145
+ [1, 2, 3].map((n) => /* @__PURE__ */ jsxs16("button", { type: "button", className: buttonClass14, "aria-pressed": grid?.vanishingPoints.length === n, "data-vector-perspective-preset": n, title: `A ${n}-point perspective grid for the page`, onClick: () => add(n), children: [
2117
2146
  n,
2118
2147
  "-pt"
2119
2148
  ] }, n))
2120
2149
  ] }),
2121
- grid && /* @__PURE__ */ jsxs15("div", { className: "flex flex-wrap items-center gap-1", children: [
2122
- /* @__PURE__ */ jsx15("span", { className: "w-16" }),
2123
- /* @__PURE__ */ jsxs15("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: [
2124
- /* @__PURE__ */ jsx15("option", { value: "", children: "Draw flat" }),
2125
- planes.map((p) => /* @__PURE__ */ jsxs15("option", { value: p, children: [
2150
+ grid && /* @__PURE__ */ jsxs16("div", { className: "flex flex-wrap items-center gap-1", children: [
2151
+ /* @__PURE__ */ jsx16("span", { className: "w-16" }),
2152
+ /* @__PURE__ */ jsxs16("select", { "aria-label": "Draw on plane", title: "New rectangles, ellipses, polygons and stars are drawn on this plane", className: selectClass11, value: state.perspectivePlane ?? "", onChange: (e) => controller.setPerspectivePlane(e.target.value || null), children: [
2153
+ /* @__PURE__ */ jsx16("option", { value: "", children: "Draw flat" }),
2154
+ planes.map((p) => /* @__PURE__ */ jsxs16("option", { value: p, children: [
2126
2155
  "Draw on ",
2127
2156
  PLANE_LABELS[p].toLowerCase()
2128
2157
  ] }, p))
2129
2158
  ] })
2130
2159
  ] }),
2131
- grid && /* @__PURE__ */ jsxs15("div", { className: "flex flex-wrap items-center gap-1", children: [
2132
- /* @__PURE__ */ jsx15("span", { className: "w-16" }),
2133
- !state.perspectivePlane && /* @__PURE__ */ jsx15("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__ */ jsx15("option", { value: p, children: PLANE_LABELS[p] }, p)) }),
2134
- /* @__PURE__ */ jsx15("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" }),
2135
- /* @__PURE__ */ jsxs15("label", { className: "flex items-center gap-1 text-xs text-foreground", title: "Show the grid on the canvas", children: [
2136
- /* @__PURE__ */ jsx15("input", { type: "checkbox", "aria-label": "Show perspective grid", checked: grid.visible !== false, onChange: (e) => controller.setPerspective({ ...grid, visible: e.target.checked }) }),
2160
+ grid && /* @__PURE__ */ jsxs16("div", { className: "flex flex-wrap items-center gap-1", children: [
2161
+ /* @__PURE__ */ jsx16("span", { className: "w-16" }),
2162
+ !state.perspectivePlane && /* @__PURE__ */ jsx16("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__ */ jsx16("option", { value: p, children: PLANE_LABELS[p] }, p)) }),
2163
+ /* @__PURE__ */ jsx16("button", { type: "button", className: buttonClass14, disabled: !state.selection.length, title: "Lay the selection onto the plane (read flat from the grid's origin)", onClick: () => controller.attachToPerspective(target), children: "Attach" }),
2164
+ /* @__PURE__ */ jsxs16("label", { className: "flex items-center gap-1 text-xs text-foreground", title: "Show the grid on the canvas", children: [
2165
+ /* @__PURE__ */ jsx16("input", { type: "checkbox", "aria-label": "Show perspective grid", checked: grid.visible !== false, onChange: (e) => controller.setPerspective({ ...grid, visible: e.target.checked }) }),
2137
2166
  "Show"
2138
2167
  ] }),
2139
- /* @__PURE__ */ jsx15("button", { type: "button", className: buttonClass13, title: "Remove the grid (art on it stays)", onClick: () => controller.setPerspective(null), children: "Remove" })
2168
+ /* @__PURE__ */ jsx16("button", { type: "button", className: buttonClass14, title: "Remove the grid (art on it stays)", onClick: () => controller.setPerspective(null), children: "Remove" })
2140
2169
  ] })
2141
2170
  ] });
2142
2171
  }
2143
2172
 
2144
2173
  // src/vector/VectorLensPanel.tsx
2145
2174
  import { VECTOR_LENS_LABELS, VECTOR_LENS_SPECS, VECTOR_LENS_TYPES } from "@pixerate/editor/vector";
2146
- import { jsx as jsx16, jsxs as jsxs16 } from "react/jsx-runtime";
2147
- var selectClass11 = "h-7 min-w-0 flex-1 rounded border border-border bg-background px-1 text-xs";
2175
+ import { jsx as jsx17, jsxs as jsxs17 } from "react/jsx-runtime";
2176
+ var selectClass12 = "h-7 min-w-0 flex-1 rounded border border-border bg-background px-1 text-xs";
2148
2177
  var numberClass9 = "h-7 w-16 rounded border border-border bg-background px-1.5 text-xs";
2149
2178
  function VectorLensPanel({ controller, className }) {
2150
2179
  const state = useVectorEditorState(controller);
@@ -2153,26 +2182,26 @@ function VectorLensPanel({ controller, className }) {
2153
2182
  const lens = path.lens ?? null;
2154
2183
  const spec = lens ? VECTOR_LENS_SPECS[lens.type] : null;
2155
2184
  const ids = [path.id];
2156
- return /* @__PURE__ */ jsxs16("div", { className: `space-y-2 ${className ?? ""}`, "data-vector-lens-panel": "", "aria-label": "Lens", children: [
2157
- /* @__PURE__ */ jsxs16("div", { className: "flex flex-wrap items-center gap-1", children: [
2158
- /* @__PURE__ */ jsx16("span", { className: "w-16 text-[11px] font-medium uppercase tracking-wide text-muted-foreground", children: "Lens" }),
2159
- /* @__PURE__ */ jsxs16(
2185
+ return /* @__PURE__ */ jsxs17("div", { className: `space-y-2 ${className ?? ""}`, "data-vector-lens-panel": "", "aria-label": "Lens", children: [
2186
+ /* @__PURE__ */ jsxs17("div", { className: "flex flex-wrap items-center gap-1", children: [
2187
+ /* @__PURE__ */ jsx17("span", { className: "w-16 text-[11px] font-medium uppercase tracking-wide text-muted-foreground", children: "Lens" }),
2188
+ /* @__PURE__ */ jsxs17(
2160
2189
  "select",
2161
2190
  {
2162
2191
  "aria-label": "Lens type",
2163
- className: selectClass11,
2192
+ className: selectClass12,
2164
2193
  value: lens?.type ?? "",
2165
2194
  onChange: (e) => e.target.value ? controller.setLens({ type: e.target.value }, { ids }) : controller.setLens(null, { ids }),
2166
2195
  children: [
2167
- /* @__PURE__ */ jsx16("option", { value: "", children: "None" }),
2168
- VECTOR_LENS_TYPES.map((t) => /* @__PURE__ */ jsx16("option", { value: t, children: VECTOR_LENS_LABELS[t] }, t))
2196
+ /* @__PURE__ */ jsx17("option", { value: "", children: "None" }),
2197
+ VECTOR_LENS_TYPES.map((t) => /* @__PURE__ */ jsx17("option", { value: t, children: VECTOR_LENS_LABELS[t] }, t))
2169
2198
  ]
2170
2199
  }
2171
2200
  )
2172
2201
  ] }),
2173
- lens && spec && /* @__PURE__ */ jsxs16("div", { className: "flex flex-wrap items-center gap-1", children: [
2174
- /* @__PURE__ */ jsx16("span", { className: "w-16" }),
2175
- /* @__PURE__ */ jsx16(
2202
+ lens && spec && /* @__PURE__ */ jsxs17("div", { className: "flex flex-wrap items-center gap-1", children: [
2203
+ /* @__PURE__ */ jsx17("span", { className: "w-16" }),
2204
+ /* @__PURE__ */ jsx17(
2176
2205
  "input",
2177
2206
  {
2178
2207
  type: "number",
@@ -2189,7 +2218,7 @@ function VectorLensPanel({ controller, className }) {
2189
2218
  }
2190
2219
  }
2191
2220
  ),
2192
- spec.color !== null && /* @__PURE__ */ jsx16("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}` }) })
2221
+ spec.color !== null && /* @__PURE__ */ jsx17("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}` }) })
2193
2222
  ] })
2194
2223
  ] });
2195
2224
  }
@@ -2207,10 +2236,10 @@ import {
2207
2236
  getVectorGradientEditor,
2208
2237
  vectorPaintToHex as vectorPaintToHex5
2209
2238
  } from "@pixerate/editor/vector";
2210
- import { Fragment as Fragment5, jsx as jsx17, jsxs as jsxs17 } from "react/jsx-runtime";
2211
- var selectClass12 = "h-7 min-w-0 flex-1 rounded border border-border bg-background px-1 text-xs";
2239
+ import { Fragment as Fragment5, jsx as jsx18, jsxs as jsxs18 } from "react/jsx-runtime";
2240
+ var selectClass13 = "h-7 min-w-0 flex-1 rounded border border-border bg-background px-1 text-xs";
2212
2241
  var numberClass10 = "h-7 w-14 rounded border border-border bg-background px-1.5 text-xs";
2213
- 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";
2242
+ var buttonClass15 = "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";
2214
2243
  var percent = (v) => Math.round(v * 1e3) / 10;
2215
2244
  function VectorGradientEditor({ controller, className }) {
2216
2245
  const state = useVectorEditorState(controller);
@@ -2225,23 +2254,23 @@ function VectorGradientEditor({ controller, className }) {
2225
2254
  controller.setStyle({ [ed.target]: vectorPaintToHex5(first?.color) ?? first?.color ?? "#000000" }, void 0, { label: "Solid Colour" });
2226
2255
  } else controller.setGradient({ type: value });
2227
2256
  };
2228
- return /* @__PURE__ */ jsxs17("div", { className: `space-y-1.5 ${className ?? ""}`, "data-vector-gradient-editor": ed.target, "aria-label": "Gradient", children: [
2229
- /* @__PURE__ */ jsxs17("div", { className: "flex items-center gap-1.5", children: [
2230
- /* @__PURE__ */ jsx17("span", { className: "w-16 text-xs text-foreground", children: "Gradient" }),
2231
- ["fill", "stroke"].map((t) => /* @__PURE__ */ jsx17("button", { type: "button", className: buttonClass14, "aria-pressed": ed.target === t, onClick: () => controller.setGradientTarget(t), children: t === "fill" ? "Fill" : "Stroke" }, t)),
2232
- ed.mixed && /* @__PURE__ */ jsx17("span", { className: "text-[11px] text-muted-foreground", children: "Mixed" })
2257
+ return /* @__PURE__ */ jsxs18("div", { className: `space-y-1.5 ${className ?? ""}`, "data-vector-gradient-editor": ed.target, "aria-label": "Gradient", children: [
2258
+ /* @__PURE__ */ jsxs18("div", { className: "flex items-center gap-1.5", children: [
2259
+ /* @__PURE__ */ jsx18("span", { className: "w-16 text-xs text-foreground", children: "Gradient" }),
2260
+ ["fill", "stroke"].map((t) => /* @__PURE__ */ jsx18("button", { type: "button", className: buttonClass15, "aria-pressed": ed.target === t, onClick: () => controller.setGradientTarget(t), children: t === "fill" ? "Fill" : "Stroke" }, t)),
2261
+ ed.mixed && /* @__PURE__ */ jsx18("span", { className: "text-[11px] text-muted-foreground", children: "Mixed" })
2233
2262
  ] }),
2234
- /* @__PURE__ */ jsxs17("div", { className: "flex items-center gap-1.5", children: [
2235
- /* @__PURE__ */ jsx17("span", { className: "w-16 text-xs text-foreground", children: "Type" }),
2236
- /* @__PURE__ */ jsxs17("select", { "aria-label": "Gradient type", className: selectClass12, value: ed.type ?? "solid", onChange: (e) => setType(e.target.value), children: [
2237
- /* @__PURE__ */ jsx17("option", { value: "solid", children: "Solid" }),
2238
- VECTOR_GRADIENT_TYPES.map((t) => /* @__PURE__ */ jsx17("option", { value: t, children: VECTOR_GRADIENT_TYPE_LABELS[t] }, t))
2263
+ /* @__PURE__ */ jsxs18("div", { className: "flex items-center gap-1.5", children: [
2264
+ /* @__PURE__ */ jsx18("span", { className: "w-16 text-xs text-foreground", children: "Type" }),
2265
+ /* @__PURE__ */ jsxs18("select", { "aria-label": "Gradient type", className: selectClass13, value: ed.type ?? "solid", onChange: (e) => setType(e.target.value), children: [
2266
+ /* @__PURE__ */ jsx18("option", { value: "solid", children: "Solid" }),
2267
+ VECTOR_GRADIENT_TYPES.map((t) => /* @__PURE__ */ jsx18("option", { value: t, children: VECTOR_GRADIENT_TYPE_LABELS[t] }, t))
2239
2268
  ] }),
2240
- /* @__PURE__ */ jsx17("button", { type: "button", className: buttonClass14, "aria-label": "Reverse gradient", title: "Reverse gradient", disabled: !g, onClick: () => controller.reverseGradient(), children: "\u21C4" })
2269
+ /* @__PURE__ */ jsx18("button", { type: "button", className: buttonClass15, "aria-label": "Reverse gradient", title: "Reverse gradient", disabled: !g, onClick: () => controller.reverseGradient(), children: "\u21C4" })
2241
2270
  ] }),
2242
- g && /* @__PURE__ */ jsxs17(Fragment5, { children: [
2243
- /* @__PURE__ */ jsxs17("div", { className: "relative h-8", "data-vector-gradient-ramp": "", children: [
2244
- /* @__PURE__ */ jsx17(
2271
+ g && /* @__PURE__ */ jsxs18(Fragment5, { children: [
2272
+ /* @__PURE__ */ jsxs18("div", { className: "relative h-8", "data-vector-gradient-ramp": "", children: [
2273
+ /* @__PURE__ */ jsx18(
2245
2274
  "div",
2246
2275
  {
2247
2276
  role: "button",
@@ -2255,7 +2284,7 @@ function VectorGradientEditor({ controller, className }) {
2255
2284
  }
2256
2285
  }
2257
2286
  ),
2258
- ed.stops.map((s) => /* @__PURE__ */ jsx17(
2287
+ ed.stops.map((s) => /* @__PURE__ */ jsx18(
2259
2288
  "button",
2260
2289
  {
2261
2290
  type: "button",
@@ -2269,8 +2298,8 @@ function VectorGradientEditor({ controller, className }) {
2269
2298
  s.index
2270
2299
  ))
2271
2300
  ] }),
2272
- sel && /* @__PURE__ */ jsxs17("div", { className: "flex flex-wrap items-center gap-1.5", "data-vector-gradient-selected-stop": sel.index, children: [
2273
- /* @__PURE__ */ jsx17(
2301
+ sel && /* @__PURE__ */ jsxs18("div", { className: "flex flex-wrap items-center gap-1.5", "data-vector-gradient-selected-stop": sel.index, children: [
2302
+ /* @__PURE__ */ jsx18(
2274
2303
  "input",
2275
2304
  {
2276
2305
  type: "color",
@@ -2281,9 +2310,9 @@ function VectorGradientEditor({ controller, className }) {
2281
2310
  onBlur: end
2282
2311
  }
2283
2312
  ),
2284
- /* @__PURE__ */ jsxs17("label", { className: "flex items-center gap-1 text-[11px] text-muted-foreground", children: [
2313
+ /* @__PURE__ */ jsxs18("label", { className: "flex items-center gap-1 text-[11px] text-muted-foreground", children: [
2285
2314
  "Opacity",
2286
- /* @__PURE__ */ jsx17(
2315
+ /* @__PURE__ */ jsx18(
2287
2316
  "input",
2288
2317
  {
2289
2318
  type: "number",
@@ -2300,9 +2329,9 @@ function VectorGradientEditor({ controller, className }) {
2300
2329
  }
2301
2330
  )
2302
2331
  ] }),
2303
- /* @__PURE__ */ jsxs17("label", { className: "flex items-center gap-1 text-[11px] text-muted-foreground", children: [
2332
+ /* @__PURE__ */ jsxs18("label", { className: "flex items-center gap-1 text-[11px] text-muted-foreground", children: [
2304
2333
  "At",
2305
- /* @__PURE__ */ jsx17(
2334
+ /* @__PURE__ */ jsx18(
2306
2335
  "input",
2307
2336
  {
2308
2337
  type: "number",
@@ -2319,9 +2348,9 @@ function VectorGradientEditor({ controller, className }) {
2319
2348
  }
2320
2349
  )
2321
2350
  ] }),
2322
- !sel.last && /* @__PURE__ */ jsxs17("label", { className: "flex items-center gap-1 text-[11px] text-muted-foreground", children: [
2351
+ !sel.last && /* @__PURE__ */ jsxs18("label", { className: "flex items-center gap-1 text-[11px] text-muted-foreground", children: [
2323
2352
  "Midpoint",
2324
- /* @__PURE__ */ jsx17(
2353
+ /* @__PURE__ */ jsx18(
2325
2354
  "input",
2326
2355
  {
2327
2356
  type: "number",
@@ -2338,15 +2367,15 @@ function VectorGradientEditor({ controller, className }) {
2338
2367
  }
2339
2368
  )
2340
2369
  ] }),
2341
- /* @__PURE__ */ jsx17("button", { type: "button", className: buttonClass14, "aria-label": "Delete stop", title: "Delete stop", disabled: ed.stops.length <= 2, onClick: () => controller.removeGradientStop(sel.index), children: "\u2715" })
2370
+ /* @__PURE__ */ jsx18("button", { type: "button", className: buttonClass15, "aria-label": "Delete stop", title: "Delete stop", disabled: ed.stops.length <= 2, onClick: () => controller.removeGradientStop(sel.index), children: "\u2715" })
2342
2371
  ] }),
2343
- /* @__PURE__ */ jsxs17("label", { className: "flex items-center gap-1.5 text-xs text-foreground", children: [
2344
- /* @__PURE__ */ jsx17("span", { className: "w-16", children: "Colours" }),
2345
- /* @__PURE__ */ jsx17("select", { "aria-label": "Gradient interpolation", className: selectClass12, value: ed.interpolation, onChange: (e) => controller.setGradient({ interpolation: e.target.value }), children: VECTOR_GRADIENT_INTERPOLATIONS.map((i) => /* @__PURE__ */ jsx17("option", { value: i, children: VECTOR_GRADIENT_INTERPOLATION_LABELS[i] }, i)) })
2372
+ /* @__PURE__ */ jsxs18("label", { className: "flex items-center gap-1.5 text-xs text-foreground", children: [
2373
+ /* @__PURE__ */ jsx18("span", { className: "w-16", children: "Colours" }),
2374
+ /* @__PURE__ */ jsx18("select", { "aria-label": "Gradient interpolation", className: selectClass13, value: ed.interpolation, onChange: (e) => controller.setGradient({ interpolation: e.target.value }), children: VECTOR_GRADIENT_INTERPOLATIONS.map((i) => /* @__PURE__ */ jsx18("option", { value: i, children: VECTOR_GRADIENT_INTERPOLATION_LABELS[i] }, i)) })
2346
2375
  ] }),
2347
- /* @__PURE__ */ jsxs17("label", { className: "flex items-center gap-1.5 text-xs text-foreground", children: [
2348
- /* @__PURE__ */ jsx17("span", { className: "w-16", children: "Smooth" }),
2349
- /* @__PURE__ */ jsx17(
2376
+ /* @__PURE__ */ jsxs18("label", { className: "flex items-center gap-1.5 text-xs text-foreground", children: [
2377
+ /* @__PURE__ */ jsx18("span", { className: "w-16", children: "Smooth" }),
2378
+ /* @__PURE__ */ jsx18(
2350
2379
  "input",
2351
2380
  {
2352
2381
  type: "range",
@@ -2362,23 +2391,23 @@ function VectorGradientEditor({ controller, className }) {
2362
2391
  }
2363
2392
  )
2364
2393
  ] }),
2365
- /* @__PURE__ */ jsxs17("label", { className: "flex items-center gap-1.5 text-xs text-foreground", children: [
2366
- /* @__PURE__ */ jsx17("span", { className: "w-16", children: "Spread" }),
2367
- /* @__PURE__ */ jsx17("select", { "aria-label": "Gradient spread", className: selectClass12, value: ed.spread, onChange: (e) => controller.setGradient({ spread: e.target.value }), children: VECTOR_GRADIENT_SPREADS.map((sp) => /* @__PURE__ */ jsx17("option", { value: sp, children: VECTOR_GRADIENT_SPREAD_LABELS[sp] }, sp)) })
2394
+ /* @__PURE__ */ jsxs18("label", { className: "flex items-center gap-1.5 text-xs text-foreground", children: [
2395
+ /* @__PURE__ */ jsx18("span", { className: "w-16", children: "Spread" }),
2396
+ /* @__PURE__ */ jsx18("select", { "aria-label": "Gradient spread", className: selectClass13, value: ed.spread, onChange: (e) => controller.setGradient({ spread: e.target.value }), children: VECTOR_GRADIENT_SPREADS.map((sp) => /* @__PURE__ */ jsx18("option", { value: sp, children: VECTOR_GRADIENT_SPREAD_LABELS[sp] }, sp)) })
2368
2397
  ] }),
2369
- ed.target === "stroke" && /* @__PURE__ */ jsxs17("label", { className: "flex items-center gap-1.5 text-xs text-foreground", children: [
2370
- /* @__PURE__ */ jsx17("span", { className: "w-16", children: "On stroke" }),
2371
- /* @__PURE__ */ jsx17("select", { "aria-label": "Gradient on stroke", className: selectClass12, value: ed.strokeMode, onChange: (e) => controller.setGradient({ strokeMode: e.target.value }), children: VECTOR_STROKE_GRADIENT_MODES.map((m) => /* @__PURE__ */ jsx17("option", { value: m, children: VECTOR_STROKE_GRADIENT_MODE_LABELS[m] }, m)) })
2398
+ ed.target === "stroke" && /* @__PURE__ */ jsxs18("label", { className: "flex items-center gap-1.5 text-xs text-foreground", children: [
2399
+ /* @__PURE__ */ jsx18("span", { className: "w-16", children: "On stroke" }),
2400
+ /* @__PURE__ */ jsx18("select", { "aria-label": "Gradient on stroke", className: selectClass13, value: ed.strokeMode, onChange: (e) => controller.setGradient({ strokeMode: e.target.value }), children: VECTOR_STROKE_GRADIENT_MODES.map((m) => /* @__PURE__ */ jsx18("option", { value: m, children: VECTOR_STROKE_GRADIENT_MODE_LABELS[m] }, m)) })
2372
2401
  ] })
2373
2402
  ] })
2374
2403
  ] });
2375
2404
  }
2376
2405
 
2377
2406
  // src/vector/VectorToolbar.tsx
2378
- import { Fragment as Fragment6, jsx as jsx18, jsxs as jsxs18 } from "react/jsx-runtime";
2407
+ import { Fragment as Fragment6, jsx as jsx19, jsxs as jsxs19 } from "react/jsx-runtime";
2379
2408
  var VECTOR_SNAP_TITLE = "Snap to objects";
2380
2409
  var modKeyLabel = () => typeof navigator !== "undefined" && /Mac|iPhone|iPad/.test(navigator.platform ?? "") ? "\u2318" : "Ctrl";
2381
- 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";
2410
+ var buttonClass16 = "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";
2382
2411
  function VectorToolbar({
2383
2412
  controller,
2384
2413
  tools = VECTOR_TOOLS,
@@ -2398,14 +2427,14 @@ function VectorToolbar({
2398
2427
  const maskSelected = state.selection.some((id) => isVectorMaskGroup(controller.getNode(id)));
2399
2428
  const [live, setLive] = React10.useState(false);
2400
2429
  const booleanDisabled = live ? count < 1 : count < 2;
2401
- const act = (label, title, run, disabled = false) => /* @__PURE__ */ jsx18("button", { type: "button", className: buttonClass15, title, "aria-label": title, disabled, onClick: () => void run(), children: label }, label);
2402
- return /* @__PURE__ */ jsxs18("div", { role: "toolbar", "aria-label": "Vector tools", className: `flex flex-wrap items-center gap-1 ${className ?? ""}`, children: [
2403
- /* @__PURE__ */ jsx18("div", { className: "flex items-center gap-1", role: "group", "aria-label": "Tools", children: tools.map((tool) => /* @__PURE__ */ jsx18(
2430
+ const act = (label, title, run, disabled = false) => /* @__PURE__ */ jsx19("button", { type: "button", className: buttonClass16, title, "aria-label": title, disabled, onClick: () => void run(), children: label }, label);
2431
+ return /* @__PURE__ */ jsxs19("div", { role: "toolbar", "aria-label": "Vector tools", className: `flex flex-wrap items-center gap-1 ${className ?? ""}`, children: [
2432
+ /* @__PURE__ */ jsx19("div", { className: "flex items-center gap-1", role: "group", "aria-label": "Tools", children: tools.map((tool) => /* @__PURE__ */ jsx19(
2404
2433
  "button",
2405
2434
  {
2406
2435
  type: "button",
2407
2436
  "data-vector-tool": tool,
2408
- className: buttonClass15,
2437
+ className: buttonClass16,
2409
2438
  "aria-pressed": state.tool === tool,
2410
2439
  title: `${vectorToolLabel(tool)} (${VECTOR_TOOL_SHORTCUTS[tool]})`,
2411
2440
  "aria-label": vectorToolLabel(tool),
@@ -2414,17 +2443,17 @@ function VectorToolbar({
2414
2443
  },
2415
2444
  tool
2416
2445
  )) }),
2417
- showHistory && /* @__PURE__ */ jsxs18("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "History", children: [
2446
+ showHistory && /* @__PURE__ */ jsxs19("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "History", children: [
2418
2447
  act("\u21B6", state.canUndo ? `Undo ${state.undoLabel}` : "Undo", () => controller.undo(), !state.canUndo),
2419
2448
  act("\u21B7", state.canRedo ? `Redo ${state.redoLabel}` : "Redo", () => controller.redo(), !state.canRedo)
2420
2449
  ] }),
2421
- showSnap && /* @__PURE__ */ jsxs18("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Snapping", children: [
2422
- /* @__PURE__ */ jsx18(
2450
+ showSnap && /* @__PURE__ */ jsxs19("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Snapping", children: [
2451
+ /* @__PURE__ */ jsx19(
2423
2452
  "button",
2424
2453
  {
2425
2454
  type: "button",
2426
2455
  "data-vector-snap": "",
2427
- className: buttonClass15,
2456
+ className: buttonClass16,
2428
2457
  "aria-pressed": state.snap.objects,
2429
2458
  title: `${VECTOR_SNAP_TITLE} (hold ${modKeyLabel()} to bypass)`,
2430
2459
  "aria-label": VECTOR_SNAP_TITLE,
@@ -2432,7 +2461,7 @@ function VectorToolbar({
2432
2461
  children: "Snap"
2433
2462
  }
2434
2463
  ),
2435
- /* @__PURE__ */ jsxs18(
2464
+ /* @__PURE__ */ jsxs19(
2436
2465
  "select",
2437
2466
  {
2438
2467
  "data-vector-polar": "",
@@ -2442,8 +2471,8 @@ function VectorToolbar({
2442
2471
  value: String(vectorPolarIncrementOf(state.snap.angles) ?? ""),
2443
2472
  onChange: (e) => controller.setSnap({ angles: vectorPolarAngles(Number(e.target.value)) }),
2444
2473
  children: [
2445
- vectorPolarIncrementOf(state.snap.angles) === null && /* @__PURE__ */ jsx18("option", { value: "", children: "Custom" }),
2446
- [90, 45, 30, 15].map((n) => /* @__PURE__ */ jsxs18("option", { value: n, children: [
2474
+ vectorPolarIncrementOf(state.snap.angles) === null && /* @__PURE__ */ jsx19("option", { value: "", children: "Custom" }),
2475
+ [90, 45, 30, 15].map((n) => /* @__PURE__ */ jsxs19("option", { value: n, children: [
2447
2476
  n,
2448
2477
  "\xB0"
2449
2478
  ] }, n))
@@ -2451,13 +2480,13 @@ function VectorToolbar({
2451
2480
  }
2452
2481
  )
2453
2482
  ] }),
2454
- showPathEdit && /* @__PURE__ */ jsxs18("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Path editing", children: [
2455
- VECTOR_PATH_ACTIONS.map((a) => /* @__PURE__ */ jsx18(
2483
+ showPathEdit && /* @__PURE__ */ jsxs19("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Path editing", children: [
2484
+ VECTOR_PATH_ACTIONS.map((a) => /* @__PURE__ */ jsx19(
2456
2485
  "button",
2457
2486
  {
2458
2487
  type: "button",
2459
2488
  "data-vector-path-action": a.id,
2460
- className: buttonClass15,
2489
+ className: buttonClass16,
2461
2490
  title: a.shortcut ? `${a.title} (${modKeyLabel()}+${a.shortcut})` : a.title,
2462
2491
  "aria-label": a.title,
2463
2492
  disabled: !a.enabled(count, points),
@@ -2466,12 +2495,12 @@ function VectorToolbar({
2466
2495
  },
2467
2496
  a.id
2468
2497
  )),
2469
- VECTOR_EDIT_TOGGLES.map((t) => /* @__PURE__ */ jsx18(
2498
+ VECTOR_EDIT_TOGGLES.map((t) => /* @__PURE__ */ jsx19(
2470
2499
  "button",
2471
2500
  {
2472
2501
  type: "button",
2473
2502
  "data-vector-edit": t.key,
2474
- className: buttonClass15,
2503
+ className: buttonClass16,
2475
2504
  "aria-pressed": editing[t.key],
2476
2505
  title: t.title,
2477
2506
  "aria-label": t.title,
@@ -2480,9 +2509,9 @@ function VectorToolbar({
2480
2509
  },
2481
2510
  t.key
2482
2511
  )),
2483
- state.tool === "pencil" && /* @__PURE__ */ jsxs18("label", { className: "flex items-center gap-1 text-xs text-foreground", children: [
2512
+ state.tool === "pencil" && /* @__PURE__ */ jsxs19("label", { className: "flex items-center gap-1 text-xs text-foreground", children: [
2484
2513
  "Smoothing",
2485
- /* @__PURE__ */ jsx18(
2514
+ /* @__PURE__ */ jsx19(
2486
2515
  "input",
2487
2516
  {
2488
2517
  type: "range",
@@ -2496,9 +2525,9 @@ function VectorToolbar({
2496
2525
  }
2497
2526
  )
2498
2527
  ] }),
2499
- state.tool === "eraser" && /* @__PURE__ */ jsxs18("label", { className: "flex items-center gap-1 text-xs text-foreground", children: [
2528
+ state.tool === "eraser" && /* @__PURE__ */ jsxs19("label", { className: "flex items-center gap-1 text-xs text-foreground", children: [
2500
2529
  "Size",
2501
- /* @__PURE__ */ jsx18(
2530
+ /* @__PURE__ */ jsx19(
2502
2531
  "input",
2503
2532
  {
2504
2533
  type: "number",
@@ -2514,12 +2543,12 @@ function VectorToolbar({
2514
2543
  }
2515
2544
  )
2516
2545
  ] }),
2517
- state.tool === "paintBucket" && /* @__PURE__ */ jsx18(
2546
+ state.tool === "paintBucket" && /* @__PURE__ */ jsx19(
2518
2547
  "button",
2519
2548
  {
2520
2549
  type: "button",
2521
2550
  "data-vector-fill-gap": "",
2522
- className: buttonClass15,
2551
+ className: buttonClass16,
2523
2552
  "aria-pressed": editing.fillGap > 0,
2524
2553
  title: "Close small gaps between path ends when filling",
2525
2554
  "aria-label": "Close gaps",
@@ -2528,12 +2557,12 @@ function VectorToolbar({
2528
2557
  }
2529
2558
  )
2530
2559
  ] }),
2531
- showDistort && /* @__PURE__ */ jsx18("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Distort", children: /* @__PURE__ */ jsx18(
2560
+ showDistort && /* @__PURE__ */ jsx19("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Distort", children: /* @__PURE__ */ jsx19(
2532
2561
  "button",
2533
2562
  {
2534
2563
  type: "button",
2535
2564
  "data-vector-free-distort": "",
2536
- className: buttonClass15,
2565
+ className: buttonClass16,
2537
2566
  "aria-pressed": state.freeDistort,
2538
2567
  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)`,
2539
2568
  "aria-label": "Free Distort",
@@ -2541,15 +2570,15 @@ function VectorToolbar({
2541
2570
  children: "Distort"
2542
2571
  }
2543
2572
  ) }),
2544
- showBlend && /* @__PURE__ */ jsx18(VectorBlendControls, { controller }),
2545
- showArrange && /* @__PURE__ */ jsxs18(Fragment6, { children: [
2546
- /* @__PURE__ */ jsxs18("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Combine", children: [
2547
- /* @__PURE__ */ jsx18(
2573
+ showBlend && /* @__PURE__ */ jsx19(VectorBlendControls, { controller }),
2574
+ showArrange && /* @__PURE__ */ jsxs19(Fragment6, { children: [
2575
+ /* @__PURE__ */ jsxs19("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Combine", children: [
2576
+ /* @__PURE__ */ jsx19(
2548
2577
  "button",
2549
2578
  {
2550
2579
  type: "button",
2551
2580
  "data-vector-live-boolean": "",
2552
- className: buttonClass15,
2581
+ className: buttonClass16,
2553
2582
  "aria-pressed": live,
2554
2583
  title: "Live booleans: keep the shapes editable",
2555
2584
  "aria-label": "Live booleans",
@@ -2566,12 +2595,12 @@ function VectorToolbar({
2566
2595
  act("\u25D0", `Make Clipping Mask (${modKeyLabel()}+7)`, () => controller.makeMask("clip"), count < 2),
2567
2596
  act("\u25D1", `Release Mask (${modKeyLabel()}+Alt+7)`, () => controller.releaseMask(), !maskSelected)
2568
2597
  ] }),
2569
- /* @__PURE__ */ jsx18("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Pathfinder", children: VECTOR_PATHFINDER_ACTIONS.map((a) => /* @__PURE__ */ jsx18(
2598
+ /* @__PURE__ */ jsx19("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Pathfinder", children: VECTOR_PATHFINDER_ACTIONS.map((a) => /* @__PURE__ */ jsx19(
2570
2599
  "button",
2571
2600
  {
2572
2601
  type: "button",
2573
2602
  "data-vector-pathfinder": a.id,
2574
- className: buttonClass15,
2603
+ className: buttonClass16,
2575
2604
  title: a.title,
2576
2605
  "aria-label": a.title,
2577
2606
  disabled: count < a.min,
@@ -2580,7 +2609,7 @@ function VectorToolbar({
2580
2609
  },
2581
2610
  a.id
2582
2611
  )) }),
2583
- /* @__PURE__ */ jsxs18("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Arrange", children: [
2612
+ /* @__PURE__ */ jsxs19("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Arrange", children: [
2584
2613
  act("\u21C8", "Bring to Front", () => controller.arrange("front"), count < 1),
2585
2614
  act("\u21CA", "Send to Back", () => controller.arrange("back"), count < 1),
2586
2615
  act("\u21E4", "Align Left", () => controller.align("left"), count < 1),
@@ -2598,19 +2627,19 @@ function VectorBlendControls({ controller }) {
2598
2627
  const blend = getVectorBlendControls(state);
2599
2628
  const o = blend.options;
2600
2629
  const selected = blend.ids.length > 0;
2601
- return /* @__PURE__ */ jsxs18("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Blend", "data-vector-blend-controls": "", children: [
2602
- /* @__PURE__ */ jsx18("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" }),
2603
- /* @__PURE__ */ jsx18(
2630
+ return /* @__PURE__ */ jsxs19("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Blend", "data-vector-blend-controls": "", children: [
2631
+ /* @__PURE__ */ jsx19("button", { type: "button", "data-vector-blend-action": "make", className: buttonClass16, title: "Make Blend", "aria-label": "Make Blend", disabled: !blend.canBlend, onClick: () => controller.blend(), children: "\u29D3" }),
2632
+ /* @__PURE__ */ jsx19(
2604
2633
  "select",
2605
2634
  {
2606
2635
  "aria-label": "Blend spacing",
2607
2636
  className: "h-7 rounded border border-border bg-background px-1 text-xs",
2608
2637
  value: o.spacing,
2609
2638
  onChange: (e) => applyVectorBlendOptions(controller, { spacing: e.target.value }),
2610
- children: VECTOR_BLEND_SPACINGS.map((sp) => /* @__PURE__ */ jsx18("option", { value: sp, children: VECTOR_BLEND_SPACING_LABELS[sp] }, sp))
2639
+ children: VECTOR_BLEND_SPACINGS.map((sp) => /* @__PURE__ */ jsx19("option", { value: sp, children: VECTOR_BLEND_SPACING_LABELS[sp] }, sp))
2611
2640
  }
2612
2641
  ),
2613
- o.spacing !== "smoothColor" && /* @__PURE__ */ jsx18(
2642
+ o.spacing !== "smoothColor" && /* @__PURE__ */ jsx19(
2614
2643
  "input",
2615
2644
  {
2616
2645
  type: "number",
@@ -2626,12 +2655,12 @@ function VectorBlendControls({ controller }) {
2626
2655
  }
2627
2656
  }
2628
2657
  ),
2629
- /* @__PURE__ */ jsx18(
2658
+ /* @__PURE__ */ jsx19(
2630
2659
  "button",
2631
2660
  {
2632
2661
  type: "button",
2633
2662
  "data-vector-blend-action": "orient",
2634
- className: buttonClass15,
2663
+ className: buttonClass16,
2635
2664
  "aria-pressed": o.orientToSpine,
2636
2665
  title: "Orient steps to the spine",
2637
2666
  "aria-label": "Orient to spine",
@@ -2639,12 +2668,12 @@ function VectorBlendControls({ controller }) {
2639
2668
  children: "\u293F"
2640
2669
  }
2641
2670
  ),
2642
- /* @__PURE__ */ jsx18(
2671
+ /* @__PURE__ */ jsx19(
2643
2672
  "button",
2644
2673
  {
2645
2674
  type: "button",
2646
2675
  "data-vector-blend-action": "spine",
2647
- className: buttonClass15,
2676
+ className: buttonClass16,
2648
2677
  title: "Replace Spine (select a blend and a path)",
2649
2678
  "aria-label": "Replace Spine",
2650
2679
  disabled: !blend.canReplaceSpine,
@@ -2652,11 +2681,11 @@ function VectorBlendControls({ controller }) {
2652
2681
  children: "\u2307"
2653
2682
  }
2654
2683
  ),
2655
- /* @__PURE__ */ jsx18("button", { type: "button", "data-vector-blend-action": "expand", className: buttonClass15, title: "Expand Blend", "aria-label": "Expand Blend", disabled: !selected, onClick: () => controller.expandBlend(), children: "\u29C9" }),
2656
- /* @__PURE__ */ jsx18("button", { type: "button", "data-vector-blend-action": "release", className: buttonClass15, title: "Release Blend", "aria-label": "Release Blend", disabled: !selected, onClick: () => controller.releaseBlend(), children: "\u29C4" })
2684
+ /* @__PURE__ */ jsx19("button", { type: "button", "data-vector-blend-action": "expand", className: buttonClass16, title: "Expand Blend", "aria-label": "Expand Blend", disabled: !selected, onClick: () => controller.expandBlend(), children: "\u29C9" }),
2685
+ /* @__PURE__ */ jsx19("button", { type: "button", "data-vector-blend-action": "release", className: buttonClass16, title: "Release Blend", "aria-label": "Release Blend", disabled: !selected, onClick: () => controller.releaseBlend(), children: "\u29C4" })
2657
2686
  ] });
2658
2687
  }
2659
- var selectClass13 = "h-7 min-w-0 flex-1 rounded border border-border bg-background px-1 text-xs";
2688
+ var selectClass14 = "h-7 min-w-0 flex-1 rounded border border-border bg-background px-1 text-xs";
2660
2689
  var numberClass11 = "h-7 w-16 rounded border border-border bg-background px-1.5 text-xs";
2661
2690
  function VectorStylePanel({ controller, className }) {
2662
2691
  const state = useVectorEditorState(controller);
@@ -2689,17 +2718,17 @@ function VectorStylePanel({ controller, className }) {
2689
2718
  if (style) controller.setWidthProfile(id);
2690
2719
  else controller.setDefaults({ strokeWidthProfile: widthProfilePreset(id)?.length ? widthProfilePreset(id) : null });
2691
2720
  };
2692
- const mixed = (key) => style?.mixed[key] ? /* @__PURE__ */ jsx18("span", { className: "text-[11px] text-muted-foreground", children: "Mixed" }) : null;
2721
+ const mixed = (key) => style?.mixed[key] ? /* @__PURE__ */ jsx19("span", { className: "text-[11px] text-muted-foreground", children: "Mixed" }) : null;
2693
2722
  const arrowRow = (end, label) => {
2694
2723
  const key = end === "start" ? "startArrow" : "endArrow";
2695
2724
  const scaleKey = end === "start" ? "startArrowScale" : "endArrowScale";
2696
- return /* @__PURE__ */ jsxs18("div", { className: "flex items-center gap-2", "data-vector-style": key, children: [
2697
- /* @__PURE__ */ jsx18("span", { className: "w-16 text-xs text-foreground", children: label }),
2698
- /* @__PURE__ */ jsxs18("select", { "aria-label": `${label} arrowhead`, className: selectClass13, value: source[key] ?? "", onChange: (e) => apply({ [key]: e.target.value || null }), children: [
2699
- /* @__PURE__ */ jsx18("option", { value: "", children: "None" }),
2700
- VECTOR_ARROWHEADS.map((h) => /* @__PURE__ */ jsx18("option", { value: h, children: VECTOR_ARROWHEAD_LABELS[h] }, h))
2725
+ return /* @__PURE__ */ jsxs19("div", { className: "flex items-center gap-2", "data-vector-style": key, children: [
2726
+ /* @__PURE__ */ jsx19("span", { className: "w-16 text-xs text-foreground", children: label }),
2727
+ /* @__PURE__ */ jsxs19("select", { "aria-label": `${label} arrowhead`, className: selectClass14, value: source[key] ?? "", onChange: (e) => apply({ [key]: e.target.value || null }), children: [
2728
+ /* @__PURE__ */ jsx19("option", { value: "", children: "None" }),
2729
+ VECTOR_ARROWHEADS.map((h) => /* @__PURE__ */ jsx19("option", { value: h, children: VECTOR_ARROWHEAD_LABELS[h] }, h))
2701
2730
  ] }),
2702
- /* @__PURE__ */ jsx18(
2731
+ /* @__PURE__ */ jsx19(
2703
2732
  "input",
2704
2733
  {
2705
2734
  type: "number",
@@ -2725,12 +2754,12 @@ function VectorStylePanel({ controller, className }) {
2725
2754
  const paint = source[key];
2726
2755
  const hex = vectorPaintToHex6(paint);
2727
2756
  const on = paint !== null && paint !== "none";
2728
- return /* @__PURE__ */ jsxs18("div", { className: "flex items-center gap-2", "data-vector-style": key, children: [
2729
- /* @__PURE__ */ jsxs18("label", { className: "flex w-16 items-center gap-1.5 text-xs text-foreground", children: [
2730
- /* @__PURE__ */ jsx18("input", { type: "checkbox", checked: on, "aria-label": `${label} on`, onChange: (e) => apply({ [key]: e.target.checked ? hex ?? "#000000" : null }) }),
2757
+ return /* @__PURE__ */ jsxs19("div", { className: "flex items-center gap-2", "data-vector-style": key, children: [
2758
+ /* @__PURE__ */ jsxs19("label", { className: "flex w-16 items-center gap-1.5 text-xs text-foreground", children: [
2759
+ /* @__PURE__ */ jsx19("input", { type: "checkbox", checked: on, "aria-label": `${label} on`, onChange: (e) => apply({ [key]: e.target.checked ? hex ?? "#000000" : null }) }),
2731
2760
  label
2732
2761
  ] }),
2733
- /* @__PURE__ */ jsx18(
2762
+ /* @__PURE__ */ jsx19(
2734
2763
  "input",
2735
2764
  {
2736
2765
  type: "color",
@@ -2741,18 +2770,18 @@ function VectorStylePanel({ controller, className }) {
2741
2770
  onChange: (e) => apply({ [key]: e.target.value })
2742
2771
  }
2743
2772
  ),
2744
- on && !hex && /* @__PURE__ */ jsx18("span", { className: "truncate text-[11px] text-muted-foreground", children: typeof paint === "string" ? paint : "Gradient" }),
2745
- style?.mixed[key] && /* @__PURE__ */ jsx18("span", { className: "text-[11px] text-muted-foreground", children: "Mixed" })
2773
+ on && !hex && /* @__PURE__ */ jsx19("span", { className: "truncate text-[11px] text-muted-foreground", children: typeof paint === "string" ? paint : "Gradient" }),
2774
+ style?.mixed[key] && /* @__PURE__ */ jsx19("span", { className: "text-[11px] text-muted-foreground", children: "Mixed" })
2746
2775
  ] });
2747
2776
  };
2748
- return /* @__PURE__ */ jsxs18("div", { className: `space-y-2 ${className ?? ""}`, "aria-label": style ? "Selection style" : "New shape style", children: [
2749
- /* @__PURE__ */ jsx18("p", { className: "text-[11px] font-medium uppercase tracking-wide text-muted-foreground", children: style ? "Selection" : "New shapes" }),
2777
+ return /* @__PURE__ */ jsxs19("div", { className: `space-y-2 ${className ?? ""}`, "aria-label": style ? "Selection style" : "New shape style", children: [
2778
+ /* @__PURE__ */ jsx19("p", { className: "text-[11px] font-medium uppercase tracking-wide text-muted-foreground", children: style ? "Selection" : "New shapes" }),
2750
2779
  paintRow("fill", "Fill"),
2751
2780
  paintRow("stroke", "Stroke"),
2752
- style && /* @__PURE__ */ jsx18(VectorGradientEditor, { controller }),
2753
- /* @__PURE__ */ jsxs18("label", { className: "flex items-center gap-2 text-xs text-foreground", children: [
2754
- /* @__PURE__ */ jsx18("span", { className: "w-16", children: "Width" }),
2755
- /* @__PURE__ */ jsx18(
2781
+ style && /* @__PURE__ */ jsx19(VectorGradientEditor, { controller }),
2782
+ /* @__PURE__ */ jsxs19("label", { className: "flex items-center gap-2 text-xs text-foreground", children: [
2783
+ /* @__PURE__ */ jsx19("span", { className: "w-16", children: "Width" }),
2784
+ /* @__PURE__ */ jsx19(
2756
2785
  "input",
2757
2786
  {
2758
2787
  type: "number",
@@ -2768,33 +2797,33 @@ function VectorStylePanel({ controller, className }) {
2768
2797
  }
2769
2798
  )
2770
2799
  ] }),
2771
- /* @__PURE__ */ jsxs18("label", { className: "flex items-center gap-2 text-xs text-foreground", "data-vector-style": "strokeAlign", children: [
2772
- /* @__PURE__ */ jsx18("span", { className: "w-16", children: "Align" }),
2773
- /* @__PURE__ */ jsx18("select", { "aria-label": "Stroke alignment", className: selectClass13, value: source.strokeAlign, onChange: (e) => apply({ strokeAlign: e.target.value }), children: Object.keys(VECTOR_STROKE_ALIGN_LABELS2).map((a) => /* @__PURE__ */ jsx18("option", { value: a, children: VECTOR_STROKE_ALIGN_LABELS2[a] }, a)) }),
2800
+ /* @__PURE__ */ jsxs19("label", { className: "flex items-center gap-2 text-xs text-foreground", "data-vector-style": "strokeAlign", children: [
2801
+ /* @__PURE__ */ jsx19("span", { className: "w-16", children: "Align" }),
2802
+ /* @__PURE__ */ jsx19("select", { "aria-label": "Stroke alignment", className: selectClass14, value: source.strokeAlign, onChange: (e) => apply({ strokeAlign: e.target.value }), children: Object.keys(VECTOR_STROKE_ALIGN_LABELS2).map((a) => /* @__PURE__ */ jsx19("option", { value: a, children: VECTOR_STROKE_ALIGN_LABELS2[a] }, a)) }),
2774
2803
  mixed("strokeAlign")
2775
2804
  ] }),
2776
- /* @__PURE__ */ jsxs18("div", { className: "flex items-center gap-2 text-xs text-foreground", "data-vector-style": "widthProfile", children: [
2777
- /* @__PURE__ */ jsx18("span", { className: "w-16", children: "Profile" }),
2778
- /* @__PURE__ */ jsxs18(
2805
+ /* @__PURE__ */ jsxs19("div", { className: "flex items-center gap-2 text-xs text-foreground", "data-vector-style": "widthProfile", children: [
2806
+ /* @__PURE__ */ jsx19("span", { className: "w-16", children: "Profile" }),
2807
+ /* @__PURE__ */ jsxs19(
2779
2808
  "select",
2780
2809
  {
2781
2810
  "aria-label": "Width profile",
2782
- className: selectClass13,
2811
+ className: selectClass14,
2783
2812
  value: source.widthProfile,
2784
2813
  onChange: (e) => {
2785
2814
  if (e.target.value !== "custom") setProfile(e.target.value);
2786
2815
  },
2787
2816
  children: [
2788
- VECTOR_WIDTH_PRESETS.map((p) => /* @__PURE__ */ jsx18("option", { value: p.id, children: p.label }, p.id)),
2789
- source.widthProfile === "custom" && /* @__PURE__ */ jsx18("option", { value: "custom", disabled: true, children: "Custom" })
2817
+ VECTOR_WIDTH_PRESETS.map((p) => /* @__PURE__ */ jsx19("option", { value: p.id, children: p.label }, p.id)),
2818
+ source.widthProfile === "custom" && /* @__PURE__ */ jsx19("option", { value: "custom", disabled: true, children: "Custom" })
2790
2819
  ]
2791
2820
  }
2792
2821
  ),
2793
- /* @__PURE__ */ jsx18(
2822
+ /* @__PURE__ */ jsx19(
2794
2823
  "button",
2795
2824
  {
2796
2825
  type: "button",
2797
- className: buttonClass15,
2826
+ className: buttonClass16,
2798
2827
  title: "Reset to uniform width",
2799
2828
  "aria-label": "Reset to uniform width",
2800
2829
  disabled: source.widthProfile === "uniform" && !style?.mixed.widthProfile,
@@ -2804,20 +2833,20 @@ function VectorStylePanel({ controller, className }) {
2804
2833
  ),
2805
2834
  mixed("widthProfile")
2806
2835
  ] }),
2807
- source.hasOpenPath && /* @__PURE__ */ jsxs18(Fragment6, { children: [
2836
+ source.hasOpenPath && /* @__PURE__ */ jsxs19(Fragment6, { children: [
2808
2837
  arrowRow("start", "Start"),
2809
2838
  arrowRow("end", "End"),
2810
- (source.startArrow || source.endArrow) && /* @__PURE__ */ jsxs18("label", { className: "flex items-center gap-2 text-xs text-foreground", "data-vector-style": "arrowAlign", children: [
2811
- /* @__PURE__ */ jsx18("span", { className: "w-16", children: "Heads" }),
2812
- /* @__PURE__ */ jsxs18("select", { "aria-label": "Arrowhead alignment", className: selectClass13, value: source.arrowAlign, onChange: (e) => apply({ arrowAlign: e.target.value }), children: [
2813
- /* @__PURE__ */ jsx18("option", { value: "extend", children: "Tip past end" }),
2814
- /* @__PURE__ */ jsx18("option", { value: "tip", children: "Tip at end" })
2839
+ (source.startArrow || source.endArrow) && /* @__PURE__ */ jsxs19("label", { className: "flex items-center gap-2 text-xs text-foreground", "data-vector-style": "arrowAlign", children: [
2840
+ /* @__PURE__ */ jsx19("span", { className: "w-16", children: "Heads" }),
2841
+ /* @__PURE__ */ jsxs19("select", { "aria-label": "Arrowhead alignment", className: selectClass14, value: source.arrowAlign, onChange: (e) => apply({ arrowAlign: e.target.value }), children: [
2842
+ /* @__PURE__ */ jsx19("option", { value: "extend", children: "Tip past end" }),
2843
+ /* @__PURE__ */ jsx19("option", { value: "tip", children: "Tip at end" })
2815
2844
  ] })
2816
2845
  ] })
2817
2846
  ] }),
2818
- source.corners > 0 && /* @__PURE__ */ jsxs18("div", { className: "flex items-center gap-2", "data-vector-style": "corners", children: [
2819
- /* @__PURE__ */ jsx18("span", { className: "w-16 text-xs text-foreground", children: "Corners" }),
2820
- /* @__PURE__ */ jsx18(
2847
+ source.corners > 0 && /* @__PURE__ */ jsxs19("div", { className: "flex items-center gap-2", "data-vector-style": "corners", children: [
2848
+ /* @__PURE__ */ jsx19("span", { className: "w-16 text-xs text-foreground", children: "Corners" }),
2849
+ /* @__PURE__ */ jsx19(
2821
2850
  "input",
2822
2851
  {
2823
2852
  type: "number",
@@ -2832,12 +2861,12 @@ function VectorStylePanel({ controller, className }) {
2832
2861
  }
2833
2862
  }
2834
2863
  ),
2835
- /* @__PURE__ */ jsx18("select", { "aria-label": "Corner kind", className: selectClass13, value: source.cornerKind, onChange: (e) => setCorners({ kind: e.target.value }), children: VECTOR_CORNER_KINDS.map((k) => /* @__PURE__ */ jsx18("option", { value: k, children: VECTOR_CORNER_KIND_LABELS[k] }, k)) }),
2864
+ /* @__PURE__ */ jsx19("select", { "aria-label": "Corner kind", className: selectClass14, value: source.cornerKind, onChange: (e) => setCorners({ kind: e.target.value }), children: VECTOR_CORNER_KINDS.map((k) => /* @__PURE__ */ jsx19("option", { value: k, children: VECTOR_CORNER_KIND_LABELS[k] }, k)) }),
2836
2865
  mixed("cornerRadius")
2837
2866
  ] }),
2838
- style && source.corners > 0 && source.cornerKind === "round" && source.cornerRadius > 0 && /* @__PURE__ */ jsxs18("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: [
2839
- /* @__PURE__ */ jsx18("span", { className: "w-16", children: "Smoothing" }),
2840
- /* @__PURE__ */ jsx18(
2867
+ style && source.corners > 0 && source.cornerKind === "round" && source.cornerRadius > 0 && /* @__PURE__ */ jsxs19("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: [
2868
+ /* @__PURE__ */ jsx19("span", { className: "w-16", children: "Smoothing" }),
2869
+ /* @__PURE__ */ jsx19(
2841
2870
  "input",
2842
2871
  {
2843
2872
  type: "range",
@@ -2852,9 +2881,9 @@ function VectorStylePanel({ controller, className }) {
2852
2881
  }
2853
2882
  )
2854
2883
  ] }),
2855
- style && /* @__PURE__ */ jsxs18("label", { className: "flex items-center gap-2 text-xs text-foreground", children: [
2856
- /* @__PURE__ */ jsx18("span", { className: "w-16", children: "Opacity" }),
2857
- /* @__PURE__ */ jsx18(
2884
+ style && /* @__PURE__ */ jsxs19("label", { className: "flex items-center gap-2 text-xs text-foreground", children: [
2885
+ /* @__PURE__ */ jsx19("span", { className: "w-16", children: "Opacity" }),
2886
+ /* @__PURE__ */ jsx19(
2858
2887
  "input",
2859
2888
  {
2860
2889
  type: "range",
@@ -2868,26 +2897,27 @@ function VectorStylePanel({ controller, className }) {
2868
2897
  }
2869
2898
  )
2870
2899
  ] }),
2871
- /* @__PURE__ */ jsx18(VectorTextPanel, { controller }),
2872
- style && /* @__PURE__ */ jsx18(VectorAppearancePanel, { controller }),
2873
- style && /* @__PURE__ */ jsx18(VectorEffectsPanel, { controller }),
2874
- style && /* @__PURE__ */ jsx18(VectorEnvelopePanel, { controller }),
2875
- style && /* @__PURE__ */ jsx18(VectorMaskPanel, { controller }),
2876
- /* @__PURE__ */ jsx18(VectorFramePanel, { controller }),
2877
- /* @__PURE__ */ jsx18(VectorConnectorPanel, { controller }),
2878
- /* @__PURE__ */ jsx18(VectorShadingPanel, { controller }),
2879
- /* @__PURE__ */ jsx18(VectorSymmetryPanel, { controller }),
2880
- /* @__PURE__ */ jsx18(VectorPerspectivePanel, { controller }),
2881
- /* @__PURE__ */ jsx18(VectorLensPanel, { controller }),
2882
- /* @__PURE__ */ jsx18(VectorTracePanel, { controller }),
2883
- /* @__PURE__ */ jsx18(VectorConstraintsPanel, { controller }),
2884
- /* @__PURE__ */ jsx18(VectorSymbolsPanel, { controller })
2900
+ /* @__PURE__ */ jsx19(VectorTextPanel, { controller }),
2901
+ style && /* @__PURE__ */ jsx19(VectorAppearancePanel, { controller }),
2902
+ style && /* @__PURE__ */ jsx19(VectorEffectsPanel, { controller }),
2903
+ style && /* @__PURE__ */ jsx19(VectorEnvelopePanel, { controller }),
2904
+ style && /* @__PURE__ */ jsx19(VectorMaskPanel, { controller }),
2905
+ /* @__PURE__ */ jsx19(VectorFramePanel, { controller }),
2906
+ /* @__PURE__ */ jsx19(VectorPagesPanel, { controller }),
2907
+ /* @__PURE__ */ jsx19(VectorConnectorPanel, { controller }),
2908
+ /* @__PURE__ */ jsx19(VectorShadingPanel, { controller }),
2909
+ /* @__PURE__ */ jsx19(VectorSymmetryPanel, { controller }),
2910
+ /* @__PURE__ */ jsx19(VectorPerspectivePanel, { controller }),
2911
+ /* @__PURE__ */ jsx19(VectorLensPanel, { controller }),
2912
+ /* @__PURE__ */ jsx19(VectorTracePanel, { controller }),
2913
+ /* @__PURE__ */ jsx19(VectorConstraintsPanel, { controller }),
2914
+ /* @__PURE__ */ jsx19(VectorSymbolsPanel, { controller })
2885
2915
  ] });
2886
2916
  }
2887
2917
 
2888
2918
  // src/vector/VectorProviderEffectsPanel.tsx
2889
2919
  import { useState as useState7, useSyncExternalStore as useSyncExternalStore2 } from "react";
2890
- import { Fragment as Fragment7, jsx as jsx19, jsxs as jsxs19 } from "react/jsx-runtime";
2920
+ import { Fragment as Fragment7, jsx as jsx20, jsxs as jsxs20 } from "react/jsx-runtime";
2891
2921
  var btn = "rounded border border-border px-1.5 py-0.5 text-[11px] disabled:opacity-40";
2892
2922
  function Controls({ controller, group }) {
2893
2923
  const provider = controller.effectsProvider;
@@ -2895,11 +2925,11 @@ function Controls({ controller, group }) {
2895
2925
  const { description, error } = useEffectDescription(provider, tag.effect);
2896
2926
  const [message, setMessage] = useState7(null);
2897
2927
  const run = (work) => void work.then(() => setMessage(null), (e) => setMessage(e instanceof Error ? e.message : String(e)));
2898
- if (!provider) return /* @__PURE__ */ jsx19("p", { className: "text-[11px] text-muted-foreground", children: "No effects provider: the shapes are kept as they are." });
2899
- if (error) return /* @__PURE__ */ jsx19("p", { className: "text-[11px] text-amber-500", children: error });
2900
- if (!description) return /* @__PURE__ */ jsx19("p", { className: "text-[11px] text-muted-foreground", children: "Loading\u2026" });
2901
- return /* @__PURE__ */ jsxs19(Fragment7, { children: [
2902
- /* @__PURE__ */ jsx19(
2928
+ if (!provider) return /* @__PURE__ */ jsx20("p", { className: "text-[11px] text-muted-foreground", children: "No effects provider: the shapes are kept as they are." });
2929
+ if (error) return /* @__PURE__ */ jsx20("p", { className: "text-[11px] text-amber-500", children: error });
2930
+ if (!description) return /* @__PURE__ */ jsx20("p", { className: "text-[11px] text-muted-foreground", children: "Loading\u2026" });
2931
+ return /* @__PURE__ */ jsxs20(Fragment7, { children: [
2932
+ /* @__PURE__ */ jsx20(
2903
2933
  EffectParameters,
2904
2934
  {
2905
2935
  description,
@@ -2909,7 +2939,7 @@ function Controls({ controller, group }) {
2909
2939
  onPreset: (v) => run(controller.updateVectorEffect(group.id, v))
2910
2940
  }
2911
2941
  ),
2912
- message && /* @__PURE__ */ jsx19("p", { role: "alert", className: "text-[11px] text-destructive", children: message })
2942
+ message && /* @__PURE__ */ jsx20("p", { role: "alert", className: "text-[11px] text-destructive", children: message })
2913
2943
  ] });
2914
2944
  }
2915
2945
  function VectorProviderEffectsPanel({ controller, className = "" }) {
@@ -2928,41 +2958,41 @@ function VectorProviderEffectsPanel({ controller, className = "" }) {
2928
2958
  setMessage(null);
2929
2959
  Promise.resolve().then(work).catch((e) => setMessage(e instanceof Error ? e.message : String(e)));
2930
2960
  };
2931
- return /* @__PURE__ */ jsxs19("section", { className: `flex flex-col gap-2 text-xs ${className}`, "aria-label": "Vector effects", "data-vector-provider-effects": "", children: [
2932
- /* @__PURE__ */ jsxs19("div", { className: "flex items-center gap-1", children: [
2933
- /* @__PURE__ */ jsx19("span", { className: "flex-1 font-semibold", children: "Effects" }),
2934
- /* @__PURE__ */ jsx19("button", { type: "button", className: btn, disabled: !provider, "aria-pressed": browse, onClick: () => setBrowse(!browse), children: "Add effect\u2026" })
2961
+ return /* @__PURE__ */ jsxs20("section", { className: `flex flex-col gap-2 text-xs ${className}`, "aria-label": "Vector effects", "data-vector-provider-effects": "", children: [
2962
+ /* @__PURE__ */ jsxs20("div", { className: "flex items-center gap-1", children: [
2963
+ /* @__PURE__ */ jsx20("span", { className: "flex-1 font-semibold", children: "Effects" }),
2964
+ /* @__PURE__ */ jsx20("button", { type: "button", className: btn, disabled: !provider, "aria-pressed": browse, onClick: () => setBrowse(!browse), children: "Add effect\u2026" })
2935
2965
  ] }),
2936
- !provider && /* @__PURE__ */ jsx19("p", { className: "text-[11px] text-muted-foreground", children: "No effects provider is registered. Effect groups are kept as shapes." }),
2937
- browse && /* @__PURE__ */ jsx19(EffectsBrowser, { provider, kinds: ["vector"], title: "Vector effects", onPick: (e) => (setBrowse(false), run(() => controller.addVectorEffect(e.id))) }),
2938
- selected.map((g) => /* @__PURE__ */ jsxs19("div", { className: "space-y-1 rounded border border-border p-1", "data-vector-effect-group": g.id, children: [
2939
- /* @__PURE__ */ jsxs19("div", { className: "flex items-center gap-1", children: [
2940
- /* @__PURE__ */ jsx19("span", { className: "min-w-0 flex-1 truncate font-medium", children: g.providerEffect.name ?? g.providerEffect.effect }),
2941
- /* @__PURE__ */ jsx19("button", { type: "button", className: btn, title: "Keep the shapes as a plain group", onClick: () => run(() => controller.detachVectorEffect(g.id)), children: "Detach" })
2966
+ !provider && /* @__PURE__ */ jsx20("p", { className: "text-[11px] text-muted-foreground", children: "No effects provider is registered. Effect groups are kept as shapes." }),
2967
+ browse && /* @__PURE__ */ jsx20(EffectsBrowser, { provider, kinds: ["vector"], title: "Vector effects", onPick: (e) => (setBrowse(false), run(() => controller.addVectorEffect(e.id))) }),
2968
+ selected.map((g) => /* @__PURE__ */ jsxs20("div", { className: "space-y-1 rounded border border-border p-1", "data-vector-effect-group": g.id, children: [
2969
+ /* @__PURE__ */ jsxs20("div", { className: "flex items-center gap-1", children: [
2970
+ /* @__PURE__ */ jsx20("span", { className: "min-w-0 flex-1 truncate font-medium", children: g.providerEffect.name ?? g.providerEffect.effect }),
2971
+ /* @__PURE__ */ jsx20("button", { type: "button", className: btn, title: "Keep the shapes as a plain group", onClick: () => run(() => controller.detachVectorEffect(g.id)), children: "Detach" })
2942
2972
  ] }),
2943
- /* @__PURE__ */ jsx19(Controls, { controller, group: g })
2973
+ /* @__PURE__ */ jsx20(Controls, { controller, group: g })
2944
2974
  ] }, g.id)),
2945
- groups.length > 0 && !selected.length && /* @__PURE__ */ jsxs19("p", { className: "text-[11px] text-muted-foreground", children: [
2975
+ groups.length > 0 && !selected.length && /* @__PURE__ */ jsxs20("p", { className: "text-[11px] text-muted-foreground", children: [
2946
2976
  "Select an effect group (",
2947
2977
  groups.length,
2948
2978
  " in the drawing) to change it."
2949
2979
  ] }),
2950
- message && /* @__PURE__ */ jsx19("p", { role: "alert", className: "text-[11px] text-destructive", children: message })
2980
+ message && /* @__PURE__ */ jsx20("p", { role: "alert", className: "text-[11px] text-destructive", children: message })
2951
2981
  ] });
2952
2982
  }
2953
2983
 
2954
2984
  // src/vector/VectorEditor.tsx
2955
- import { jsx as jsx20, jsxs as jsxs20 } from "react/jsx-runtime";
2985
+ import { jsx as jsx21, jsxs as jsxs21 } from "react/jsx-runtime";
2956
2986
  function VectorEditor({ controller, tools, showStylePanel = true, height = 520, canvasProps, effects, className }) {
2957
2987
  useEffects(controller, effects);
2958
2988
  const effectsShown = !!effects || controller.vectorEffectGroups().length > 0;
2959
- return /* @__PURE__ */ jsxs20("div", { className: `flex flex-col gap-2 ${className ?? ""}`, "data-vector-editor": "", children: [
2960
- /* @__PURE__ */ jsx20(VectorToolbar, { controller, tools }),
2961
- /* @__PURE__ */ jsxs20("div", { className: "flex min-h-0 flex-col gap-2 md:flex-row", children: [
2962
- /* @__PURE__ */ jsx20("div", { className: "relative min-h-[320px] flex-1 overflow-hidden rounded-md border border-border bg-muted/40", style: { height }, children: /* @__PURE__ */ jsx20(VectorCanvas, { controller, ...canvasProps }) }),
2963
- showStylePanel && /* @__PURE__ */ jsxs20("div", { className: "flex w-full shrink-0 flex-col gap-2 md:w-56", children: [
2964
- /* @__PURE__ */ jsx20(VectorStylePanel, { controller, className: "rounded-md border border-border p-3" }),
2965
- effectsShown && /* @__PURE__ */ jsx20(VectorProviderEffectsPanel, { controller, className: "rounded-md border border-border p-3" })
2989
+ return /* @__PURE__ */ jsxs21("div", { className: `flex flex-col gap-2 ${className ?? ""}`, "data-vector-editor": "", children: [
2990
+ /* @__PURE__ */ jsx21(VectorToolbar, { controller, tools }),
2991
+ /* @__PURE__ */ jsxs21("div", { className: "flex min-h-0 flex-col gap-2 md:flex-row", children: [
2992
+ /* @__PURE__ */ jsx21("div", { className: "relative min-h-[320px] flex-1 overflow-hidden rounded-md border border-border bg-muted/40", style: { height }, children: /* @__PURE__ */ jsx21(VectorCanvas, { controller, ...canvasProps }) }),
2993
+ showStylePanel && /* @__PURE__ */ jsxs21("div", { className: "flex w-full shrink-0 flex-col gap-2 md:w-56", children: [
2994
+ /* @__PURE__ */ jsx21(VectorStylePanel, { controller, className: "rounded-md border border-border p-3" }),
2995
+ effectsShown && /* @__PURE__ */ jsx21(VectorProviderEffectsPanel, { controller, className: "rounded-md border border-border p-3" })
2966
2996
  ] })
2967
2997
  ] })
2968
2998
  ] });
@@ -2983,6 +3013,7 @@ export {
2983
3013
  VectorLensPanel,
2984
3014
  VectorMaskPanel,
2985
3015
  VectorMaskedNodes,
3016
+ VectorPagesPanel,
2986
3017
  VectorPerspectivePanel,
2987
3018
  VectorProviderEffectsPanel,
2988
3019
  VectorSceneNodes,