circuit-bricks
Version:
A modular, Lego-style SVG circuit component system for React (ALPHA - Not for production use)
1,541 lines (1,534 loc) • 85.6 kB
JavaScript
"use client";
"use strict";
const require_chunk = require('./chunks/chunk-DkEB4ore.cjs');
const require_registry = require('./chunks/registry-Clke7-Ur.cjs');
const require_CircuitCanvas = require('./chunks/CircuitCanvas-ChVdnpl7.cjs');
const require_ssrUtils = require('./chunks/ssrUtils-BTgsP4pb.cjs');
const require_SSRSafeCircuitCanvas = require('./chunks/SSRSafeCircuitCanvas-BSa852kP.cjs');
const require_llm = require('./chunks/llm-CzENYj9R.cjs');
const require_performanceUtils = require('./chunks/performanceUtils-DbSScEs-.cjs');
const require_touchUtils = require('./chunks/touchUtils-_BBqMLob.cjs');
const require_responsiveUtils = require('./chunks/responsiveUtils-DYIcaAqt.cjs');
require('./chunks/server-Vc500-G9.cjs');
const react = require_chunk.__toESM(require("react"));
const react_jsx_runtime = require_chunk.__toESM(require("react/jsx-runtime"));
//#region src/hooks/useCircuit.ts
/**
* React hook for managing circuit state
*
* @param initialState - Optional initial state for the circuit
* @returns A tuple containing the circuit state and action functions
*/
function useCircuit(initialState) {
const undoStack = (0, react.useRef)([]);
const redoStack = (0, react.useRef)([]);
const [state, setState] = (0, react.useState)({
components: initialState?.components || [],
wires: initialState?.wires || [],
selectedComponentIds: initialState?.selectedComponentIds || [],
selectedWireIds: initialState?.selectedWireIds || []
});
const saveStateForUndo = (0, react.useCallback)((prevState) => {
undoStack.current.push(JSON.parse(JSON.stringify(prevState)));
redoStack.current = [];
}, []);
const setStateWithHistory = (0, react.useCallback)((updater) => {
setState((prev) => {
saveStateForUndo(prev);
return updater(prev);
});
}, [saveStateForUndo]);
const generateId = (0, react.useCallback)((prefix) => {
return `${prefix}-${Math.random().toString(36).substring(2, 11)}`;
}, []);
const addComponent = (0, react.useCallback)((component) => {
const id = generateId("component");
setStateWithHistory((prev) => ({
...prev,
components: [...prev.components, {
...component,
id
}]
}));
return id;
}, [generateId, setStateWithHistory]);
const updateComponent = (0, react.useCallback)((id, updates) => {
setStateWithHistory((prev) => ({
...prev,
components: prev.components.map((c) => c.id === id ? {
...c,
...updates
} : c)
}));
}, [setStateWithHistory]);
const removeComponent = (0, react.useCallback)((id) => {
setStateWithHistory((prev) => {
const components = prev.components.filter((c) => c.id !== id);
const wires = prev.wires.filter((w) => w.from.componentId !== id && w.to.componentId !== id);
const selectedComponentIds = prev.selectedComponentIds.filter((selectedId) => selectedId !== id);
return {
...prev,
components,
wires,
selectedComponentIds
};
});
}, [setStateWithHistory]);
const addWire = (0, react.useCallback)((from, to, style) => {
const id = generateId("wire");
setStateWithHistory((prev) => ({
...prev,
wires: [...prev.wires, {
id,
from,
to,
style
}]
}));
return id;
}, [generateId, setStateWithHistory]);
const updateWire = (0, react.useCallback)((id, updates) => {
setStateWithHistory((prev) => ({
...prev,
wires: prev.wires.map((w) => w.id === id ? {
...w,
...updates
} : w)
}));
}, [setStateWithHistory]);
const removeWire = (0, react.useCallback)((id) => {
setStateWithHistory((prev) => ({
...prev,
wires: prev.wires.filter((w) => w.id !== id),
selectedWireIds: prev.selectedWireIds.filter((wireId) => wireId !== id)
}));
}, [setStateWithHistory]);
const selectComponent = (0, react.useCallback)((id, addToSelection = false) => {
setState((prev) => {
const selectedComponentIds = addToSelection ? [...prev.selectedComponentIds, id] : [id];
return {
...prev,
selectedComponentIds: [...new Set(selectedComponentIds)]
};
});
}, []);
const selectWire = (0, react.useCallback)((id, addToSelection = false) => {
setState((prev) => {
const selectedWireIds = addToSelection ? [...prev.selectedWireIds, id] : [id];
return {
...prev,
selectedWireIds: [...new Set(selectedWireIds)]
};
});
}, []);
const deselectAll = (0, react.useCallback)(() => {
setState((prev) => ({
...prev,
selectedComponentIds: [],
selectedWireIds: []
}));
}, []);
const getSelectedComponents = (0, react.useCallback)(() => {
return state.components.filter((c) => state.selectedComponentIds.includes(c.id));
}, [state.components, state.selectedComponentIds]);
const getSelectedWires = (0, react.useCallback)(() => {
return state.wires.filter((w) => state.selectedWireIds.includes(w.id));
}, [state.wires, state.selectedWireIds]);
const moveComponent = (0, react.useCallback)((id, newPosition) => {
setStateWithHistory((prev) => ({
...prev,
components: prev.components.map((c) => c.id === id ? {
...c,
position: newPosition
} : c)
}));
}, [setStateWithHistory]);
const moveSelectedComponents = (0, react.useCallback)((dx, dy) => {
setStateWithHistory((prev) => ({
...prev,
components: prev.components.map((c) => {
if (prev.selectedComponentIds.includes(c.id)) return {
...c,
position: {
x: c.position.x + dx,
y: c.position.y + dy
}
};
return c;
})
}));
}, [setStateWithHistory]);
const rotateComponent = (0, react.useCallback)((id, degrees) => {
setStateWithHistory((prev) => ({
...prev,
components: prev.components.map((c) => {
if (c.id === id) {
const currentRotation = c.rotation || 0;
const newRotation = (currentRotation + degrees) % 360;
return {
...c,
rotation: newRotation < 0 ? newRotation + 360 : newRotation
};
}
return c;
})
}));
}, [setStateWithHistory]);
const rotateSelectedComponents = (0, react.useCallback)((degrees) => {
setStateWithHistory((prev) => ({
...prev,
components: prev.components.map((c) => {
if (prev.selectedComponentIds.includes(c.id)) {
const currentRotation = c.rotation || 0;
const newRotation = (currentRotation + degrees) % 360;
return {
...c,
rotation: newRotation < 0 ? newRotation + 360 : newRotation
};
}
return c;
})
}));
}, [setStateWithHistory]);
const undo = (0, react.useCallback)(() => {
if (undoStack.current.length > 0) {
redoStack.current.push(JSON.parse(JSON.stringify(state)));
const prevState = undoStack.current.pop();
setState(prevState);
}
}, [state]);
const redo = (0, react.useCallback)(() => {
if (redoStack.current.length > 0) {
undoStack.current.push(JSON.parse(JSON.stringify(state)));
const nextState = redoStack.current.pop();
setState(nextState);
}
}, [state]);
const canUndo = (0, react.useCallback)(() => {
return undoStack.current.length > 0;
}, []);
const canRedo = (0, react.useCallback)(() => {
return redoStack.current.length > 0;
}, []);
const [wireDrawingState, setWireDrawingState] = (0, react.useState)({
isDrawing: false,
fromComponentId: null,
fromPortId: null
});
const startWireDrawing = (0, react.useCallback)((componentId, portId) => {
setWireDrawingState({
isDrawing: true,
fromComponentId: componentId,
fromPortId: portId
});
}, []);
const completeWireDrawing = (0, react.useCallback)((toComponentId, toPortId) => {
const { isDrawing, fromComponentId, fromPortId } = wireDrawingState;
if (!isDrawing || !fromComponentId || !fromPortId) return false;
const isValid = isValidConnection(fromComponentId, fromPortId, toComponentId, toPortId);
if (isValid) {
addWire({
componentId: fromComponentId,
portId: fromPortId
}, {
componentId: toComponentId,
portId: toPortId
});
setWireDrawingState({
isDrawing: false,
fromComponentId: null,
fromPortId: null
});
return true;
}
return false;
}, [wireDrawingState, addWire]);
const cancelWireDrawing = (0, react.useCallback)(() => {
setWireDrawingState({
isDrawing: false,
fromComponentId: null,
fromPortId: null
});
}, []);
const isValidConnection = (0, react.useCallback)((fromId, fromPortId, toId, toPortId) => {
if (fromId === toId) return false;
const isDuplicate = state.wires.some((wire) => wire.from.componentId === fromId && wire.from.portId === fromPortId && wire.to.componentId === toId && wire.to.portId === toPortId || wire.from.componentId === toId && wire.from.portId === toPortId && wire.to.componentId === fromId && wire.to.portId === fromPortId);
if (isDuplicate) return false;
const fromComponent = state.components.find((c) => c.id === fromId);
const toComponent = state.components.find((c) => c.id === toId);
if (!fromComponent || !toComponent) return false;
const fromSchema = require_registry.getComponentSchema(fromComponent.type);
const toSchema = require_registry.getComponentSchema(toComponent.type);
if (!fromSchema || !toSchema) return false;
const fromPort = fromSchema.ports.find((p) => p.id === fromPortId);
const toPort = toSchema.ports.find((p) => p.id === toPortId);
if (!fromPort || !toPort) return false;
if (fromPort.type === "output" && toPort.type === "input") return true;
if (fromPort.type === "input" && toPort.type === "output") return true;
if (fromPort.type === "inout" || toPort.type === "inout") return true;
return false;
}, [state.components, state.wires]);
const actions = {
addComponent,
updateComponent,
removeComponent,
addWire,
updateWire,
removeWire,
selectComponent,
selectWire,
deselectAll,
getSelectedComponents,
getSelectedWires,
moveComponent,
moveSelectedComponents,
startWireDrawing,
completeWireDrawing,
cancelWireDrawing,
isValidConnection,
rotateComponent,
rotateSelectedComponents,
undo,
redo,
canUndo,
canRedo
};
return [state, actions];
}
var useCircuit_default = useCircuit;
//#endregion
//#region src/hooks/usePortPosition.ts
/**
* A hook to track the positions of ports connected by a wire
*
* @param wire - The wire connecting the ports
* @param deps - Additional dependencies that should trigger position updates
* @returns Object containing port positions and error state
*/
function usePortPosition(wire, deps = []) {
const [state, setState] = (0, react.useState)({
from: null,
to: null,
error: null
});
const updatePositions = (0, react.useCallback)(() => {
if (!wire) {
setState({
from: null,
to: null,
error: "No wire provided"
});
return;
}
setTimeout(() => {
try {
const fromPos = require_CircuitCanvas.getPortPosition(wire.from.componentId, wire.from.portId);
const toPos = require_CircuitCanvas.getPortPosition(wire.to.componentId, wire.to.portId);
setState({
from: fromPos,
to: toPos,
error: !fromPos || !toPos ? `Could not find ports for wire ${wire.id}` : null
});
} catch (error) {
setState({
from: null,
to: null,
error: `Error getting port positions: ${error}`
});
}
}, 0);
}, [wire]);
(0, react.useEffect)(() => {
updatePositions();
window.addEventListener("resize", updatePositions);
const handleViewportChange = () => {
updatePositions();
};
const canvas = document.querySelector("[data-circuit-canvas]");
if (canvas) {
canvas.addEventListener("wheel", updatePositions, { passive: true });
canvas.addEventListener("viewport-change", handleViewportChange);
}
return () => {
window.removeEventListener("resize", updatePositions);
if (canvas) {
canvas.removeEventListener("wheel", updatePositions);
canvas.removeEventListener("viewport-change", handleViewportChange);
}
};
}, [
wire,
updatePositions,
...deps
]);
return state;
}
/**
* Hook to track a single port position
*
* @param componentId - The component ID
* @param portId - The port ID
* @param deps - Additional dependencies that should trigger position updates
* @returns The port position or null if not found
*/
function useSinglePortPosition(componentId, portId, deps = []) {
const [position, setPosition] = (0, react.useState)(null);
const updatePosition = (0, react.useCallback)(() => {
if (!componentId || !portId) {
setPosition(null);
return;
}
setTimeout(() => {
try {
const pos = require_CircuitCanvas.getPortPosition(componentId, portId);
setPosition(pos);
} catch (error) {
console.warn(`Error getting port position: ${error}`);
setPosition(null);
}
}, 0);
}, [componentId, portId]);
(0, react.useEffect)(() => {
updatePosition();
window.addEventListener("resize", updatePosition);
const handleViewportChange = () => {
updatePosition();
};
const canvas = document.querySelector("[data-circuit-canvas]");
if (canvas) {
canvas.addEventListener("wheel", updatePosition, { passive: true });
canvas.addEventListener("viewport-change", handleViewportChange);
}
return () => {
window.removeEventListener("resize", updatePosition);
if (canvas) {
canvas.removeEventListener("wheel", updatePosition);
canvas.removeEventListener("viewport-change", handleViewportChange);
}
};
}, [
componentId,
portId,
updatePosition,
...deps
]);
return position;
}
var usePortPosition_default = usePortPosition;
//#endregion
//#region src/ui/headless/HeadlessPropertyPanel.tsx
const HeadlessPropertyPanel = ({ component, onPropertyChange, className = "", classNames = {}, style = {}, styles = {} }) => {
if (!component) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
className: `cb-property-panel-empty ${classNames.emptyContainer || ""} ${className}`,
style,
"data-testid": "property-panel-empty",
children: [
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("svg", {
width: "48",
height: "48",
viewBox: "0 0 24 24",
fill: "none",
stroke: "currentColor",
strokeWidth: "1.5",
strokeLinecap: "round",
strokeLinejoin: "round",
"aria-hidden": "true",
children: [
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("rect", {
x: "2",
y: "2",
width: "20",
height: "8",
rx: "2",
ry: "2"
}),
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("rect", {
x: "2",
y: "14",
width: "20",
height: "8",
rx: "2",
ry: "2"
}),
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("line", {
x1: "6",
y1: "6",
x2: "6.01",
y2: "6"
}),
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("line", {
x1: "6",
y1: "18",
x2: "6.01",
y2: "18"
})
]
}),
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: "No component selected" }),
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: "Select a component to edit its properties" })
]
});
const schema = require_registry.getComponentSchema(component.type);
if (!schema) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
className: `cb-property-panel-error ${classNames.errorContainer || ""} ${className}`,
style,
"data-testid": "property-panel-error",
role: "alert",
children: [
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("svg", {
width: "48",
height: "48",
viewBox: "0 0 24 24",
fill: "none",
stroke: "currentColor",
strokeWidth: "1.5",
strokeLinecap: "round",
strokeLinejoin: "round",
"aria-hidden": "true",
children: [
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("circle", {
cx: "12",
cy: "12",
r: "10"
}),
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("line", {
x1: "12",
y1: "8",
x2: "12",
y2: "12"
}),
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("line", {
x1: "12",
y1: "16",
x2: "12.01",
y2: "16"
})
]
}),
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", { children: [
"Error: Unknown component type '",
component.type,
"'"
] }),
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: "This component type is not registered in the system" })
]
});
const handleChange = (key, event) => {
const property = schema.properties.find((prop) => prop.key === key);
if (!property) return;
let value = event.target.value;
if (property.type === "number") {
value = parseFloat(value);
if (property.min !== void 0 && value < property.min) value = property.min;
if (property.max !== void 0 && value > property.max) value = property.max;
} else if (property.type === "boolean") value = event.target.checked;
onPropertyChange?.(component.id, key, value);
};
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
className: `cb-property-panel ${className}`,
style,
"data-testid": "property-panel",
children: [
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
className: `cb-property-panel-header ${classNames.header || ""}`,
style: styles.header,
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("h3", {
className: `cb-property-panel-title ${classNames.title || ""}`,
style: styles.title,
children: [schema.name, " Properties"]
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
className: `cb-property-panel-component-id ${classNames.componentId || ""}`,
style: styles.componentId,
children: ["ID: ", component.id]
})]
}),
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
className: `cb-property-panel-content ${classNames.content || ""}`,
style: styles.content,
children: schema.properties.map((property) => {
const currentValue = component.props[property.key] ?? property.default;
const inputId = `prop-${component.id}-${property.key}`;
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
className: `cb-property-item ${classNames.propertyItem || ""}`,
style: styles.propertyItem,
"data-property-key": property.key,
children: [
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
htmlFor: inputId,
className: `cb-property-label ${classNames.propertyLabel || ""}`,
style: styles.propertyLabel,
children: [property.label, property.unit && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
className: `cb-property-unit ${classNames.propertyUnit || ""}`,
style: styles.propertyUnit,
children: property.unit
})]
}),
property.type === "number" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
id: inputId,
type: "number",
value: currentValue,
onChange: (e) => handleChange(property.key, e),
min: property.min,
max: property.max,
step: "any",
className: `cb-property-number-input ${classNames.numberInput || ""}`,
style: styles.numberInput,
"aria-label": `${property.label} ${property.unit || ""}`
}),
property.type === "text" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
id: inputId,
type: "text",
value: currentValue,
onChange: (e) => handleChange(property.key, e),
className: `cb-property-text-input ${classNames.textInput || ""}`,
style: styles.textInput,
"aria-label": property.label
}),
property.type === "boolean" && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
className: `cb-property-checkbox-container ${classNames.checkboxContainer || ""}`,
style: styles.checkboxContainer,
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
id: inputId,
type: "checkbox",
checked: currentValue,
onChange: (e) => handleChange(property.key, e),
className: `cb-property-checkbox ${classNames.checkbox || ""}`,
style: styles.checkbox,
"aria-label": property.label
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
className: `cb-property-checkbox-label ${classNames.checkboxLabel || ""}`,
style: styles.checkboxLabel,
children: currentValue ? "Enabled" : "Disabled"
})]
}),
property.type === "select" && property.options && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("select", {
id: inputId,
value: currentValue,
onChange: (e) => handleChange(property.key, e),
className: `cb-property-select ${classNames.select || ""}`,
style: styles.select,
"aria-label": property.label,
children: property.options.map((option) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
value: option.value,
children: option.label
}, option.value))
}),
property.type === "color" && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
className: `cb-property-color-container ${classNames.colorContainer || ""}`,
style: styles.colorContainer,
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
id: inputId,
type: "color",
value: currentValue,
onChange: (e) => handleChange(property.key, e),
className: `cb-property-color-input ${classNames.colorInput || ""}`,
style: styles.colorInput,
"aria-label": `${property.label} color picker`
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
type: "text",
value: currentValue,
onChange: (e) => handleChange(property.key, e),
className: `cb-property-color-text-input ${classNames.colorTextInput || ""}`,
style: styles.colorTextInput,
"aria-label": `${property.label} color value`
})]
})
]
}, property.key);
})
}),
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
className: `cb-property-panel-position ${classNames.positionSection || ""}`,
style: styles.positionSection,
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h4", {
className: `cb-property-panel-position-title ${classNames.positionTitle || ""}`,
style: styles.positionTitle,
children: "Position"
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
className: `cb-property-panel-position-inputs ${classNames.positionInputs || ""}`,
style: styles.positionInputs,
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
className: `cb-property-panel-position-input-group ${classNames.positionInputGroup || ""}`,
style: styles.positionInputGroup,
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
className: `cb-property-panel-position-label ${classNames.positionLabel || ""}`,
style: styles.positionLabel,
htmlFor: "position-x",
children: "X:"
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
id: "position-x",
type: "number",
value: component.position.x,
onChange: (e) => onPropertyChange?.(component.id, "position", {
...component.position,
x: parseFloat(e.target.value)
}),
className: `cb-property-panel-position-input ${classNames.positionInput || ""}`,
style: styles.positionInput,
"aria-label": "X position"
})]
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
className: `cb-property-panel-position-input-group ${classNames.positionInputGroup || ""}`,
style: styles.positionInputGroup,
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
className: `cb-property-panel-position-label ${classNames.positionLabel || ""}`,
style: styles.positionLabel,
htmlFor: "position-y",
children: "Y:"
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
id: "position-y",
type: "number",
value: component.position.y,
onChange: (e) => onPropertyChange?.(component.id, "position", {
...component.position,
y: parseFloat(e.target.value)
}),
className: `cb-property-panel-position-input ${classNames.positionInput || ""}`,
style: styles.positionInput,
"aria-label": "Y position"
})]
})]
})]
}),
component.rotation !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
className: `cb-property-panel-rotation ${classNames.rotationSection || ""}`,
style: styles.rotationSection,
children: [
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h4", {
className: `cb-property-panel-rotation-title ${classNames.rotationTitle || ""}`,
style: styles.rotationTitle,
children: "Rotation"
}),
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
className: `cb-property-panel-rotation-slider ${classNames.rotationSlider || ""}`,
style: styles.rotationSlider,
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
type: "range",
min: "0",
max: "360",
step: "90",
value: component.rotation,
onChange: (e) => onPropertyChange?.(component.id, "rotation", parseFloat(e.target.value)),
className: `cb-property-panel-rotation-range ${classNames.rotationRange || ""}`,
style: styles.rotationRange,
"aria-label": "Rotation angle"
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
className: `cb-property-panel-rotation-value ${classNames.rotationValue || ""}`,
style: styles.rotationValue,
children: [component.rotation, "°"]
})]
}),
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
className: `cb-property-panel-rotation-presets ${classNames.rotationPresets || ""}`,
style: styles.rotationPresets,
children: [
0,
90,
180,
270,
360
].map((angle) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
onClick: () => onPropertyChange?.(component.id, "rotation", angle),
className: `cb-property-panel-rotation-preset-button ${classNames.rotationPresetButton || ""}`,
style: styles.rotationPresetButton,
"data-active": component.rotation === angle,
"aria-label": `Set rotation to ${angle} degrees`,
"aria-pressed": component.rotation === angle,
children: [angle, "°"]
}, angle))
})
]
})
]
});
};
var HeadlessPropertyPanel_default = HeadlessPropertyPanel;
//#endregion
//#region src/ui/PropertyPanel.tsx
const PropertyPanel = ({ component, onPropertyChange, className = "", style = {} }) => {
const rootStyle = {
backgroundColor: "#1a202c",
borderRadius: "12px",
boxShadow: "0 4px 12px rgba(0, 0, 0, 0.15)",
overflow: "hidden",
border: "1px solid #2d3748",
color: "#e2e8f0",
...style
};
const defaultStyles = {
emptyContainer: {
backgroundColor: "#1a202c",
borderRadius: "12px",
padding: "24px",
display: "flex",
flexDirection: "column",
alignItems: "center",
justifyContent: "center",
color: "#a0aec0",
height: "100%",
boxShadow: "0 4px 12px rgba(0, 0, 0, 0.1)",
border: "1px solid rgb(21, 25, 32)",
textAlign: "center"
},
errorContainer: {
backgroundColor: "#1a202c",
borderRadius: "12px",
padding: "24px",
display: "flex",
flexDirection: "column",
alignItems: "center",
justifyContent: "center",
color: "#f56565",
boxShadow: "0 4px 12px rgba(0, 0, 0, 0.1)",
border: "1px solid rgb(21, 26, 34)",
textAlign: "center"
},
header: {
padding: "16px",
borderBottom: "1px solid #2d3748",
background: "#202938"
},
title: {
margin: "0 0 4px 0",
fontSize: "18px",
fontWeight: 600,
color: "#e2e8f0"
},
componentId: {
fontSize: "12px",
color: "#a0aec0",
opacity: .7
},
content: { padding: "16px" },
propertyItem: { marginBottom: "16px" },
propertyLabel: {
display: "block",
marginBottom: "6px",
fontSize: "14px",
fontWeight: 500,
color: "#cbd5e0"
},
propertyUnit: {
marginLeft: "4px",
color: "#a0aec0",
fontSize: "12px"
},
numberInput: {
width: "100%",
padding: "8px 12px",
borderRadius: "6px",
border: "1px solid #4a5568",
backgroundColor: "#2d3748",
color: "#e2e8f0",
fontSize: "14px",
outline: "none",
transition: "all 0.2s ease",
boxSizing: "border-box"
},
textInput: {
width: "100%",
padding: "8px 12px",
borderRadius: "6px",
border: "1px solid #4a5568",
backgroundColor: "#2d3748",
color: "#e2e8f0",
fontSize: "14px",
outline: "none",
transition: "all 0.2s ease",
boxSizing: "border-box"
},
checkboxContainer: {
display: "flex",
alignItems: "center"
},
checkbox: {
width: "18px",
height: "18px",
cursor: "pointer",
accentColor: "#4299e1",
borderRadius: "4px",
marginRight: "8px"
},
checkboxLabel: {
fontSize: "14px",
color: "#cbd5e0"
},
select: {
width: "100%",
padding: "8px 12px",
borderRadius: "6px",
border: "1px solid #4a5568",
backgroundColor: "#2d3748",
color: "#e2e8f0",
fontSize: "14px",
outline: "none",
transition: "all 0.2s ease",
cursor: "pointer",
appearance: "none",
backgroundImage: "url(\"data:image/svg+xml;charset=US-ASCII,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%22292.4%22%20height%3D%22292.4%22%3E%3Cpath%20fill%3D%22%23a0aec0%22%20d%3D%22M287%2069.4a17.6%2017.6%200%200%200-13-5.4H18.4c-5%200-9.3%201.8-12.9%205.4A17.6%2017.6%200%200%200%200%2082.2c0%205%201.8%209.3%205.4%2012.9l128%20127.9c3.6%203.6%207.8%205.4%2012.8%205.4s9.2-1.8%2012.8-5.4L287%2095c3.5-3.5%205.4-7.8%205.4-12.8%200-5-1.9-9.2-5.5-12.8z%22%2F%3E%3C%2Fsvg%3E\")",
backgroundRepeat: "no-repeat",
backgroundPosition: "right 12px top 50%",
backgroundSize: "12px auto",
paddingRight: "30px"
},
colorContainer: {
display: "flex",
alignItems: "center",
gap: "10px"
},
colorInput: {
width: "36px",
height: "36px",
border: "none",
borderRadius: "6px",
backgroundColor: "transparent",
cursor: "pointer"
},
colorTextInput: {
flexGrow: 1,
padding: "8px 12px",
borderRadius: "6px",
border: "1px solid #4a5568",
backgroundColor: "#2d3748",
color: "#e2e8f0",
fontSize: "14px"
},
positionSection: {
padding: "0 16px 16px",
borderTop: "1px solid #2d3748",
marginTop: "8px"
},
positionTitle: {
fontSize: "16px",
fontWeight: 500,
margin: "16px 0 12px",
color: "#cbd5e0"
},
positionInputs: {
display: "grid",
gridTemplateColumns: "1fr 1fr",
gap: "12px"
},
positionInputGroup: {
display: "flex",
flexDirection: "column"
},
positionLabel: {
fontSize: "14px",
fontWeight: 500,
marginBottom: "6px",
color: "#cbd5e0"
},
positionInput: {
width: "100%",
padding: "8px 12px",
borderRadius: "6px",
border: "1px solid #4a5568",
backgroundColor: "#2d3748",
color: "#e2e8f0",
fontSize: "14px",
outline: "none",
transition: "all 0.2s ease"
},
rotationSection: {
padding: "0 16px 16px",
borderTop: "1px solid #2d3748"
},
rotationTitle: {
fontSize: "16px",
fontWeight: 500,
margin: "16px 0 12px",
color: "#cbd5e0"
},
rotationSlider: {
display: "flex",
alignItems: "center",
gap: "12px"
},
rotationRange: {
flex: 1,
height: "6px",
borderRadius: "3px",
backgroundColor: "#4a5568",
appearance: "none",
outline: "none",
cursor: "pointer",
accentColor: "#4299e1"
},
rotationValue: {
padding: "6px 10px",
backgroundColor: "#2d3748",
borderRadius: "6px",
border: "1px solid #4a5568",
fontSize: "14px",
fontWeight: 500,
color: "#e2e8f0",
minWidth: "48px",
textAlign: "center"
},
rotationPresets: {
display: "flex",
justifyContent: "space-between",
marginTop: "8px"
},
rotationPresetButton: {
background: "#2d3748",
border: "none",
borderRadius: "4px",
width: "32px",
height: "24px",
fontSize: "12px",
color: "#cbd5e0",
cursor: "pointer",
transition: "all 0.2s ease",
display: "flex",
alignItems: "center",
justifyContent: "center"
}
};
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(HeadlessPropertyPanel_default, {
component,
onPropertyChange,
className,
style: rootStyle,
styles: defaultStyles
});
};
var PropertyPanel_default = PropertyPanel;
//#endregion
//#region src/ui/headless/HeadlessComponentPalette.tsx
const HeadlessComponentPalette = ({ onSelectComponent, onDragComponent, className = "", classNames = {}, style = {}, styles = {} }) => {
const [searchTerm, setSearchTerm] = (0, react.useState)("");
const [activeCategory, setActiveCategory] = (0, react.useState)(null);
const allComponents = require_registry.getAllComponents();
const allCategories = Array.from(new Set(allComponents.map((comp) => comp.category))).sort();
const filteredComponents = activeCategory ? require_registry.getComponentsByCategory(activeCategory) : allComponents;
const searchFilteredComponents = searchTerm ? filteredComponents.filter((comp) => comp.name.toLowerCase().includes(searchTerm.toLowerCase()) || comp.description.toLowerCase().includes(searchTerm.toLowerCase())) : filteredComponents;
const tabsRef = (0, react.useRef)(null);
const listRef = (0, react.useRef)(null);
const [showTabsLeftShadow, setShowTabsLeftShadow] = (0, react.useState)(false);
const [showTabsRightShadow, setShowTabsRightShadow] = (0, react.useState)(false);
const [showListTopShadow, setShowListTopShadow] = (0, react.useState)(false);
const [showListBottomShadow, setShowListBottomShadow] = (0, react.useState)(false);
const handleTabsScroll = () => {
if (tabsRef.current) {
const { scrollLeft, scrollWidth, clientWidth } = tabsRef.current;
setShowTabsLeftShadow(scrollLeft > 0);
setShowTabsRightShadow(scrollLeft < scrollWidth - clientWidth - 1);
}
};
const handleListScroll = () => {
if (listRef.current) {
const { scrollTop, scrollHeight, clientHeight } = listRef.current;
setShowListTopShadow(scrollTop > 0);
setShowListBottomShadow(scrollTop < scrollHeight - clientHeight - 1);
}
};
(0, react.useEffect)(() => {
handleTabsScroll();
handleListScroll();
const handleResize = () => {
handleTabsScroll();
handleListScroll();
};
window.addEventListener("resize", handleResize);
return () => {
window.removeEventListener("resize", handleResize);
};
}, []);
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
className: `cb-component-palette ${className}`,
style,
"data-testid": "component-palette",
children: [
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
className: `cb-component-palette-header ${classNames.header || ""}`,
style: styles.header,
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
className: `cb-component-palette-title ${classNames.title || ""}`,
style: styles.title,
children: "Components"
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
className: `cb-component-palette-search-container ${classNames.searchContainer || ""}`,
style: styles.searchContainer,
children: [
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
type: "text",
placeholder: "Search components...",
value: searchTerm,
onChange: (e) => setSearchTerm(e.target.value),
className: `cb-component-palette-search-input ${classNames.searchInput || ""}`,
style: styles.searchInput,
"aria-label": "Search components"
}),
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("svg", {
width: "18",
height: "18",
viewBox: "0 0 24 24",
fill: "none",
stroke: "currentColor",
strokeWidth: "2",
strokeLinecap: "round",
strokeLinejoin: "round",
className: `cb-component-palette-search-icon ${classNames.searchIcon || ""}`,
style: styles.searchIcon,
"aria-hidden": "true",
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("circle", {
cx: "11",
cy: "11",
r: "8"
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("line", {
x1: "21",
y1: "21",
x2: "16.65",
y2: "16.65"
})]
}),
searchTerm && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
onClick: () => setSearchTerm(""),
className: `cb-component-palette-clear-search ${classNames.clearSearchButton || ""}`,
style: styles.clearSearchButton,
"aria-label": "Clear search",
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("svg", {
width: "16",
height: "16",
viewBox: "0 0 24 24",
fill: "none",
stroke: "currentColor",
strokeWidth: "2",
strokeLinecap: "round",
strokeLinejoin: "round",
"aria-hidden": "true",
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("line", {
x1: "18",
y1: "6",
x2: "6",
y2: "18"
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("line", {
x1: "6",
y1: "6",
x2: "18",
y2: "18"
})]
})
})
]
})]
}),
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
className: "cb-component-palette-tabs-wrapper",
children: [
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
ref: tabsRef,
className: `cb-component-palette-category-tabs ${classNames.categoryTabs || ""}`,
style: styles.categoryTabs,
onScroll: handleTabsScroll,
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
className: `cb-component-palette-category-tabs-inner ${classNames.categoryTabsInner || ""}`,
style: styles.categoryTabsInner,
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
className: `cb-component-palette-category-tab ${activeCategory === null ? "cb-component-palette-category-tab-active" : ""} ${classNames.categoryTab || ""} ${activeCategory === null ? classNames.activeCategoryTab || "" : ""}`,
onClick: () => setActiveCategory(null),
style: activeCategory === null ? {
...styles.categoryTab,
...styles.activeCategoryTab
} : styles.categoryTab,
"data-active": activeCategory === null,
"aria-pressed": activeCategory === null,
children: "All"
}), allCategories.map((category) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
className: `cb-component-palette-category-tab ${activeCategory === category ? "cb-component-palette-category-tab-active" : ""} ${classNames.categoryTab || ""} ${activeCategory === category ? classNames.activeCategoryTab || "" : ""}`,
onClick: () => setActiveCategory(category),
style: activeCategory === category ? {
...styles.categoryTab,
...styles.activeCategoryTab
} : styles.categoryTab,
"data-active": activeCategory === category,
"aria-pressed": activeCategory === category,
children: category.charAt(0).toUpperCase() + category.slice(1)
}, category))]
})
}),
showTabsLeftShadow && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
className: `cb-component-palette-left-shadow ${classNames.leftShadow || ""}`,
style: styles.leftShadow,
"aria-hidden": "true"
}),
showTabsRightShadow && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
className: `cb-component-palette-right-shadow ${classNames.rightShadow || ""}`,
style: styles.rightShadow,
"aria-hidden": "true"
})
]
}),
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
className: "cb-component-palette-list-wrapper",
children: [
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
ref: listRef,
className: `cb-component-palette-component-list ${classNames.componentList || ""}`,
style: styles.componentList,
onScroll: handleListScroll,
children: searchFilteredComponents.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
className: `cb-component-palette-empty-state ${classNames.emptyState || ""}`,
style: styles.emptyState,
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("svg", {
width: "40",
height: "40",
viewBox: "0 0 24 24",
fill: "none",
stroke: "currentColor",
strokeWidth: "2",
strokeLinecap: "round",
strokeLinejoin: "round",
"aria-hidden": "true",
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("circle", {
cx: "11",
cy: "11",
r: "8"
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("line", {
x1: "21",
y1: "21",
x2: "16.65",
y2: "16.65"
})]
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: "No components found." })]
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
className: "cb-component-palette-component-items",
children: searchFilteredComponents.map((component) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(HeadlessComponentListItem, {
component,
onSelect: () => onSelectComponent(component.id),
onDrag: onDragComponent ? (e) => onDragComponent(component.id, e) : void 0,
classNames,
styles
}, component.id))
})
}),
showListTopShadow && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
className: `cb-component-palette-top-shadow ${classNames.topShadow || ""}`,
style: styles.topShadow,
"aria-hidden": "true"
}),
showListBottomShadow && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
className: `cb-component-palette-bottom-shadow ${classNames.bottomShadow || ""}`,
style: styles.bottomShadow,
"aria-hidden": "true"
})
]
})
]
});
};
const HeadlessComponentListItem = ({ component, onSelect, onDrag, classNames = {}, styles = {} }) => {
const handleDragStart = (event) => {
event.dataTransfer.setData("application/circuit-component", component.id);
event.dataTransfer.effectAllowed = "copy";
const dragImg = document.createElement("div");
dragImg.innerHTML = `
<svg width="40" height="40" viewBox="0 0 ${component.defaultWidth} ${component.defaultHeight}">
<path
d="${component.svgPath}"
fill="none"
stroke="#333"
stroke-width="2"
/>
</svg>
`;
dragImg.style.position = "absolute";
dragImg.style.top = "-1000px";
document.body.appendChild(dragImg);
event.dataTransfer.setDragImage(dragImg, 20, 20);
onDrag?.(event);
setTimeout(() => {
document.body.removeChild(dragImg);
}, 0);
};
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", {
className: `cb-component-item ${classNames.componentItem || ""}`,
onClick: onSelect,
title: component.description,
draggable: true,
onDragStart: handleDragStart,
style: styles.componentItem,
"data-component-id": component.id,
"data-component-category": component.category,
children: [
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
className: `cb-component-icon ${classNames.componentIcon || ""}`,
style: styles.componentIcon,
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
width: "32",
height: "32",
viewBox: `0 0 ${component.defaultWidth} ${component.defaultHeight}`,
preserveAspectRatio: "xMidYMid meet",
"aria-hidden": "true",
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
d: component.svgPath,
fill: "none",
stroke: "currentColor",
strokeWidth: 2,
vectorEffect: "non-scaling-stroke"
})
})
}),
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
className: `cb-component-details ${classNames.componentDetails || ""}`,
style: styles.componentDetails,
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
className: `cb-component-name ${classNames.componentName || ""}`,
style: styles.componentName,
children: component.name
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
className: `cb-component-category ${classNames.componentCategory || ""}`,
style: styles.componentCategory,
children: component.category
})]
}),
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
className: `cb-component-arrow ${classNames.componentArrow || ""}`,
style: styles.componentArrow,
"aria-hidden": "true",
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
width: "16",
height: "16",
viewBox: "0 0 24 24",
fill: "none",
stroke: "currentColor",
strokeWidth: "2",
strokeLinecap: "round",
strokeLinejoin: "round",
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("polyline", { points: "9 18 15 12 9 6" })
})
})
]
});
};
var HeadlessComponentPalette_default = HeadlessComponentPalette;
//#endregion
//#region src/ui/ComponentPalette.tsx
const ComponentPalette = ({ onSelectComponent, onDragComponent, className = "", style = {} }) => {
const rootStyle = {
display: "flex",
flexDirection: "column",
background: "#2d3748",
borderRadius: "12px",
boxShadow: "0 4px 12px rgba(0, 0, 0, 0.15)",
overflow: "hidden",
height: "100%",
border: "1px solid #1a202c",
...style
};
const defaultStyles = {
header: {
padding: "16px",
borderBottom: "1px solid #1a202c",
background: "#202938"
},
title: {
margin: "0 0 12px 0",
fontSize: "18px",
fontWeight: 600,
color: "#e2e8f0"
},
searchContainer: {
position: "relative",
display: "flex",
alignItems: "center"
},
searchInput: {
width: "100%",
padding: "10px 12px 10px 36px",
fontSize: "14px",
border: "1px solid #4a5568",
borderRadius: "8px",
backgroundColor: "#1a202c",
color: "#e2e8f0",
transition: "all 0.2s ease",
outline: "none",
boxSizing: "border-box",
boxShadow: "inset 0 1px 2px rgba(0, 0, 0, 0.2)"
},
searchIcon: {
position: "absolute",
left: "12px",
pointerEvents: "none",
color: "#a0aec0"
},
clearSearchButton: {
position: "absolute",
right: "12px",
background: "none",
border: "none",
cursor: "pointer",
padding: "0",
display: "flex",
alignItems: "center",
justifyContent: "center",
color: "#a0aec0"
},
categoryTabs: {
display: "flex",
overflowX: "auto",
padding: "0 16px",
scrollbarWidth: "none",
msOverflowStyle: "none",
background: "#202938",
position: "relative",
zIndex: 1
},
categoryTabsInner: {
display: "flex",
padding: "8px 0",
gap: "8px"
},
categoryTab: {
padding: "8px 16px",
borderRadius: "6px",
fontSize: "14px",
fontWeight: 500,
border: "none",
background: "transparent",
color: "#a0aec0",
cursor: "pointer",
whiteSpace: "nowrap",
transition: "all 0.2s ease",
boxShadow: "none"
},
activeCategoryTab: {
background: "#3182ce",
color: "#fff",
boxShadow: "0 1px 3px rgba(0, 0, 0, 0.2)"
},
componentList: {
overflowY: "auto",
height: "100%",
padding: "0 16px 16px",
position: "relative",
zIndex: 1
},
emptyState: {
display: "flex",
flexDirection: "column",
alignItems: "center",
justifyContent: "center",
padding: "32px 0",
color: "#718096",
textAlign: "center",
margin: "16px 0"
},
componentItems: {
padding: 0,
margin: "16px 0 0 0",
display: "grid",
gap: "10px"
},
componentItem: {
display: "flex",
alignItems: "center",
padding: "10px 12px",
borderRadius: "8px",
background: "#1a202c",
border: "1px solid #2d3748",
boxShadow: "0 1px 3px rgba(0, 0, 0, 0.2)",
cursor: "pointer",
transition: "all 0.2s ease",
gap: "12px",
listStyle: "none"
},
componentIcon: {
display: "flex",
alignItems: "center",
justifyContent: "center",
width: "48px",
height: "48px",
borderRadius: "6px",
background: "#2d3748",
color: "#a0aec0",
flexShrink: 0
},
componentDetails: {
display: "flex",
flexDirection: "column",
overflow: "hidden"
},
componentName: {
fontWeight: 500,
fontSize: "14px",
color: "#e2e8f0",
whiteSpace: "nowrap",
overflow: "hidden",
textOverflow: "ellipsis",
opacity: .9
},
componentCategory: {
fontSize: "12px",
color: "#a0aec0",
marginTop: "2px",
opacity: .7
},
componentArrow: {
marginLeft: "auto",
opacity: .4,
flexShrink: 0,
color: "#718096"
},
leftShadow: {
position: "absolute",
top: 0,
left: 0,
width: "16px",
height: "100%",
background: "linear-gradient(to right, rgba(32, 41, 56, 0.9), rgba(32, 41, 56, 0))",
zIndex: 2,
pointerEvents: "none"
},
rightShadow: {
position: "absolute",
top: 0,
right: 0,
width: "16px",
height: "100%",
background: "linear-gradient(to left, rgba(32, 41, 56, 0.9), rgba(32, 41, 56, 0))",
zIndex: 2,
pointerEvents: "none"
},
topShadow: {
position: "absolute",
top: 0,
left: 0,
right: 0,
height: "12px",
background: "linear-gradient(to bottom, rgba(45, 55, 72, 0.9), rgba(45, 55, 72, 0))",
zIndex: 2,
pointerEvents: "none"
},
bottomShadow: {
position: "absolute",
bottom: 0,
left: 0,
right: 0,
height: "12px",
background: "linear-gradient(to top, rgba(45, 55, 72, 0.9), rgba(45, 55, 72, 0))",
zIndex: 2,
pointerEvents: "none"
}
};
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(HeadlessComponentPalette_default, {
onSelectComponent,
onDragComponent,
className,
style: rootStyle,
styles: defaultStyles
});
};
var ComponentPalette_default = ComponentPalette;
//#endregion
//#region src/ui/headless/HeadlessCircuitToolbar.tsx
const HeadlessCircuitToolbar = ({ onAction, hasSelection = false, canUndo = false, canRedo = false, showGrid = true, validationIssues = 0, className = "", classNames = {}, style = {}, styles = {} }) => {
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
className: `cb-circuit-toolbar ${className}`,
style,
"data-testid": "circuit-toolbar",
children: [
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
className: `