${labels && Object.keys(labels).length > 0 ? `
diff --git a/public/scripts/components/NodeDetailsComponent.js b/public/scripts/components/NodeDetailsComponent.js
index 484950b..103e912 100644
--- a/public/scripts/components/NodeDetailsComponent.js
+++ b/public/scripts/components/NodeDetailsComponent.js
@@ -99,12 +99,29 @@ class NodeDetailsComponent extends Component {
value="${defaultValue}">`;
}
+ renderBooleanComponent(p, formId, pidx) {
+ const defaultValue = p.default !== undefined ? p.default : false;
+ const checked = defaultValue ? 'checked' : '';
+ return `
+
+
+
`;
+ }
+
// Component map for parameter types
getParameterComponentMap() {
const components = {
select: this.renderSelectComponent,
color: this.renderColorComponent,
numberRange: this.renderNumberRangeComponent,
+ boolean: this.renderBooleanComponent,
text: this.renderTextComponent
};
@@ -119,7 +136,8 @@ class NodeDetailsComponent extends Component {
const typeRules = [
{ condition: () => Array.isArray(p.values) && p.values.length > 1, type: 'select' },
{ condition: () => p.type === 'color', type: 'color' },
- { condition: () => p.type === 'numberRange', type: 'numberRange' }
+ { condition: () => p.type === 'numberRange', type: 'numberRange' },
+ { condition: () => p.type === 'boolean', type: 'boolean' }
];
const matchedRule = typeRules.find(rule => rule.condition());
@@ -616,6 +634,10 @@ class NodeDetailsComponent extends Component {
const rgbDisplay = input.parentElement.querySelector('.color-rgb-display');
value = rgbDisplay ? rgbDisplay.value : this.hexToRgbInt(input.value);
}
+ // For boolean type, convert checkbox checked state to boolean
+ else if (input.dataset.paramType === 'boolean' && input.type === 'checkbox') {
+ value = input.checked;
+ }
return {
name: input.dataset.paramName,
location: input.dataset.paramLocation || 'body',
@@ -626,7 +648,7 @@ class NodeDetailsComponent extends Component {
});
// Required validation
- const missing = params.filter(p => p.required && (!p.value || String(p.value).trim() === ''));
+ const missing = params.filter(p => p.required && (p.type === 'boolean' ? false : (!p.value || String(p.value).trim() === '')));
if (missing.length > 0) {
resultEl.style.display = 'block';
resultEl.innerHTML = `
diff --git a/public/styles/main.css b/public/styles/main.css
index 07cf3a7..f87cf1f 100644
--- a/public/styles/main.css
+++ b/public/styles/main.css
@@ -749,6 +749,7 @@ p {
background: rgba(76, 175, 80, 0.3);
color: var(--accent-success);
border: 1px solid rgba(76, 175, 80, 0.5);
+ border-radius: 50%;
}
.status-offline {
@@ -2423,6 +2424,56 @@ select.param-input:focus {
background-size: 12px 12px;
}
+/* Boolean checkbox styling */
+.boolean-input-container {
+ display: flex;
+ align-items: center;
+ gap: 0.5rem;
+}
+
+.param-input.boolean-checkbox {
+ width: auto;
+ padding: 0;
+ margin: 0;
+ background: transparent;
+ border: 1px solid var(--border-primary);
+ border-radius: 4px;
+ width: 16px;
+ height: 16px;
+ appearance: none;
+ cursor: pointer;
+ position: relative;
+ transition: all 0.2s ease;
+}
+
+.param-input.boolean-checkbox:checked {
+ background: var(--accent-primary);
+ border-color: var(--accent-primary);
+}
+
+.param-input.boolean-checkbox:checked::after {
+ content: '✓';
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ color: white;
+ font-size: 10px;
+ font-weight: bold;
+}
+
+.param-input.boolean-checkbox:focus {
+ border-color: rgba(139, 92, 246, 0.5);
+ box-shadow: 0 0 0 2px rgba(139, 92, 246, 0.15);
+}
+
+.boolean-label {
+ font-size: 0.9rem;
+ color: var(--text-primary);
+ cursor: pointer;
+ user-select: none;
+}
+
.endpoint-params.none {
opacity: 1;
font-size: 0.85rem;