@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.
- package/dist/vector/index.cjs +413 -381
- package/dist/vector/index.cjs.map +1 -1
- package/dist/vector/index.d.cts +11 -1
- package/dist/vector/index.d.ts +11 -1
- package/dist/vector/index.js +302 -271
- package/dist/vector/index.js.map +1 -1
- package/package.json +3 -3
package/dist/vector/index.js
CHANGED
|
@@ -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
|
|
1898
|
-
var
|
|
1899
|
-
var
|
|
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__ */
|
|
1907
|
-
/* @__PURE__ */
|
|
1908
|
-
e ? /* @__PURE__ */
|
|
1909
|
-
/* @__PURE__ */
|
|
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:
|
|
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__ */
|
|
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__ */
|
|
1920
|
-
] }) : /* @__PURE__ */
|
|
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__ */
|
|
1923
|
-
/* @__PURE__ */
|
|
1924
|
-
/* @__PURE__ */
|
|
1925
|
-
/* @__PURE__ */
|
|
1926
|
-
panel.route === "elbow" && /* @__PURE__ */
|
|
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
|
|
1952
|
-
var
|
|
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__ */
|
|
1973
|
-
/* @__PURE__ */
|
|
1974
|
-
/* @__PURE__ */
|
|
1975
|
-
/* @__PURE__ */
|
|
1976
|
-
/* @__PURE__ */
|
|
1977
|
-
/* @__PURE__ */
|
|
1978
|
-
/* @__PURE__ */
|
|
1979
|
-
/* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
1986
|
-
/* @__PURE__ */
|
|
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__ */
|
|
1991
|
-
/* @__PURE__ */
|
|
1992
|
-
free && /* @__PURE__ */
|
|
1993
|
-
/* @__PURE__ */
|
|
1994
|
-
/* @__PURE__ */
|
|
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
|
|
2003
|
-
var
|
|
2004
|
-
var
|
|
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__ */
|
|
2035
|
-
/* @__PURE__ */
|
|
2036
|
-
/* @__PURE__ */
|
|
2037
|
-
/* @__PURE__ */
|
|
2038
|
-
/* @__PURE__ */
|
|
2039
|
-
/* @__PURE__ */
|
|
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__ */
|
|
2042
|
-
/* @__PURE__ */
|
|
2043
|
-
/* @__PURE__ */
|
|
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__ */
|
|
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
|
|
2065
|
-
var
|
|
2066
|
-
var
|
|
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__ */
|
|
2075
|
-
/* @__PURE__ */
|
|
2076
|
-
/* @__PURE__ */
|
|
2077
|
-
!group && /* @__PURE__ */
|
|
2078
|
-
!group && /* @__PURE__ */
|
|
2079
|
-
group && s && /* @__PURE__ */
|
|
2080
|
-
/* @__PURE__ */
|
|
2081
|
-
s.mode === "radial" && /* @__PURE__ */
|
|
2082
|
-
/* @__PURE__ */
|
|
2083
|
-
/* @__PURE__ */
|
|
2084
|
-
/* @__PURE__ */
|
|
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__ */
|
|
2091
|
-
/* @__PURE__ */
|
|
2092
|
-
/* @__PURE__ */
|
|
2093
|
-
/* @__PURE__ */
|
|
2094
|
-
/* @__PURE__ */
|
|
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
|
|
2103
|
-
var
|
|
2104
|
-
var
|
|
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__ */
|
|
2114
|
-
/* @__PURE__ */
|
|
2115
|
-
/* @__PURE__ */
|
|
2116
|
-
[1, 2, 3].map((n) => /* @__PURE__ */
|
|
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__ */
|
|
2122
|
-
/* @__PURE__ */
|
|
2123
|
-
/* @__PURE__ */
|
|
2124
|
-
/* @__PURE__ */
|
|
2125
|
-
planes.map((p) => /* @__PURE__ */
|
|
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__ */
|
|
2132
|
-
/* @__PURE__ */
|
|
2133
|
-
!state.perspectivePlane && /* @__PURE__ */
|
|
2134
|
-
/* @__PURE__ */
|
|
2135
|
-
/* @__PURE__ */
|
|
2136
|
-
/* @__PURE__ */
|
|
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__ */
|
|
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
|
|
2147
|
-
var
|
|
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__ */
|
|
2157
|
-
/* @__PURE__ */
|
|
2158
|
-
/* @__PURE__ */
|
|
2159
|
-
/* @__PURE__ */
|
|
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:
|
|
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__ */
|
|
2168
|
-
VECTOR_LENS_TYPES.map((t) => /* @__PURE__ */
|
|
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__ */
|
|
2174
|
-
/* @__PURE__ */
|
|
2175
|
-
/* @__PURE__ */
|
|
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__ */
|
|
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
|
|
2211
|
-
var
|
|
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
|
|
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__ */
|
|
2229
|
-
/* @__PURE__ */
|
|
2230
|
-
/* @__PURE__ */
|
|
2231
|
-
["fill", "stroke"].map((t) => /* @__PURE__ */
|
|
2232
|
-
ed.mixed && /* @__PURE__ */
|
|
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__ */
|
|
2235
|
-
/* @__PURE__ */
|
|
2236
|
-
/* @__PURE__ */
|
|
2237
|
-
/* @__PURE__ */
|
|
2238
|
-
VECTOR_GRADIENT_TYPES.map((t) => /* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
2243
|
-
/* @__PURE__ */
|
|
2244
|
-
/* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
2273
|
-
/* @__PURE__ */
|
|
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__ */
|
|
2313
|
+
/* @__PURE__ */ jsxs18("label", { className: "flex items-center gap-1 text-[11px] text-muted-foreground", children: [
|
|
2285
2314
|
"Opacity",
|
|
2286
|
-
/* @__PURE__ */
|
|
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__ */
|
|
2332
|
+
/* @__PURE__ */ jsxs18("label", { className: "flex items-center gap-1 text-[11px] text-muted-foreground", children: [
|
|
2304
2333
|
"At",
|
|
2305
|
-
/* @__PURE__ */
|
|
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__ */
|
|
2351
|
+
!sel.last && /* @__PURE__ */ jsxs18("label", { className: "flex items-center gap-1 text-[11px] text-muted-foreground", children: [
|
|
2323
2352
|
"Midpoint",
|
|
2324
|
-
/* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
2344
|
-
/* @__PURE__ */
|
|
2345
|
-
/* @__PURE__ */
|
|
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__ */
|
|
2348
|
-
/* @__PURE__ */
|
|
2349
|
-
/* @__PURE__ */
|
|
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__ */
|
|
2366
|
-
/* @__PURE__ */
|
|
2367
|
-
/* @__PURE__ */
|
|
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__ */
|
|
2370
|
-
/* @__PURE__ */
|
|
2371
|
-
/* @__PURE__ */
|
|
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
|
|
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
|
|
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__ */
|
|
2402
|
-
return /* @__PURE__ */
|
|
2403
|
-
/* @__PURE__ */
|
|
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:
|
|
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__ */
|
|
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__ */
|
|
2422
|
-
/* @__PURE__ */
|
|
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:
|
|
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__ */
|
|
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__ */
|
|
2446
|
-
[90, 45, 30, 15].map((n) => /* @__PURE__ */
|
|
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__ */
|
|
2455
|
-
VECTOR_PATH_ACTIONS.map((a) => /* @__PURE__ */
|
|
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:
|
|
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__ */
|
|
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:
|
|
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__ */
|
|
2512
|
+
state.tool === "pencil" && /* @__PURE__ */ jsxs19("label", { className: "flex items-center gap-1 text-xs text-foreground", children: [
|
|
2484
2513
|
"Smoothing",
|
|
2485
|
-
/* @__PURE__ */
|
|
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__ */
|
|
2528
|
+
state.tool === "eraser" && /* @__PURE__ */ jsxs19("label", { className: "flex items-center gap-1 text-xs text-foreground", children: [
|
|
2500
2529
|
"Size",
|
|
2501
|
-
/* @__PURE__ */
|
|
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__ */
|
|
2546
|
+
state.tool === "paintBucket" && /* @__PURE__ */ jsx19(
|
|
2518
2547
|
"button",
|
|
2519
2548
|
{
|
|
2520
2549
|
type: "button",
|
|
2521
2550
|
"data-vector-fill-gap": "",
|
|
2522
|
-
className:
|
|
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__ */
|
|
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:
|
|
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__ */
|
|
2545
|
-
showArrange && /* @__PURE__ */
|
|
2546
|
-
/* @__PURE__ */
|
|
2547
|
-
/* @__PURE__ */
|
|
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:
|
|
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__ */
|
|
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:
|
|
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__ */
|
|
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__ */
|
|
2602
|
-
/* @__PURE__ */
|
|
2603
|
-
/* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
2658
|
+
/* @__PURE__ */ jsx19(
|
|
2630
2659
|
"button",
|
|
2631
2660
|
{
|
|
2632
2661
|
type: "button",
|
|
2633
2662
|
"data-vector-blend-action": "orient",
|
|
2634
|
-
className:
|
|
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__ */
|
|
2671
|
+
/* @__PURE__ */ jsx19(
|
|
2643
2672
|
"button",
|
|
2644
2673
|
{
|
|
2645
2674
|
type: "button",
|
|
2646
2675
|
"data-vector-blend-action": "spine",
|
|
2647
|
-
className:
|
|
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__ */
|
|
2656
|
-
/* @__PURE__ */
|
|
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
|
|
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__ */
|
|
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__ */
|
|
2697
|
-
/* @__PURE__ */
|
|
2698
|
-
/* @__PURE__ */
|
|
2699
|
-
/* @__PURE__ */
|
|
2700
|
-
VECTOR_ARROWHEADS.map((h) => /* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
2729
|
-
/* @__PURE__ */
|
|
2730
|
-
/* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
2745
|
-
style?.mixed[key] && /* @__PURE__ */
|
|
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__ */
|
|
2749
|
-
/* @__PURE__ */
|
|
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__ */
|
|
2753
|
-
/* @__PURE__ */
|
|
2754
|
-
/* @__PURE__ */
|
|
2755
|
-
/* @__PURE__ */
|
|
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__ */
|
|
2772
|
-
/* @__PURE__ */
|
|
2773
|
-
/* @__PURE__ */
|
|
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__ */
|
|
2777
|
-
/* @__PURE__ */
|
|
2778
|
-
/* @__PURE__ */
|
|
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:
|
|
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__ */
|
|
2789
|
-
source.widthProfile === "custom" && /* @__PURE__ */
|
|
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__ */
|
|
2822
|
+
/* @__PURE__ */ jsx19(
|
|
2794
2823
|
"button",
|
|
2795
2824
|
{
|
|
2796
2825
|
type: "button",
|
|
2797
|
-
className:
|
|
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__ */
|
|
2836
|
+
source.hasOpenPath && /* @__PURE__ */ jsxs19(Fragment6, { children: [
|
|
2808
2837
|
arrowRow("start", "Start"),
|
|
2809
2838
|
arrowRow("end", "End"),
|
|
2810
|
-
(source.startArrow || source.endArrow) && /* @__PURE__ */
|
|
2811
|
-
/* @__PURE__ */
|
|
2812
|
-
/* @__PURE__ */
|
|
2813
|
-
/* @__PURE__ */
|
|
2814
|
-
/* @__PURE__ */
|
|
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__ */
|
|
2819
|
-
/* @__PURE__ */
|
|
2820
|
-
/* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
2839
|
-
/* @__PURE__ */
|
|
2840
|
-
/* @__PURE__ */
|
|
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__ */
|
|
2856
|
-
/* @__PURE__ */
|
|
2857
|
-
/* @__PURE__ */
|
|
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__ */
|
|
2872
|
-
style && /* @__PURE__ */
|
|
2873
|
-
style && /* @__PURE__ */
|
|
2874
|
-
style && /* @__PURE__ */
|
|
2875
|
-
style && /* @__PURE__ */
|
|
2876
|
-
/* @__PURE__ */
|
|
2877
|
-
/* @__PURE__ */
|
|
2878
|
-
/* @__PURE__ */
|
|
2879
|
-
/* @__PURE__ */
|
|
2880
|
-
/* @__PURE__ */
|
|
2881
|
-
/* @__PURE__ */
|
|
2882
|
-
/* @__PURE__ */
|
|
2883
|
-
/* @__PURE__ */
|
|
2884
|
-
/* @__PURE__ */
|
|
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
|
|
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__ */
|
|
2899
|
-
if (error) return /* @__PURE__ */
|
|
2900
|
-
if (!description) return /* @__PURE__ */
|
|
2901
|
-
return /* @__PURE__ */
|
|
2902
|
-
/* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
2932
|
-
/* @__PURE__ */
|
|
2933
|
-
/* @__PURE__ */
|
|
2934
|
-
/* @__PURE__ */
|
|
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__ */
|
|
2937
|
-
browse && /* @__PURE__ */
|
|
2938
|
-
selected.map((g) => /* @__PURE__ */
|
|
2939
|
-
/* @__PURE__ */
|
|
2940
|
-
/* @__PURE__ */
|
|
2941
|
-
/* @__PURE__ */
|
|
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__ */
|
|
2973
|
+
/* @__PURE__ */ jsx20(Controls, { controller, group: g })
|
|
2944
2974
|
] }, g.id)),
|
|
2945
|
-
groups.length > 0 && !selected.length && /* @__PURE__ */
|
|
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__ */
|
|
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
|
|
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__ */
|
|
2960
|
-
/* @__PURE__ */
|
|
2961
|
-
/* @__PURE__ */
|
|
2962
|
-
/* @__PURE__ */
|
|
2963
|
-
showStylePanel && /* @__PURE__ */
|
|
2964
|
-
/* @__PURE__ */
|
|
2965
|
-
effectsShown && /* @__PURE__ */
|
|
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,
|