From 97f8f6cd526014eaefb9d75f63caf33a0539305e Mon Sep 17 00:00:00 2001 From: David Montero Crespo Date: Mon, 6 Apr 2026 23:44:24 -0300 Subject: [PATCH] feat: add protocol selection and editable properties in component dialogs --- frontend/public/components-metadata.json | 16 +- frontend/src/components/DynamicComponent.tsx | 20 ++ .../simulator/ComponentPropertyDialog.css | 50 +++++ .../simulator/ComponentPropertyDialog.tsx | 32 +++ .../components/simulator/SimulatorCanvas.tsx | 8 + .../src/simulation/parts/ProtocolParts.ts | 206 ++++++++++++------ wokwi-libs/qemu-lcgamboa | 2 +- 7 files changed, 264 insertions(+), 70 deletions(-) diff --git a/frontend/public/components-metadata.json b/frontend/public/components-metadata.json index 95a14af2..392c6fd0 100644 --- a/frontend/public/components-metadata.json +++ b/frontend/public/components-metadata.json @@ -247,9 +247,17 @@ "name": "imageData", "type": "string", "control": "text" + }, + { + "name": "protocol", + "type": "select", + "control": "select", + "options": ["i2c", "spi"], + "defaultValue": "i2c", + "description": "Communication protocol" } ], - "defaultValues": {}, + "defaultValues": { "protocol": "i2c" }, "pinCount": 0, "tags": [ "ssd1306" @@ -1324,9 +1332,11 @@ }, { "name": "color", - "type": "string", + "type": "select", "defaultValue": "red", - "control": "text" + "control": "select", + "options": ["red", "green", "blue", "yellow", "orange", "white", "purple"], + "description": "LED color" }, { "name": "lightColor", diff --git a/frontend/src/components/DynamicComponent.tsx b/frontend/src/components/DynamicComponent.tsx index 209583a8..f77cf33e 100644 --- a/frontend/src/components/DynamicComponent.tsx +++ b/frontend/src/components/DynamicComponent.tsx @@ -266,11 +266,31 @@ export const DynamicComponent: React.FC = ({ marginTop: '4px', color: '#666', pointerEvents: 'none', + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + gap: '4px', }} > {properties.pin !== undefined ? `Pin ${properties.pin}` : metadata.name} + {properties.protocol && ( + + {String(properties.protocol)} + + )} ); diff --git a/frontend/src/components/simulator/ComponentPropertyDialog.css b/frontend/src/components/simulator/ComponentPropertyDialog.css index 83ff4d04..806d492f 100644 --- a/frontend/src/components/simulator/ComponentPropertyDialog.css +++ b/frontend/src/components/simulator/ComponentPropertyDialog.css @@ -105,6 +105,56 @@ font-weight: 600; } +.property-edit-section { + margin-bottom: 12px; + border-top: 1px solid #444; + padding-top: 10px; +} + +.property-edit-row { + display: flex; + align-items: center; + justify-content: space-between; + gap: 8px; + margin-bottom: 6px; +} + +.property-edit-label { + font-size: 12px; + color: #aaa; + font-weight: 500; + white-space: nowrap; +} + +.property-edit-select { + flex: 1; + max-width: 100px; + padding: 4px 8px; + font-size: 12px; + font-weight: 600; + color: #fff; + background-color: #3d3d3d; + border: 1px solid #555; + border-radius: 4px; + cursor: pointer; + outline: none; + transition: border-color 0.2s; +} + +.property-edit-select:hover { + border-color: #007acc; +} + +.property-edit-select:focus { + border-color: #007acc; + box-shadow: 0 0 0 2px rgba(0, 122, 204, 0.25); +} + +.property-edit-select option { + background-color: #2d2d2d; + color: #fff; +} + .property-actions { display: flex; gap: 8px; diff --git a/frontend/src/components/simulator/ComponentPropertyDialog.tsx b/frontend/src/components/simulator/ComponentPropertyDialog.tsx index b5678731..d63f2a5b 100644 --- a/frontend/src/components/simulator/ComponentPropertyDialog.tsx +++ b/frontend/src/components/simulator/ComponentPropertyDialog.tsx @@ -18,6 +18,7 @@ interface ComponentPropertyDialogProps { onClose: () => void; onRotate: (componentId: string) => void; onDelete: (componentId: string) => void; + onPropertyChange?: (componentId: string, propertyName: string, value: unknown) => void; } export const ComponentPropertyDialog: React.FC = ({ @@ -29,6 +30,7 @@ export const ComponentPropertyDialog: React.FC = ( onClose, onRotate, onDelete, + onPropertyChange, }) => { const dialogRef = useRef(null); const [dialogPosition, setDialogPosition] = useState({ x: 0, y: 0 }); @@ -142,6 +144,36 @@ export const ComponentPropertyDialog: React.FC = ( )} + {/* Editable Properties (select dropdowns) */} + {componentMetadata.properties + .filter((p: any) => p.control === 'select' && p.options) + .length > 0 && ( +
+ {componentMetadata.properties + .filter((p: any) => p.control === 'select' && p.options) + .map((prop: any) => ( +
+ + +
+ ))} +
+ )} + {/* Action Buttons */}