@pixerate/editor-react 0.83.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 +476 -391
- package/dist/vector/index.cjs.map +1 -1
- package/dist/vector/index.d.cts +22 -1
- package/dist/vector/index.d.ts +22 -1
- package/dist/vector/index.js +336 -253
- package/dist/vector/index.js.map +1 -1
- package/package.json +3 -3
package/dist/vector/index.js
CHANGED
|
@@ -649,6 +649,11 @@ function VectorCanvas({
|
|
|
649
649
|
sh.lines.map((d, i) => /* @__PURE__ */ jsx2("path", { d, stroke: accentColor, strokeWidth: 1, opacity: 0.7 }, i)),
|
|
650
650
|
sh.points.map((p) => /* @__PURE__ */ jsx2("circle", { "data-vector-shading-point": p.index, "data-selected": p.selected || void 0, cx: p.x, cy: p.y, r: p.selected ? 6 : 4.5, fill: p.color, stroke: p.selected ? accentColor : "#ffffff", strokeWidth: p.selected ? 2 : 1.5 }, p.index))
|
|
651
651
|
] }, `shading:${sh.nodeId}`)),
|
|
652
|
+
overlay.perspective && /* @__PURE__ */ jsxs2("g", { "data-vector-perspective-grid": "", fill: "none", pointerEvents: "none", children: [
|
|
653
|
+
overlay.perspective.planes.map((pl) => /* @__PURE__ */ jsx2("path", { "data-vector-perspective-plane": pl.plane, "data-active": pl.active || void 0, d: pl.d, stroke: pl.active ? accentColor : guideColor, strokeWidth: pl.active ? 1 : 0.75, opacity: pl.active ? 0.55 : 0.25 }, pl.plane)),
|
|
654
|
+
overlay.perspective.vanishingPoints.map((v, i) => /* @__PURE__ */ jsx2("circle", { "data-vector-vanishing-point": i, cx: v.x, cy: v.y, r: 4, stroke: accentColor, strokeWidth: 1.5 }, i)),
|
|
655
|
+
/* @__PURE__ */ jsx2("circle", { "data-vector-perspective-origin": "", cx: overlay.perspective.origin.x, cy: overlay.perspective.origin.y, r: 3, fill: accentColor })
|
|
656
|
+
] }),
|
|
652
657
|
overlay.symmetry.map((sy) => /* @__PURE__ */ jsx2("g", { "data-vector-symmetry-axes": sy.nodeId, stroke: accentColor, strokeDasharray: sy.drawing ? void 0 : "6 4", opacity: sy.drawing ? 0.9 : 0.6, children: sy.lines.map((l, i) => /* @__PURE__ */ jsx2("line", { x1: l.x1, y1: l.y1, x2: l.x2, y2: l.y2 }, i)) }, `sym:${sy.nodeId}`)),
|
|
653
658
|
overlay.trackingPoints.map((t, i) => /* @__PURE__ */ jsx2("path", { "data-vector-tracking-point": "", d: `M${t.x - 5} ${t.y}H${t.x + 5}M${t.x} ${t.y - 5}V${t.y + 5}`, stroke: guideColor, strokeWidth: 1.5 }, `track:${i}`)),
|
|
654
659
|
overlay.typedEntry && /* @__PURE__ */ jsxs2("g", { "data-vector-typed-entry": "", transform: `translate(${overlay.typedEntry.x + 14} ${overlay.typedEntry.y + 18})`, children: [
|
|
@@ -785,7 +790,7 @@ function VectorCanvas({
|
|
|
785
790
|
}
|
|
786
791
|
|
|
787
792
|
// src/vector/VectorToolbar.tsx
|
|
788
|
-
import
|
|
793
|
+
import React10 from "react";
|
|
789
794
|
import {
|
|
790
795
|
VECTOR_TOOLS,
|
|
791
796
|
VECTOR_TOOL_GLYPHS,
|
|
@@ -1881,6 +1886,35 @@ function VectorFramePanel({ controller, className }) {
|
|
|
1881
1886
|
] });
|
|
1882
1887
|
}
|
|
1883
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
|
+
|
|
1884
1918
|
// src/vector/VectorConnectorPanel.tsx
|
|
1885
1919
|
import {
|
|
1886
1920
|
VECTOR_CONNECTOR_ROUTES,
|
|
@@ -1889,36 +1923,36 @@ import {
|
|
|
1889
1923
|
VECTOR_CONNECTOR_SIDE_LABELS,
|
|
1890
1924
|
getVectorConnectorPanel
|
|
1891
1925
|
} from "@pixerate/editor/vector";
|
|
1892
|
-
import { Fragment as Fragment2, jsx as
|
|
1893
|
-
var
|
|
1894
|
-
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";
|
|
1895
1929
|
var numberClass5 = "h-7 w-16 rounded border border-border bg-background px-1.5 text-xs";
|
|
1896
1930
|
function VectorConnectorPanel({ controller, className }) {
|
|
1897
1931
|
const state = useVectorEditorState(controller);
|
|
1898
1932
|
const panel = getVectorConnectorPanel(state);
|
|
1899
1933
|
if (!panel) return null;
|
|
1900
1934
|
const ids = [panel.id];
|
|
1901
|
-
const end = (key, label, e) => /* @__PURE__ */
|
|
1902
|
-
/* @__PURE__ */
|
|
1903
|
-
e ? /* @__PURE__ */
|
|
1904
|
-
/* @__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(
|
|
1905
1939
|
"select",
|
|
1906
1940
|
{
|
|
1907
1941
|
"aria-label": `${label} side`,
|
|
1908
|
-
className:
|
|
1942
|
+
className: selectClass8,
|
|
1909
1943
|
value: e.side ?? "auto",
|
|
1910
1944
|
onChange: (ev) => controller.setConnector({ [key]: { nodeId: e.nodeId, side: ev.target.value } }, { ids }),
|
|
1911
|
-
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))
|
|
1912
1946
|
}
|
|
1913
1947
|
),
|
|
1914
|
-
/* @__PURE__ */
|
|
1915
|
-
] }) : /* @__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" })
|
|
1916
1950
|
] });
|
|
1917
|
-
return /* @__PURE__ */
|
|
1918
|
-
/* @__PURE__ */
|
|
1919
|
-
/* @__PURE__ */
|
|
1920
|
-
/* @__PURE__ */
|
|
1921
|
-
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(
|
|
1922
1956
|
"input",
|
|
1923
1957
|
{
|
|
1924
1958
|
type: "number",
|
|
@@ -1943,8 +1977,8 @@ function VectorConnectorPanel({ controller, className }) {
|
|
|
1943
1977
|
// src/vector/VectorShadingPanel.tsx
|
|
1944
1978
|
import { useState as useState5 } from "react";
|
|
1945
1979
|
import { isVectorFreeformGradient, isVectorMeshGradient, vectorPaintToHex as vectorPaintToHex4 } from "@pixerate/editor/vector";
|
|
1946
|
-
import { Fragment as Fragment3, jsx as
|
|
1947
|
-
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";
|
|
1948
1982
|
var numberClass6 = "h-7 w-12 rounded border border-border bg-background px-1.5 text-xs";
|
|
1949
1983
|
function VectorShadingPanel({ controller, className }) {
|
|
1950
1984
|
const state = useVectorEditorState(controller);
|
|
@@ -1964,29 +1998,29 @@ function VectorShadingPanel({ controller, className }) {
|
|
|
1964
1998
|
if (mesh) controller.setMeshPoint(path.id, sel.index, patch, merge ? { merge } : {});
|
|
1965
1999
|
else controller.setFreeformPoint(path.id, sel.index, patch, merge ? { merge } : {});
|
|
1966
2000
|
};
|
|
1967
|
-
return /* @__PURE__ */
|
|
1968
|
-
/* @__PURE__ */
|
|
1969
|
-
/* @__PURE__ */
|
|
1970
|
-
/* @__PURE__ */
|
|
1971
|
-
/* @__PURE__ */
|
|
1972
|
-
/* @__PURE__ */
|
|
1973
|
-
/* @__PURE__ */
|
|
1974
|
-
/* @__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" })
|
|
1975
2009
|
] }),
|
|
1976
|
-
(mesh || free) && !sel && /* @__PURE__ */
|
|
2010
|
+
(mesh || free) && !sel && /* @__PURE__ */ jsxs13("p", { className: "text-[11px] text-muted-foreground", children: [
|
|
1977
2011
|
"Gradient tool (G): drag points; ",
|
|
1978
2012
|
free ? "click inside to add one, Alt-click to remove." : "pick one to colour it."
|
|
1979
2013
|
] }),
|
|
1980
|
-
sel && /* @__PURE__ */
|
|
1981
|
-
/* @__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: [
|
|
1982
2016
|
"Point ",
|
|
1983
2017
|
sel.index
|
|
1984
2018
|
] }),
|
|
1985
|
-
/* @__PURE__ */
|
|
1986
|
-
/* @__PURE__ */
|
|
1987
|
-
free && /* @__PURE__ */
|
|
1988
|
-
/* @__PURE__ */
|
|
1989
|
-
/* @__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" })
|
|
1990
2024
|
] })
|
|
1991
2025
|
] })
|
|
1992
2026
|
] });
|
|
@@ -1994,9 +2028,9 @@ function VectorShadingPanel({ controller, className }) {
|
|
|
1994
2028
|
|
|
1995
2029
|
// src/vector/VectorTracePanel.tsx
|
|
1996
2030
|
import { useRef as useRef4, useState as useState6 } from "react";
|
|
1997
|
-
import { jsx as
|
|
1998
|
-
var
|
|
1999
|
-
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";
|
|
2000
2034
|
var numberClass7 = "h-7 w-12 rounded border border-border bg-background px-1.5 text-xs";
|
|
2001
2035
|
async function filePixels(file, max = 1e6) {
|
|
2002
2036
|
const bitmap = await createImageBitmap(file);
|
|
@@ -2026,16 +2060,16 @@ function VectorTracePanel({ controller, className }) {
|
|
|
2026
2060
|
setMessage("That file could not be read as an image.");
|
|
2027
2061
|
}
|
|
2028
2062
|
};
|
|
2029
|
-
return /* @__PURE__ */
|
|
2030
|
-
/* @__PURE__ */
|
|
2031
|
-
/* @__PURE__ */
|
|
2032
|
-
/* @__PURE__ */
|
|
2033
|
-
/* @__PURE__ */
|
|
2034
|
-
/* @__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" })
|
|
2035
2069
|
] }),
|
|
2036
|
-
mode === "color" && /* @__PURE__ */
|
|
2037
|
-
/* @__PURE__ */
|
|
2038
|
-
/* @__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(
|
|
2039
2073
|
"input",
|
|
2040
2074
|
{
|
|
2041
2075
|
ref: input,
|
|
@@ -2050,15 +2084,15 @@ function VectorTracePanel({ controller, className }) {
|
|
|
2050
2084
|
}
|
|
2051
2085
|
)
|
|
2052
2086
|
] }),
|
|
2053
|
-
message && /* @__PURE__ */
|
|
2087
|
+
message && /* @__PURE__ */ jsx14("p", { className: "text-[11px] text-red-600", children: message })
|
|
2054
2088
|
] });
|
|
2055
2089
|
}
|
|
2056
2090
|
|
|
2057
2091
|
// src/vector/VectorSymmetryPanel.tsx
|
|
2058
2092
|
import { VECTOR_SYMMETRY_MODES, VECTOR_SYMMETRY_MODE_LABELS, isVectorSymmetryGroup } from "@pixerate/editor/vector";
|
|
2059
|
-
import { Fragment as Fragment4, jsx as
|
|
2060
|
-
var
|
|
2061
|
-
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";
|
|
2062
2096
|
var numberClass8 = "h-7 w-12 rounded border border-border bg-background px-1.5 text-xs";
|
|
2063
2097
|
function VectorSymmetryPanel({ controller, className }) {
|
|
2064
2098
|
const state = useVectorEditorState(controller);
|
|
@@ -2066,35 +2100,80 @@ function VectorSymmetryPanel({ controller, className }) {
|
|
|
2066
2100
|
const s = group?.symmetry;
|
|
2067
2101
|
const ids = group ? [group.id] : void 0;
|
|
2068
2102
|
const drawing = !!group && state.symmetryTarget === group.id;
|
|
2069
|
-
return /* @__PURE__ */
|
|
2070
|
-
/* @__PURE__ */
|
|
2071
|
-
/* @__PURE__ */
|
|
2072
|
-
!group && /* @__PURE__ */
|
|
2073
|
-
!group && /* @__PURE__ */
|
|
2074
|
-
group && s && /* @__PURE__ */
|
|
2075
|
-
/* @__PURE__ */
|
|
2076
|
-
s.mode === "radial" && /* @__PURE__ */
|
|
2077
|
-
/* @__PURE__ */
|
|
2078
|
-
/* @__PURE__ */
|
|
2079
|
-
/* @__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 }) }),
|
|
2080
2114
|
"Mirror"
|
|
2081
2115
|
] })
|
|
2082
2116
|
] })
|
|
2083
2117
|
] })
|
|
2084
2118
|
] }),
|
|
2085
|
-
group && /* @__PURE__ */
|
|
2086
|
-
/* @__PURE__ */
|
|
2087
|
-
/* @__PURE__ */
|
|
2088
|
-
/* @__PURE__ */
|
|
2089
|
-
/* @__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" })
|
|
2124
|
+
] })
|
|
2125
|
+
] });
|
|
2126
|
+
}
|
|
2127
|
+
|
|
2128
|
+
// src/vector/VectorPerspectivePanel.tsx
|
|
2129
|
+
import React9 from "react";
|
|
2130
|
+
import { vectorPerspectivePlanes, vectorPerspectivePreset } from "@pixerate/editor/vector";
|
|
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";
|
|
2134
|
+
var PLANE_LABELS = { floor: "Floor", left: "Left wall", right: "Right wall", side: "Side wall", front: "Front" };
|
|
2135
|
+
function VectorPerspectivePanel({ controller, className }) {
|
|
2136
|
+
const state = useVectorEditorState(controller);
|
|
2137
|
+
const grid = state.document.perspective ?? null;
|
|
2138
|
+
const planes = grid ? vectorPerspectivePlanes(grid) : [];
|
|
2139
|
+
const [attachTo, setAttachTo] = React9.useState("");
|
|
2140
|
+
const target = (state.perspectivePlane ?? attachTo) || planes[0];
|
|
2141
|
+
const add = (n) => controller.setPerspective(vectorPerspectivePreset(n, state.document.width, state.document.height));
|
|
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: [
|
|
2146
|
+
n,
|
|
2147
|
+
"-pt"
|
|
2148
|
+
] }, n))
|
|
2149
|
+
] }),
|
|
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: [
|
|
2155
|
+
"Draw on ",
|
|
2156
|
+
PLANE_LABELS[p].toLowerCase()
|
|
2157
|
+
] }, p))
|
|
2158
|
+
] })
|
|
2159
|
+
] }),
|
|
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 }) }),
|
|
2166
|
+
"Show"
|
|
2167
|
+
] }),
|
|
2168
|
+
/* @__PURE__ */ jsx16("button", { type: "button", className: buttonClass14, title: "Remove the grid (art on it stays)", onClick: () => controller.setPerspective(null), children: "Remove" })
|
|
2090
2169
|
] })
|
|
2091
2170
|
] });
|
|
2092
2171
|
}
|
|
2093
2172
|
|
|
2094
2173
|
// src/vector/VectorLensPanel.tsx
|
|
2095
2174
|
import { VECTOR_LENS_LABELS, VECTOR_LENS_SPECS, VECTOR_LENS_TYPES } from "@pixerate/editor/vector";
|
|
2096
|
-
import { jsx as
|
|
2097
|
-
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";
|
|
2098
2177
|
var numberClass9 = "h-7 w-16 rounded border border-border bg-background px-1.5 text-xs";
|
|
2099
2178
|
function VectorLensPanel({ controller, className }) {
|
|
2100
2179
|
const state = useVectorEditorState(controller);
|
|
@@ -2103,26 +2182,26 @@ function VectorLensPanel({ controller, className }) {
|
|
|
2103
2182
|
const lens = path.lens ?? null;
|
|
2104
2183
|
const spec = lens ? VECTOR_LENS_SPECS[lens.type] : null;
|
|
2105
2184
|
const ids = [path.id];
|
|
2106
|
-
return /* @__PURE__ */
|
|
2107
|
-
/* @__PURE__ */
|
|
2108
|
-
/* @__PURE__ */
|
|
2109
|
-
/* @__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(
|
|
2110
2189
|
"select",
|
|
2111
2190
|
{
|
|
2112
2191
|
"aria-label": "Lens type",
|
|
2113
|
-
className:
|
|
2192
|
+
className: selectClass12,
|
|
2114
2193
|
value: lens?.type ?? "",
|
|
2115
2194
|
onChange: (e) => e.target.value ? controller.setLens({ type: e.target.value }, { ids }) : controller.setLens(null, { ids }),
|
|
2116
2195
|
children: [
|
|
2117
|
-
/* @__PURE__ */
|
|
2118
|
-
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))
|
|
2119
2198
|
]
|
|
2120
2199
|
}
|
|
2121
2200
|
)
|
|
2122
2201
|
] }),
|
|
2123
|
-
lens && spec && /* @__PURE__ */
|
|
2124
|
-
/* @__PURE__ */
|
|
2125
|
-
/* @__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(
|
|
2126
2205
|
"input",
|
|
2127
2206
|
{
|
|
2128
2207
|
type: "number",
|
|
@@ -2139,7 +2218,7 @@ function VectorLensPanel({ controller, className }) {
|
|
|
2139
2218
|
}
|
|
2140
2219
|
}
|
|
2141
2220
|
),
|
|
2142
|
-
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}` }) })
|
|
2143
2222
|
] })
|
|
2144
2223
|
] });
|
|
2145
2224
|
}
|
|
@@ -2157,10 +2236,10 @@ import {
|
|
|
2157
2236
|
getVectorGradientEditor,
|
|
2158
2237
|
vectorPaintToHex as vectorPaintToHex5
|
|
2159
2238
|
} from "@pixerate/editor/vector";
|
|
2160
|
-
import { Fragment as Fragment5, jsx as
|
|
2161
|
-
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";
|
|
2162
2241
|
var numberClass10 = "h-7 w-14 rounded border border-border bg-background px-1.5 text-xs";
|
|
2163
|
-
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";
|
|
2164
2243
|
var percent = (v) => Math.round(v * 1e3) / 10;
|
|
2165
2244
|
function VectorGradientEditor({ controller, className }) {
|
|
2166
2245
|
const state = useVectorEditorState(controller);
|
|
@@ -2175,23 +2254,23 @@ function VectorGradientEditor({ controller, className }) {
|
|
|
2175
2254
|
controller.setStyle({ [ed.target]: vectorPaintToHex5(first?.color) ?? first?.color ?? "#000000" }, void 0, { label: "Solid Colour" });
|
|
2176
2255
|
} else controller.setGradient({ type: value });
|
|
2177
2256
|
};
|
|
2178
|
-
return /* @__PURE__ */
|
|
2179
|
-
/* @__PURE__ */
|
|
2180
|
-
/* @__PURE__ */
|
|
2181
|
-
["fill", "stroke"].map((t) => /* @__PURE__ */
|
|
2182
|
-
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" })
|
|
2183
2262
|
] }),
|
|
2184
|
-
/* @__PURE__ */
|
|
2185
|
-
/* @__PURE__ */
|
|
2186
|
-
/* @__PURE__ */
|
|
2187
|
-
/* @__PURE__ */
|
|
2188
|
-
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))
|
|
2189
2268
|
] }),
|
|
2190
|
-
/* @__PURE__ */
|
|
2269
|
+
/* @__PURE__ */ jsx18("button", { type: "button", className: buttonClass15, "aria-label": "Reverse gradient", title: "Reverse gradient", disabled: !g, onClick: () => controller.reverseGradient(), children: "\u21C4" })
|
|
2191
2270
|
] }),
|
|
2192
|
-
g && /* @__PURE__ */
|
|
2193
|
-
/* @__PURE__ */
|
|
2194
|
-
/* @__PURE__ */
|
|
2271
|
+
g && /* @__PURE__ */ jsxs18(Fragment5, { children: [
|
|
2272
|
+
/* @__PURE__ */ jsxs18("div", { className: "relative h-8", "data-vector-gradient-ramp": "", children: [
|
|
2273
|
+
/* @__PURE__ */ jsx18(
|
|
2195
2274
|
"div",
|
|
2196
2275
|
{
|
|
2197
2276
|
role: "button",
|
|
@@ -2205,7 +2284,7 @@ function VectorGradientEditor({ controller, className }) {
|
|
|
2205
2284
|
}
|
|
2206
2285
|
}
|
|
2207
2286
|
),
|
|
2208
|
-
ed.stops.map((s) => /* @__PURE__ */
|
|
2287
|
+
ed.stops.map((s) => /* @__PURE__ */ jsx18(
|
|
2209
2288
|
"button",
|
|
2210
2289
|
{
|
|
2211
2290
|
type: "button",
|
|
@@ -2219,8 +2298,8 @@ function VectorGradientEditor({ controller, className }) {
|
|
|
2219
2298
|
s.index
|
|
2220
2299
|
))
|
|
2221
2300
|
] }),
|
|
2222
|
-
sel && /* @__PURE__ */
|
|
2223
|
-
/* @__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(
|
|
2224
2303
|
"input",
|
|
2225
2304
|
{
|
|
2226
2305
|
type: "color",
|
|
@@ -2231,9 +2310,9 @@ function VectorGradientEditor({ controller, className }) {
|
|
|
2231
2310
|
onBlur: end
|
|
2232
2311
|
}
|
|
2233
2312
|
),
|
|
2234
|
-
/* @__PURE__ */
|
|
2313
|
+
/* @__PURE__ */ jsxs18("label", { className: "flex items-center gap-1 text-[11px] text-muted-foreground", children: [
|
|
2235
2314
|
"Opacity",
|
|
2236
|
-
/* @__PURE__ */
|
|
2315
|
+
/* @__PURE__ */ jsx18(
|
|
2237
2316
|
"input",
|
|
2238
2317
|
{
|
|
2239
2318
|
type: "number",
|
|
@@ -2250,9 +2329,9 @@ function VectorGradientEditor({ controller, className }) {
|
|
|
2250
2329
|
}
|
|
2251
2330
|
)
|
|
2252
2331
|
] }),
|
|
2253
|
-
/* @__PURE__ */
|
|
2332
|
+
/* @__PURE__ */ jsxs18("label", { className: "flex items-center gap-1 text-[11px] text-muted-foreground", children: [
|
|
2254
2333
|
"At",
|
|
2255
|
-
/* @__PURE__ */
|
|
2334
|
+
/* @__PURE__ */ jsx18(
|
|
2256
2335
|
"input",
|
|
2257
2336
|
{
|
|
2258
2337
|
type: "number",
|
|
@@ -2269,9 +2348,9 @@ function VectorGradientEditor({ controller, className }) {
|
|
|
2269
2348
|
}
|
|
2270
2349
|
)
|
|
2271
2350
|
] }),
|
|
2272
|
-
!sel.last && /* @__PURE__ */
|
|
2351
|
+
!sel.last && /* @__PURE__ */ jsxs18("label", { className: "flex items-center gap-1 text-[11px] text-muted-foreground", children: [
|
|
2273
2352
|
"Midpoint",
|
|
2274
|
-
/* @__PURE__ */
|
|
2353
|
+
/* @__PURE__ */ jsx18(
|
|
2275
2354
|
"input",
|
|
2276
2355
|
{
|
|
2277
2356
|
type: "number",
|
|
@@ -2288,15 +2367,15 @@ function VectorGradientEditor({ controller, className }) {
|
|
|
2288
2367
|
}
|
|
2289
2368
|
)
|
|
2290
2369
|
] }),
|
|
2291
|
-
/* @__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" })
|
|
2292
2371
|
] }),
|
|
2293
|
-
/* @__PURE__ */
|
|
2294
|
-
/* @__PURE__ */
|
|
2295
|
-
/* @__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)) })
|
|
2296
2375
|
] }),
|
|
2297
|
-
/* @__PURE__ */
|
|
2298
|
-
/* @__PURE__ */
|
|
2299
|
-
/* @__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(
|
|
2300
2379
|
"input",
|
|
2301
2380
|
{
|
|
2302
2381
|
type: "range",
|
|
@@ -2312,23 +2391,23 @@ function VectorGradientEditor({ controller, className }) {
|
|
|
2312
2391
|
}
|
|
2313
2392
|
)
|
|
2314
2393
|
] }),
|
|
2315
|
-
/* @__PURE__ */
|
|
2316
|
-
/* @__PURE__ */
|
|
2317
|
-
/* @__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)) })
|
|
2318
2397
|
] }),
|
|
2319
|
-
ed.target === "stroke" && /* @__PURE__ */
|
|
2320
|
-
/* @__PURE__ */
|
|
2321
|
-
/* @__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)) })
|
|
2322
2401
|
] })
|
|
2323
2402
|
] })
|
|
2324
2403
|
] });
|
|
2325
2404
|
}
|
|
2326
2405
|
|
|
2327
2406
|
// src/vector/VectorToolbar.tsx
|
|
2328
|
-
import { Fragment as Fragment6, jsx as
|
|
2407
|
+
import { Fragment as Fragment6, jsx as jsx19, jsxs as jsxs19 } from "react/jsx-runtime";
|
|
2329
2408
|
var VECTOR_SNAP_TITLE = "Snap to objects";
|
|
2330
2409
|
var modKeyLabel = () => typeof navigator !== "undefined" && /Mac|iPhone|iPad/.test(navigator.platform ?? "") ? "\u2318" : "Ctrl";
|
|
2331
|
-
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";
|
|
2332
2411
|
function VectorToolbar({
|
|
2333
2412
|
controller,
|
|
2334
2413
|
tools = VECTOR_TOOLS,
|
|
@@ -2346,16 +2425,16 @@ function VectorToolbar({
|
|
|
2346
2425
|
const editing = state.editing;
|
|
2347
2426
|
const groupSelected = state.selection.some((id) => controller.getNode(id)?.type === "group");
|
|
2348
2427
|
const maskSelected = state.selection.some((id) => isVectorMaskGroup(controller.getNode(id)));
|
|
2349
|
-
const [live, setLive] =
|
|
2428
|
+
const [live, setLive] = React10.useState(false);
|
|
2350
2429
|
const booleanDisabled = live ? count < 1 : count < 2;
|
|
2351
|
-
const act = (label, title, run, disabled = false) => /* @__PURE__ */
|
|
2352
|
-
return /* @__PURE__ */
|
|
2353
|
-
/* @__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(
|
|
2354
2433
|
"button",
|
|
2355
2434
|
{
|
|
2356
2435
|
type: "button",
|
|
2357
2436
|
"data-vector-tool": tool,
|
|
2358
|
-
className:
|
|
2437
|
+
className: buttonClass16,
|
|
2359
2438
|
"aria-pressed": state.tool === tool,
|
|
2360
2439
|
title: `${vectorToolLabel(tool)} (${VECTOR_TOOL_SHORTCUTS[tool]})`,
|
|
2361
2440
|
"aria-label": vectorToolLabel(tool),
|
|
@@ -2364,17 +2443,17 @@ function VectorToolbar({
|
|
|
2364
2443
|
},
|
|
2365
2444
|
tool
|
|
2366
2445
|
)) }),
|
|
2367
|
-
showHistory && /* @__PURE__ */
|
|
2446
|
+
showHistory && /* @__PURE__ */ jsxs19("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "History", children: [
|
|
2368
2447
|
act("\u21B6", state.canUndo ? `Undo ${state.undoLabel}` : "Undo", () => controller.undo(), !state.canUndo),
|
|
2369
2448
|
act("\u21B7", state.canRedo ? `Redo ${state.redoLabel}` : "Redo", () => controller.redo(), !state.canRedo)
|
|
2370
2449
|
] }),
|
|
2371
|
-
showSnap && /* @__PURE__ */
|
|
2372
|
-
/* @__PURE__ */
|
|
2450
|
+
showSnap && /* @__PURE__ */ jsxs19("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Snapping", children: [
|
|
2451
|
+
/* @__PURE__ */ jsx19(
|
|
2373
2452
|
"button",
|
|
2374
2453
|
{
|
|
2375
2454
|
type: "button",
|
|
2376
2455
|
"data-vector-snap": "",
|
|
2377
|
-
className:
|
|
2456
|
+
className: buttonClass16,
|
|
2378
2457
|
"aria-pressed": state.snap.objects,
|
|
2379
2458
|
title: `${VECTOR_SNAP_TITLE} (hold ${modKeyLabel()} to bypass)`,
|
|
2380
2459
|
"aria-label": VECTOR_SNAP_TITLE,
|
|
@@ -2382,7 +2461,7 @@ function VectorToolbar({
|
|
|
2382
2461
|
children: "Snap"
|
|
2383
2462
|
}
|
|
2384
2463
|
),
|
|
2385
|
-
/* @__PURE__ */
|
|
2464
|
+
/* @__PURE__ */ jsxs19(
|
|
2386
2465
|
"select",
|
|
2387
2466
|
{
|
|
2388
2467
|
"data-vector-polar": "",
|
|
@@ -2392,8 +2471,8 @@ function VectorToolbar({
|
|
|
2392
2471
|
value: String(vectorPolarIncrementOf(state.snap.angles) ?? ""),
|
|
2393
2472
|
onChange: (e) => controller.setSnap({ angles: vectorPolarAngles(Number(e.target.value)) }),
|
|
2394
2473
|
children: [
|
|
2395
|
-
vectorPolarIncrementOf(state.snap.angles) === null && /* @__PURE__ */
|
|
2396
|
-
[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: [
|
|
2397
2476
|
n,
|
|
2398
2477
|
"\xB0"
|
|
2399
2478
|
] }, n))
|
|
@@ -2401,13 +2480,13 @@ function VectorToolbar({
|
|
|
2401
2480
|
}
|
|
2402
2481
|
)
|
|
2403
2482
|
] }),
|
|
2404
|
-
showPathEdit && /* @__PURE__ */
|
|
2405
|
-
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(
|
|
2406
2485
|
"button",
|
|
2407
2486
|
{
|
|
2408
2487
|
type: "button",
|
|
2409
2488
|
"data-vector-path-action": a.id,
|
|
2410
|
-
className:
|
|
2489
|
+
className: buttonClass16,
|
|
2411
2490
|
title: a.shortcut ? `${a.title} (${modKeyLabel()}+${a.shortcut})` : a.title,
|
|
2412
2491
|
"aria-label": a.title,
|
|
2413
2492
|
disabled: !a.enabled(count, points),
|
|
@@ -2416,12 +2495,12 @@ function VectorToolbar({
|
|
|
2416
2495
|
},
|
|
2417
2496
|
a.id
|
|
2418
2497
|
)),
|
|
2419
|
-
VECTOR_EDIT_TOGGLES.map((t) => /* @__PURE__ */
|
|
2498
|
+
VECTOR_EDIT_TOGGLES.map((t) => /* @__PURE__ */ jsx19(
|
|
2420
2499
|
"button",
|
|
2421
2500
|
{
|
|
2422
2501
|
type: "button",
|
|
2423
2502
|
"data-vector-edit": t.key,
|
|
2424
|
-
className:
|
|
2503
|
+
className: buttonClass16,
|
|
2425
2504
|
"aria-pressed": editing[t.key],
|
|
2426
2505
|
title: t.title,
|
|
2427
2506
|
"aria-label": t.title,
|
|
@@ -2430,9 +2509,9 @@ function VectorToolbar({
|
|
|
2430
2509
|
},
|
|
2431
2510
|
t.key
|
|
2432
2511
|
)),
|
|
2433
|
-
state.tool === "pencil" && /* @__PURE__ */
|
|
2512
|
+
state.tool === "pencil" && /* @__PURE__ */ jsxs19("label", { className: "flex items-center gap-1 text-xs text-foreground", children: [
|
|
2434
2513
|
"Smoothing",
|
|
2435
|
-
/* @__PURE__ */
|
|
2514
|
+
/* @__PURE__ */ jsx19(
|
|
2436
2515
|
"input",
|
|
2437
2516
|
{
|
|
2438
2517
|
type: "range",
|
|
@@ -2446,9 +2525,9 @@ function VectorToolbar({
|
|
|
2446
2525
|
}
|
|
2447
2526
|
)
|
|
2448
2527
|
] }),
|
|
2449
|
-
state.tool === "eraser" && /* @__PURE__ */
|
|
2528
|
+
state.tool === "eraser" && /* @__PURE__ */ jsxs19("label", { className: "flex items-center gap-1 text-xs text-foreground", children: [
|
|
2450
2529
|
"Size",
|
|
2451
|
-
/* @__PURE__ */
|
|
2530
|
+
/* @__PURE__ */ jsx19(
|
|
2452
2531
|
"input",
|
|
2453
2532
|
{
|
|
2454
2533
|
type: "number",
|
|
@@ -2464,12 +2543,12 @@ function VectorToolbar({
|
|
|
2464
2543
|
}
|
|
2465
2544
|
)
|
|
2466
2545
|
] }),
|
|
2467
|
-
state.tool === "paintBucket" && /* @__PURE__ */
|
|
2546
|
+
state.tool === "paintBucket" && /* @__PURE__ */ jsx19(
|
|
2468
2547
|
"button",
|
|
2469
2548
|
{
|
|
2470
2549
|
type: "button",
|
|
2471
2550
|
"data-vector-fill-gap": "",
|
|
2472
|
-
className:
|
|
2551
|
+
className: buttonClass16,
|
|
2473
2552
|
"aria-pressed": editing.fillGap > 0,
|
|
2474
2553
|
title: "Close small gaps between path ends when filling",
|
|
2475
2554
|
"aria-label": "Close gaps",
|
|
@@ -2478,12 +2557,12 @@ function VectorToolbar({
|
|
|
2478
2557
|
}
|
|
2479
2558
|
)
|
|
2480
2559
|
] }),
|
|
2481
|
-
showDistort && /* @__PURE__ */
|
|
2560
|
+
showDistort && /* @__PURE__ */ jsx19("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Distort", children: /* @__PURE__ */ jsx19(
|
|
2482
2561
|
"button",
|
|
2483
2562
|
{
|
|
2484
2563
|
type: "button",
|
|
2485
2564
|
"data-vector-free-distort": "",
|
|
2486
|
-
className:
|
|
2565
|
+
className: buttonClass16,
|
|
2487
2566
|
"aria-pressed": state.freeDistort,
|
|
2488
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)`,
|
|
2489
2568
|
"aria-label": "Free Distort",
|
|
@@ -2491,15 +2570,15 @@ function VectorToolbar({
|
|
|
2491
2570
|
children: "Distort"
|
|
2492
2571
|
}
|
|
2493
2572
|
) }),
|
|
2494
|
-
showBlend && /* @__PURE__ */
|
|
2495
|
-
showArrange && /* @__PURE__ */
|
|
2496
|
-
/* @__PURE__ */
|
|
2497
|
-
/* @__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(
|
|
2498
2577
|
"button",
|
|
2499
2578
|
{
|
|
2500
2579
|
type: "button",
|
|
2501
2580
|
"data-vector-live-boolean": "",
|
|
2502
|
-
className:
|
|
2581
|
+
className: buttonClass16,
|
|
2503
2582
|
"aria-pressed": live,
|
|
2504
2583
|
title: "Live booleans: keep the shapes editable",
|
|
2505
2584
|
"aria-label": "Live booleans",
|
|
@@ -2516,12 +2595,12 @@ function VectorToolbar({
|
|
|
2516
2595
|
act("\u25D0", `Make Clipping Mask (${modKeyLabel()}+7)`, () => controller.makeMask("clip"), count < 2),
|
|
2517
2596
|
act("\u25D1", `Release Mask (${modKeyLabel()}+Alt+7)`, () => controller.releaseMask(), !maskSelected)
|
|
2518
2597
|
] }),
|
|
2519
|
-
/* @__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(
|
|
2520
2599
|
"button",
|
|
2521
2600
|
{
|
|
2522
2601
|
type: "button",
|
|
2523
2602
|
"data-vector-pathfinder": a.id,
|
|
2524
|
-
className:
|
|
2603
|
+
className: buttonClass16,
|
|
2525
2604
|
title: a.title,
|
|
2526
2605
|
"aria-label": a.title,
|
|
2527
2606
|
disabled: count < a.min,
|
|
@@ -2530,7 +2609,7 @@ function VectorToolbar({
|
|
|
2530
2609
|
},
|
|
2531
2610
|
a.id
|
|
2532
2611
|
)) }),
|
|
2533
|
-
/* @__PURE__ */
|
|
2612
|
+
/* @__PURE__ */ jsxs19("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Arrange", children: [
|
|
2534
2613
|
act("\u21C8", "Bring to Front", () => controller.arrange("front"), count < 1),
|
|
2535
2614
|
act("\u21CA", "Send to Back", () => controller.arrange("back"), count < 1),
|
|
2536
2615
|
act("\u21E4", "Align Left", () => controller.align("left"), count < 1),
|
|
@@ -2548,19 +2627,19 @@ function VectorBlendControls({ controller }) {
|
|
|
2548
2627
|
const blend = getVectorBlendControls(state);
|
|
2549
2628
|
const o = blend.options;
|
|
2550
2629
|
const selected = blend.ids.length > 0;
|
|
2551
|
-
return /* @__PURE__ */
|
|
2552
|
-
/* @__PURE__ */
|
|
2553
|
-
/* @__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(
|
|
2554
2633
|
"select",
|
|
2555
2634
|
{
|
|
2556
2635
|
"aria-label": "Blend spacing",
|
|
2557
2636
|
className: "h-7 rounded border border-border bg-background px-1 text-xs",
|
|
2558
2637
|
value: o.spacing,
|
|
2559
2638
|
onChange: (e) => applyVectorBlendOptions(controller, { spacing: e.target.value }),
|
|
2560
|
-
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))
|
|
2561
2640
|
}
|
|
2562
2641
|
),
|
|
2563
|
-
o.spacing !== "smoothColor" && /* @__PURE__ */
|
|
2642
|
+
o.spacing !== "smoothColor" && /* @__PURE__ */ jsx19(
|
|
2564
2643
|
"input",
|
|
2565
2644
|
{
|
|
2566
2645
|
type: "number",
|
|
@@ -2576,12 +2655,12 @@ function VectorBlendControls({ controller }) {
|
|
|
2576
2655
|
}
|
|
2577
2656
|
}
|
|
2578
2657
|
),
|
|
2579
|
-
/* @__PURE__ */
|
|
2658
|
+
/* @__PURE__ */ jsx19(
|
|
2580
2659
|
"button",
|
|
2581
2660
|
{
|
|
2582
2661
|
type: "button",
|
|
2583
2662
|
"data-vector-blend-action": "orient",
|
|
2584
|
-
className:
|
|
2663
|
+
className: buttonClass16,
|
|
2585
2664
|
"aria-pressed": o.orientToSpine,
|
|
2586
2665
|
title: "Orient steps to the spine",
|
|
2587
2666
|
"aria-label": "Orient to spine",
|
|
@@ -2589,12 +2668,12 @@ function VectorBlendControls({ controller }) {
|
|
|
2589
2668
|
children: "\u293F"
|
|
2590
2669
|
}
|
|
2591
2670
|
),
|
|
2592
|
-
/* @__PURE__ */
|
|
2671
|
+
/* @__PURE__ */ jsx19(
|
|
2593
2672
|
"button",
|
|
2594
2673
|
{
|
|
2595
2674
|
type: "button",
|
|
2596
2675
|
"data-vector-blend-action": "spine",
|
|
2597
|
-
className:
|
|
2676
|
+
className: buttonClass16,
|
|
2598
2677
|
title: "Replace Spine (select a blend and a path)",
|
|
2599
2678
|
"aria-label": "Replace Spine",
|
|
2600
2679
|
disabled: !blend.canReplaceSpine,
|
|
@@ -2602,11 +2681,11 @@ function VectorBlendControls({ controller }) {
|
|
|
2602
2681
|
children: "\u2307"
|
|
2603
2682
|
}
|
|
2604
2683
|
),
|
|
2605
|
-
/* @__PURE__ */
|
|
2606
|
-
/* @__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" })
|
|
2607
2686
|
] });
|
|
2608
2687
|
}
|
|
2609
|
-
var
|
|
2688
|
+
var selectClass14 = "h-7 min-w-0 flex-1 rounded border border-border bg-background px-1 text-xs";
|
|
2610
2689
|
var numberClass11 = "h-7 w-16 rounded border border-border bg-background px-1.5 text-xs";
|
|
2611
2690
|
function VectorStylePanel({ controller, className }) {
|
|
2612
2691
|
const state = useVectorEditorState(controller);
|
|
@@ -2639,17 +2718,17 @@ function VectorStylePanel({ controller, className }) {
|
|
|
2639
2718
|
if (style) controller.setWidthProfile(id);
|
|
2640
2719
|
else controller.setDefaults({ strokeWidthProfile: widthProfilePreset(id)?.length ? widthProfilePreset(id) : null });
|
|
2641
2720
|
};
|
|
2642
|
-
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;
|
|
2643
2722
|
const arrowRow = (end, label) => {
|
|
2644
2723
|
const key = end === "start" ? "startArrow" : "endArrow";
|
|
2645
2724
|
const scaleKey = end === "start" ? "startArrowScale" : "endArrowScale";
|
|
2646
|
-
return /* @__PURE__ */
|
|
2647
|
-
/* @__PURE__ */
|
|
2648
|
-
/* @__PURE__ */
|
|
2649
|
-
/* @__PURE__ */
|
|
2650
|
-
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))
|
|
2651
2730
|
] }),
|
|
2652
|
-
/* @__PURE__ */
|
|
2731
|
+
/* @__PURE__ */ jsx19(
|
|
2653
2732
|
"input",
|
|
2654
2733
|
{
|
|
2655
2734
|
type: "number",
|
|
@@ -2675,12 +2754,12 @@ function VectorStylePanel({ controller, className }) {
|
|
|
2675
2754
|
const paint = source[key];
|
|
2676
2755
|
const hex = vectorPaintToHex6(paint);
|
|
2677
2756
|
const on = paint !== null && paint !== "none";
|
|
2678
|
-
return /* @__PURE__ */
|
|
2679
|
-
/* @__PURE__ */
|
|
2680
|
-
/* @__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 }) }),
|
|
2681
2760
|
label
|
|
2682
2761
|
] }),
|
|
2683
|
-
/* @__PURE__ */
|
|
2762
|
+
/* @__PURE__ */ jsx19(
|
|
2684
2763
|
"input",
|
|
2685
2764
|
{
|
|
2686
2765
|
type: "color",
|
|
@@ -2691,18 +2770,18 @@ function VectorStylePanel({ controller, className }) {
|
|
|
2691
2770
|
onChange: (e) => apply({ [key]: e.target.value })
|
|
2692
2771
|
}
|
|
2693
2772
|
),
|
|
2694
|
-
on && !hex && /* @__PURE__ */
|
|
2695
|
-
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" })
|
|
2696
2775
|
] });
|
|
2697
2776
|
};
|
|
2698
|
-
return /* @__PURE__ */
|
|
2699
|
-
/* @__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" }),
|
|
2700
2779
|
paintRow("fill", "Fill"),
|
|
2701
2780
|
paintRow("stroke", "Stroke"),
|
|
2702
|
-
style && /* @__PURE__ */
|
|
2703
|
-
/* @__PURE__ */
|
|
2704
|
-
/* @__PURE__ */
|
|
2705
|
-
/* @__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(
|
|
2706
2785
|
"input",
|
|
2707
2786
|
{
|
|
2708
2787
|
type: "number",
|
|
@@ -2718,33 +2797,33 @@ function VectorStylePanel({ controller, className }) {
|
|
|
2718
2797
|
}
|
|
2719
2798
|
)
|
|
2720
2799
|
] }),
|
|
2721
|
-
/* @__PURE__ */
|
|
2722
|
-
/* @__PURE__ */
|
|
2723
|
-
/* @__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)) }),
|
|
2724
2803
|
mixed("strokeAlign")
|
|
2725
2804
|
] }),
|
|
2726
|
-
/* @__PURE__ */
|
|
2727
|
-
/* @__PURE__ */
|
|
2728
|
-
/* @__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(
|
|
2729
2808
|
"select",
|
|
2730
2809
|
{
|
|
2731
2810
|
"aria-label": "Width profile",
|
|
2732
|
-
className:
|
|
2811
|
+
className: selectClass14,
|
|
2733
2812
|
value: source.widthProfile,
|
|
2734
2813
|
onChange: (e) => {
|
|
2735
2814
|
if (e.target.value !== "custom") setProfile(e.target.value);
|
|
2736
2815
|
},
|
|
2737
2816
|
children: [
|
|
2738
|
-
VECTOR_WIDTH_PRESETS.map((p) => /* @__PURE__ */
|
|
2739
|
-
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" })
|
|
2740
2819
|
]
|
|
2741
2820
|
}
|
|
2742
2821
|
),
|
|
2743
|
-
/* @__PURE__ */
|
|
2822
|
+
/* @__PURE__ */ jsx19(
|
|
2744
2823
|
"button",
|
|
2745
2824
|
{
|
|
2746
2825
|
type: "button",
|
|
2747
|
-
className:
|
|
2826
|
+
className: buttonClass16,
|
|
2748
2827
|
title: "Reset to uniform width",
|
|
2749
2828
|
"aria-label": "Reset to uniform width",
|
|
2750
2829
|
disabled: source.widthProfile === "uniform" && !style?.mixed.widthProfile,
|
|
@@ -2754,20 +2833,20 @@ function VectorStylePanel({ controller, className }) {
|
|
|
2754
2833
|
),
|
|
2755
2834
|
mixed("widthProfile")
|
|
2756
2835
|
] }),
|
|
2757
|
-
source.hasOpenPath && /* @__PURE__ */
|
|
2836
|
+
source.hasOpenPath && /* @__PURE__ */ jsxs19(Fragment6, { children: [
|
|
2758
2837
|
arrowRow("start", "Start"),
|
|
2759
2838
|
arrowRow("end", "End"),
|
|
2760
|
-
(source.startArrow || source.endArrow) && /* @__PURE__ */
|
|
2761
|
-
/* @__PURE__ */
|
|
2762
|
-
/* @__PURE__ */
|
|
2763
|
-
/* @__PURE__ */
|
|
2764
|
-
/* @__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" })
|
|
2765
2844
|
] })
|
|
2766
2845
|
] })
|
|
2767
2846
|
] }),
|
|
2768
|
-
source.corners > 0 && /* @__PURE__ */
|
|
2769
|
-
/* @__PURE__ */
|
|
2770
|
-
/* @__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(
|
|
2771
2850
|
"input",
|
|
2772
2851
|
{
|
|
2773
2852
|
type: "number",
|
|
@@ -2782,12 +2861,12 @@ function VectorStylePanel({ controller, className }) {
|
|
|
2782
2861
|
}
|
|
2783
2862
|
}
|
|
2784
2863
|
),
|
|
2785
|
-
/* @__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)) }),
|
|
2786
2865
|
mixed("cornerRadius")
|
|
2787
2866
|
] }),
|
|
2788
|
-
style && source.corners > 0 && source.cornerKind === "round" && source.cornerRadius > 0 && /* @__PURE__ */
|
|
2789
|
-
/* @__PURE__ */
|
|
2790
|
-
/* @__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(
|
|
2791
2870
|
"input",
|
|
2792
2871
|
{
|
|
2793
2872
|
type: "range",
|
|
@@ -2802,9 +2881,9 @@ function VectorStylePanel({ controller, className }) {
|
|
|
2802
2881
|
}
|
|
2803
2882
|
)
|
|
2804
2883
|
] }),
|
|
2805
|
-
style && /* @__PURE__ */
|
|
2806
|
-
/* @__PURE__ */
|
|
2807
|
-
/* @__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(
|
|
2808
2887
|
"input",
|
|
2809
2888
|
{
|
|
2810
2889
|
type: "range",
|
|
@@ -2818,25 +2897,27 @@ function VectorStylePanel({ controller, className }) {
|
|
|
2818
2897
|
}
|
|
2819
2898
|
)
|
|
2820
2899
|
] }),
|
|
2821
|
-
/* @__PURE__ */
|
|
2822
|
-
style && /* @__PURE__ */
|
|
2823
|
-
style && /* @__PURE__ */
|
|
2824
|
-
style && /* @__PURE__ */
|
|
2825
|
-
style && /* @__PURE__ */
|
|
2826
|
-
/* @__PURE__ */
|
|
2827
|
-
/* @__PURE__ */
|
|
2828
|
-
/* @__PURE__ */
|
|
2829
|
-
/* @__PURE__ */
|
|
2830
|
-
/* @__PURE__ */
|
|
2831
|
-
/* @__PURE__ */
|
|
2832
|
-
/* @__PURE__ */
|
|
2833
|
-
/* @__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 })
|
|
2834
2915
|
] });
|
|
2835
2916
|
}
|
|
2836
2917
|
|
|
2837
2918
|
// src/vector/VectorProviderEffectsPanel.tsx
|
|
2838
2919
|
import { useState as useState7, useSyncExternalStore as useSyncExternalStore2 } from "react";
|
|
2839
|
-
import { Fragment as Fragment7, jsx as
|
|
2920
|
+
import { Fragment as Fragment7, jsx as jsx20, jsxs as jsxs20 } from "react/jsx-runtime";
|
|
2840
2921
|
var btn = "rounded border border-border px-1.5 py-0.5 text-[11px] disabled:opacity-40";
|
|
2841
2922
|
function Controls({ controller, group }) {
|
|
2842
2923
|
const provider = controller.effectsProvider;
|
|
@@ -2844,11 +2925,11 @@ function Controls({ controller, group }) {
|
|
|
2844
2925
|
const { description, error } = useEffectDescription(provider, tag.effect);
|
|
2845
2926
|
const [message, setMessage] = useState7(null);
|
|
2846
2927
|
const run = (work) => void work.then(() => setMessage(null), (e) => setMessage(e instanceof Error ? e.message : String(e)));
|
|
2847
|
-
if (!provider) return /* @__PURE__ */
|
|
2848
|
-
if (error) return /* @__PURE__ */
|
|
2849
|
-
if (!description) return /* @__PURE__ */
|
|
2850
|
-
return /* @__PURE__ */
|
|
2851
|
-
/* @__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(
|
|
2852
2933
|
EffectParameters,
|
|
2853
2934
|
{
|
|
2854
2935
|
description,
|
|
@@ -2858,7 +2939,7 @@ function Controls({ controller, group }) {
|
|
|
2858
2939
|
onPreset: (v) => run(controller.updateVectorEffect(group.id, v))
|
|
2859
2940
|
}
|
|
2860
2941
|
),
|
|
2861
|
-
message && /* @__PURE__ */
|
|
2942
|
+
message && /* @__PURE__ */ jsx20("p", { role: "alert", className: "text-[11px] text-destructive", children: message })
|
|
2862
2943
|
] });
|
|
2863
2944
|
}
|
|
2864
2945
|
function VectorProviderEffectsPanel({ controller, className = "" }) {
|
|
@@ -2877,41 +2958,41 @@ function VectorProviderEffectsPanel({ controller, className = "" }) {
|
|
|
2877
2958
|
setMessage(null);
|
|
2878
2959
|
Promise.resolve().then(work).catch((e) => setMessage(e instanceof Error ? e.message : String(e)));
|
|
2879
2960
|
};
|
|
2880
|
-
return /* @__PURE__ */
|
|
2881
|
-
/* @__PURE__ */
|
|
2882
|
-
/* @__PURE__ */
|
|
2883
|
-
/* @__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" })
|
|
2884
2965
|
] }),
|
|
2885
|
-
!provider && /* @__PURE__ */
|
|
2886
|
-
browse && /* @__PURE__ */
|
|
2887
|
-
selected.map((g) => /* @__PURE__ */
|
|
2888
|
-
/* @__PURE__ */
|
|
2889
|
-
/* @__PURE__ */
|
|
2890
|
-
/* @__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" })
|
|
2891
2972
|
] }),
|
|
2892
|
-
/* @__PURE__ */
|
|
2973
|
+
/* @__PURE__ */ jsx20(Controls, { controller, group: g })
|
|
2893
2974
|
] }, g.id)),
|
|
2894
|
-
groups.length > 0 && !selected.length && /* @__PURE__ */
|
|
2975
|
+
groups.length > 0 && !selected.length && /* @__PURE__ */ jsxs20("p", { className: "text-[11px] text-muted-foreground", children: [
|
|
2895
2976
|
"Select an effect group (",
|
|
2896
2977
|
groups.length,
|
|
2897
2978
|
" in the drawing) to change it."
|
|
2898
2979
|
] }),
|
|
2899
|
-
message && /* @__PURE__ */
|
|
2980
|
+
message && /* @__PURE__ */ jsx20("p", { role: "alert", className: "text-[11px] text-destructive", children: message })
|
|
2900
2981
|
] });
|
|
2901
2982
|
}
|
|
2902
2983
|
|
|
2903
2984
|
// src/vector/VectorEditor.tsx
|
|
2904
|
-
import { jsx as
|
|
2985
|
+
import { jsx as jsx21, jsxs as jsxs21 } from "react/jsx-runtime";
|
|
2905
2986
|
function VectorEditor({ controller, tools, showStylePanel = true, height = 520, canvasProps, effects, className }) {
|
|
2906
2987
|
useEffects(controller, effects);
|
|
2907
2988
|
const effectsShown = !!effects || controller.vectorEffectGroups().length > 0;
|
|
2908
|
-
return /* @__PURE__ */
|
|
2909
|
-
/* @__PURE__ */
|
|
2910
|
-
/* @__PURE__ */
|
|
2911
|
-
/* @__PURE__ */
|
|
2912
|
-
showStylePanel && /* @__PURE__ */
|
|
2913
|
-
/* @__PURE__ */
|
|
2914
|
-
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" })
|
|
2915
2996
|
] })
|
|
2916
2997
|
] })
|
|
2917
2998
|
] });
|
|
@@ -2932,6 +3013,8 @@ export {
|
|
|
2932
3013
|
VectorLensPanel,
|
|
2933
3014
|
VectorMaskPanel,
|
|
2934
3015
|
VectorMaskedNodes,
|
|
3016
|
+
VectorPagesPanel,
|
|
3017
|
+
VectorPerspectivePanel,
|
|
2935
3018
|
VectorProviderEffectsPanel,
|
|
2936
3019
|
VectorSceneNodes,
|
|
2937
3020
|
VectorShadingPanel,
|