- Added the "Model Switch" node
- Updated README
- Cleaned up the CHANGELOG to meet standard
This commit is contained in:
Alex Furer
2025-12-15 13:32:22 +01:00
parent bdbc55264b
commit 9f5abfb8b5
7 changed files with 592 additions and 35 deletions
+3 -2
View File
@@ -88,8 +88,9 @@ yarn-error.log*
# AF speciffic
Status.txt
*.docx
*.psd
*.docx
helpers/
nodes.zip
node.zip
Docs/screenshots/
Docs/screenshots/
+16 -17
View File
@@ -1,43 +1,44 @@
# Changelog
## [1.0.9] - 2025-11-11
### 1.1.0
- Added the "Model Switch" node
- Updated README
- Cleaned up the CHANGELOG to meet standard
### v1.0.9
- Added "Show Text" node documentation
- Added a video about the "Edit Generated Prompt" node
- Updated the workflow example to use my own "Show Text" node
## [1.0.8] - 2025-11-10
### v1.0.8
- Fixed an error on the "Edit Generated Prompt" node ( invalid prompt: {'type': 'prompt_no_outputs', 'message': 'Prompt has no outputs', 'details': '', 'extra_info': {}} )
- Added a "Show Text" node, so folks don't have to install any other custom nodes to run the example workflow (Yeah ComfyUI does NOT have a simple show text node...)
## [1.0.7] - 2025-11-05
### v1.0.7
- Changed Icon location to my own website as the ComfyUI Registry botched out on any GitHub URL I tried...
## [1.0.6] - 2025-10-30
### v1.0.6
- Minor changes for registry compliance
## [1.0.5] - 2025-10-30
### Changed
### v1.0.5
- Added screenshots
- Added workflow examples
- Updated README
## [1.0.4] - 2025-10-30
### Changed
### v1.0.4
- Save Prompt History now only saves, when the prompt changed, eliminating duplicate prompts in the archive.
- Added screenshots
- Added workflow examples
- Updated README
## [1.0.3] - 2025-10-29
### Changed
### v1.0.3
- Fixed links in nodes.py to git
- Added CHANGELOG.md
- Simplified __init__.py
- Added package.json for ComfyUI Manager compliance
- Added pyproject.toml for registry compliance
## [1.0.2] - 2025-10-29
### Changed
### v1.0.2
- Changed "project" to "filename" in load prompt node
- Changed display on info node
- Changed console output to include ID number
@@ -45,11 +46,9 @@
- Fixed Versioning text
- Made print out in console less prominent
## [1.0.1] - 2025-10-22
### Added
### v1.0.1
- Added "project" back in as a tag that gets stored in the json
## [1.0.0] - 2025-10-20
### Major Changes
### v1.0.0
- Merged AF Edit Generated Prompt and AF Prompt History Nodes into single pack
- Complete overhauled Load Prompt node
- Complete overhauled Load Prompt node
+96 -8
View File
@@ -7,7 +7,8 @@ A lightweight suite of ComfyUI custom nodes for AI prompt management and history
**[AF - Edit Generated Prompt](#af---edit-generated-prompt)** - Receive LLM-generated prompts and optionally edit them manually
**[AF - Save Prompt History](#af---save-prompt-history)** - Archive prompts to JSON files with timestamps
**[AF - Load Prompt History](#af---load-prompt-history)** - Browse and load previous prompts by index
**[AF - Show Text](#af---show-text)** - SImple node that displays the connected text
**[AF - Show Text](#af---show-text)** - Simple node that displays the connected text
**[AF - Model Switch](#af---model-switch)** - A dynamic model/clip/vae switching node
---
@@ -204,6 +205,18 @@ AF Load - ✓ Successfully loaded: ID: 126 | 2025-10-29 14:32:15
---
## 💡 Pro Tips
- **Index 0 is your friend** - Always points to newest prompt
- **Info output shows everything** - See exactly what you loaded
- **Console logging** - Check ID and timestamp confirmation
- **Multiple load nodes** - Each can access different files/indices
- **Workflows persist** - Your selections save with the workflow
- **Project field** - Use it as a mental note when saving, great for manual inspection
---
### AF - Show Text
A very simple node that shows the text connected to the input of the Node
@@ -239,14 +252,89 @@ A very simple node that shows the text connected to the input of the Node
---
## 💡 Pro Tips
## AF - Model Switch
- **Index 0 is your friend** - Always points to newest prompt
- **Info output shows everything** - See exactly what you loaded
- **Console logging** - Check ID and timestamp confirmation
- **Multiple load nodes** - Each can access different files/indices
- **Workflows persist** - Your selections save with the workflow
- **Project field** - Use it as a mental note when saving, great for manual inspection
A dynamic model switching node that allows you to toggle between multiple model/CLIP/VAE combinations without rewiring your workflow.
### Features
- **Dynamic Rails**: Add 2-10 model rails on demand
- **Smart AUTO Mode**: Automatically selects the first connected rail
- **1-Based Indexing**: Intuitive numbering (Rail 1, Rail 2, Rail 3...)
- **Permanent Base Rails**: Rails 1-2 never lose connections when adjusting rail count
- **Real-Time Status**: Color-coded display shows active rail instantly
- **Flexible Sizing**: Resizable node that preserves your layout preferences
### How It Works
The Model Switch acts as a gateway for your model pipeline. Instead of maintaining multiple workflow branches or manually reconnecting loaders, you can:
1. Connect multiple checkpoint loaders (or AIO loaders) to different rails
2. Choose which rail to use via the `selected_rail` parameter
3. The node outputs the MODEL, CLIP, and VAE from your selected rail
### Parameters
| Parameter | Type | Range | Description |
|-----------|------|-------|-------------|
| `number_of_rails` | INT | 2-10 | Number of visible model rails |
| `selected_rail` | INT | 0-10 | Active rail: `0` = AUTO (first connected), `1-10` = manual selection |
| `status` | STRING | - | Read-only display showing current active rail |
### Status Display Colors
- 🟠 **Orange**: AUTO mode - automatically using first connected rail
- 🔵 **Blue**: Manual mode - specific rail selected
- 🔴 **Red**: Error - no models connected
### Usage Example
**Basic Setup:**
```
Checkpoint Loader A → model_1, clip_1, vae_1
Checkpoint Loader B → model_2, clip_2, vae_2
↓
AF Model Switch
(selected_rail: 0)
↓
→ model, clip, vae
```
**A/B Testing Workflow:**
1. Set `number_of_rails: 2`
2. Connect two different models to Rails 1 and 2
3. Set `selected_rail: 1` to use first model, `2` for second model
4. Set `selected_rail: 0` for AUTO mode (uses first connected)
**Advanced: Quality vs Speed:**
1. Set `number_of_rails: 3`
2. Rail 1: High-quality slow model
3. Rail 2: Balanced model
4. Rail 3: Fast draft model
5. Switch between them by changing `selected_rail`
### Tips
- **Rails 1-2 are permanent**: These connections are never lost when you adjust `number_of_rails`
- **Rails 3-10 are dynamic**: These are added/removed as needed
- **Use with rgthree Group Bypasser**: Bypass entire model loading groups to save memory - only the selected rail's loaders will execute
- **AUTO mode is smart**: It will find the first rail with actual data, skipping any bypassed/muted nodes
- **Status updates instantly**: No need to run the workflow to see which rail is selected
### Integration with Other Nodes
**Works great with:**
- `rgthree Context Switch` - for full pipeline switching
- `rgthree Group Bypasser` - to disable unused model loaders
- `AF - Save Prompt History` - save which model was used with each prompt
- Any checkpoint loader or AIO loader node
### Technical Notes
- Input names are 1-based: `model_1`, `clip_1`, `vae_1`, etc.
- Internally consistent: no confusing 0-based conversions
- Minimum node width: 160px
- Height auto-adjusts based on number of rails
---
+126 -4
View File
@@ -475,21 +475,143 @@ class AF_Show_Text:
"ui": {"text": [str(text)]},
"result": (text,)
}
# =============================================================================
# Node Mappings
# AF - Model Switch Node (1-based Rails, 0=AUTO)
# =============================================================================
class AF_Model_Switch:
"""
Switch between model configurations with dynamic input visibility.
Uses 1-based indexing: Rail 1, Rail 2, Rail 3, etc.
Rails 1 and 2 are permanent (connections never lost).
Rails 3-10 are dynamically added/removed.
Selected rail: 0 = AUTO (first connected), 1-10 = manual selection
"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"number_of_rails": ("INT", {
"default": 2,
"min": 2,
"max": 10,
"step": 1,
"display": "number",
"tooltip": "Number of visible rails (2-10)"
}),
"selected_rail": ("INT", {
"default": 0,
"min": 0,
"max": 10,
"step": 1,
"display": "number",
"tooltip": "Active rail: 0=AUTO (first connected), 1-10=manual selection"
}),
"status": ("STRING", {
"default": "💡 Active Rail: auto",
"multiline": True,
"dynamicPrompts": False
}),
},
"optional": {
# Rail 1 and Rail 2 are PERMANENT - never removed
"model_1": ("MODEL",),
"clip_1": ("CLIP",),
"vae_1": ("VAE",),
"model_2": ("MODEL",),
"clip_2": ("CLIP",),
"vae_2": ("VAE",),
},
"hidden": {
"unique_id": "UNIQUE_ID",
"extra_pnginfo": "EXTRA_PNGINFO",
}
}
RETURN_TYPES = ("MODEL", "CLIP", "VAE")
RETURN_NAMES = ("model", "clip", "vae")
FUNCTION = "switch_model"
CATEGORY = "AF - Nodes/AF - Prompt Pack"
OUTPUT_NODE = True
def switch_model(self, number_of_rails, selected_rail, status="", unique_id=None, extra_pnginfo=None, **kwargs):
# Clamp values to valid ranges
number_of_rails = max(2, min(10, number_of_rails))
selected_rail = max(0, min(number_of_rails, selected_rail))
# Determine actual active rail (1-based throughout!)
actual_rail = None
is_auto_mode = (selected_rail == 0)
if is_auto_mode:
# AUTO mode - find first rail with data
for i in range(1, number_of_rails + 1): # Rails 1 to number_of_rails
test_model = kwargs.get(f"model_{i}", None)
if test_model is not None:
actual_rail = i
print(f"AF Model Switch - AUTO → Rail {actual_rail}")
break
if actual_rail is None:
# No rails have data, default to rail 1
actual_rail = 1
print(f"AF Model Switch - AUTO → No data found, defaulting to Rail 1")
status_text = f"💡 Active Rail: auto → {actual_rail}"
else:
# Manual mode - user selected specific rail (1-based)
actual_rail = selected_rail
status_text = f"🎯 Active Rail: {actual_rail}"
print(f"AF Model Switch - Manual → Rail {actual_rail}")
# Get selected components using 1-based rail number directly
model = kwargs.get(f"model_{actual_rail}", None)
clip = kwargs.get(f"clip_{actual_rail}", None)
vae = kwargs.get(f"vae_{actual_rail}", None)
# Final fallback if still no model
if model is None:
for i in range(1, number_of_rails + 1):
test_model = kwargs.get(f"model_{i}", None)
if test_model is not None:
model = test_model
clip = kwargs.get(f"clip_{i}", None)
vae = kwargs.get(f"vae_{i}", None)
print(f"AF Model Switch - Fallback to rail {i}")
break
if model is not None:
print(f"AF Model Switch - ✓ Outputting Rail {actual_rail} (of 1-{number_of_rails})")
else:
print(f"AF Model Switch - ⚠️ No models connected")
status_text = "⚠️ Active Rail: none (no connections)"
return {
"ui": {"status": [status_text]},
"result": (model, clip, vae)
}
@classmethod
def IS_CHANGED(cls, number_of_rails, selected_rail, **kwargs):
# Force re-evaluation when inputs change
return f"{number_of_rails}-{selected_rail}"
# Update NODE_CLASS_MAPPINGS
NODE_CLASS_MAPPINGS = {
"AF_Edit_Generated_Prompt": AF_Edit_Generated_Prompt,
"AF_Save_Prompt_History": AF_Save_Prompt_History,
"AF_Load_Prompt_History": AF_Load_Prompt_History,
"AF_Show_Text": AF_Show_Text,
"AF_Model_Switch": AF_Model_Switch,
}
NODE_DISPLAY_NAME_MAPPINGS = {
"AF_Edit_Generated_Prompt": "AF - Edit Generated Prompt",
"AF_Save_Prompt_History": "AF - Save Prompt History",
"AF_Save_Prompt_History": "AF - Save Prompt History",
"AF_Load_Prompt_History": "AF - Load Prompt History",
"AF_Show_Text": "AF - Show Text",
}
"AF_Model_Switch": "AF - Model Switch",
}
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "comfyui-af-pack-prompt-nodes",
"version": "1.0.9",
"version": "1.1.0",
"description": "A lightweight suite of ComfyUI custom nodes for AI prompt management and history tracking.",
"author": "Alex Furer",
"license": "MIT",
@@ -9,4 +9,4 @@
"url": "https://github.com/alFrame/ComfyUI-AF-Pack-Prompt-Nodes.git"
},
"nodepath": "custom_nodes/ComfyUI-AF-Pack-Prompt-Nodes"
}
}
+2 -2
View File
@@ -1,7 +1,7 @@
[project]
name = "comfyui-af-pack-prompt-nodes"
description = "A lightweight suite of ComfyUI custom nodes for AI prompt management and history tracking."
version = "1.0.9"
version = "1.1.0"
license = {file = "LICENSE"}
dependencies = []
@@ -11,4 +11,4 @@ Repository = "https://github.com/alFrame/ComfyUI-AF-Pack-Prompt-Nodes"
[tool.comfy]
PublisherId = "alframe"
DisplayName = "ComfyUI AF - Pack-Prompt-Nodes"
Icon = "https://fullframestudios.ch/ComfyUI-Icon/icon.png"
Icon = "https://fullframestudios.ch/ComfyUI-Icon/icon.png"
+347
View File
@@ -0,0 +1,347 @@
import { app } from "/scripts/app.js";
app.registerExtension({
name: "AF.ModelSwitch",
async beforeRegisterNodeDef(nodeType, nodeData) {
if (nodeData.name === "AF_Model_Switch") {
// Hook into onExecuted to update status after workflow runs
const originalOnExecuted = nodeType.prototype.onExecuted;
nodeType.prototype.onExecuted = function(message) {
const r = originalOnExecuted?.apply(this, arguments);
if (message?.status && Array.isArray(message.status) && message.status.length > 0) {
const statusText = message.status[0];
const statusWidget = this.widgets?.find(w => w.name === "status");
if (statusWidget) {
statusWidget.value = statusText;
if (statusWidget.inputEl) {
statusWidget.inputEl.value = statusText;
if (statusText.includes("auto")) {
statusWidget.inputEl.style.backgroundColor = "#2a2a1a";
statusWidget.inputEl.style.color = "#ffaa44";
statusWidget.inputEl.style.borderColor = "#cc8833";
} else if (statusText.includes("none")) {
statusWidget.inputEl.style.backgroundColor = "#2a1a1a";
statusWidget.inputEl.style.color = "#ff6666";
statusWidget.inputEl.style.borderColor = "#cc4444";
} else {
statusWidget.inputEl.style.backgroundColor = "#1a2a3a";
statusWidget.inputEl.style.color = "#88ccff";
statusWidget.inputEl.style.borderColor = "#4488cc";
}
}
this.setDirtyCanvas(true, false);
}
}
return r;
};
// Store original onNodeCreated
const originalOnNodeCreated = nodeType.prototype.onNodeCreated;
nodeType.prototype.onNodeCreated = function() {
const result = originalOnNodeCreated?.apply(this, arguments);
// Rails 1 and 2 are PERMANENT (defined in Python INPUT_TYPES)
// So we start counting from rail 2 as the baseline
// _currentRailCount represents the TOTAL number of rails visible
// Detect existing rail count from loaded inputs
// Check for the highest rail number present (rails 3-10 are dynamic)
let detectedRailCount = 2; // Always have at least rails 1 and 2
if (this.inputs) {
for (let i = 10; i >= 3; i--) {
if (this.inputs.find(inp => inp.name === `model_${i}`)) {
detectedRailCount = i;
break;
}
}
}
// Initialize tracking
this._currentRailCount = detectedRailCount;
this._userResized = false;
this._isInitializing = true; // Flag to prevent duplicate initialization
// Setup callbacks with delay to ensure widgets exist
setTimeout(() => {
const railCountWidget = this.widgets?.find(w => w.name === "number_of_rails");
const selectWidget = this.widgets?.find(w => w.name === "selected_rail");
// Rail count callback
if (railCountWidget) {
railCountWidget.callback = (value) => {
const count = Math.max(2, Math.min(10, parseInt(value) || 2));
this.updateRailInputs(count);
};
}
// Selected rail callback
if (selectWidget) {
selectWidget.callback = (value) => {
this.updateStatusDisplay(parseInt(value));
};
}
this.updateStatusDisplay(0);
this._isInitializing = false;
}, 100);
return result;
};
// Update status display (UI only, no execution)
nodeType.prototype.updateStatusDisplay = function(selectedRail) {
const statusWidget = this.widgets?.find(w => w.name === "status");
if (!statusWidget) return;
let statusText, bgColor, textColor, borderColor;
// 0 = AUTO, 1-10 = manual rail selection
if (selectedRail === 0 || isNaN(selectedRail)) {
statusText = "💡 Active Rail: auto";
bgColor = "#2a2a1a";
textColor = "#ffaa44";
borderColor = "#cc8833";
} else {
// Display 1-based rail number
statusText = `🎯 Active Rail: ${selectedRail}`;
bgColor = "#1a2a3a";
textColor = "#88ccff";
borderColor = "#4488cc";
}
statusWidget.value = statusText;
if (statusWidget.inputEl) {
statusWidget.inputEl.value = statusText;
statusWidget.inputEl.style.backgroundColor = bgColor;
statusWidget.inputEl.style.color = textColor;
statusWidget.inputEl.style.borderColor = borderColor;
}
this.setDirtyCanvas(true, false);
};
// Update rail inputs dynamically
nodeType.prototype.updateRailInputs = function(railCount) {
railCount = Math.max(2, Math.min(10, railCount));
if (this._currentRailCount === railCount) return;
const oldCount = this._currentRailCount;
const newCount = railCount;
console.log(`AF Model Switch - Updating rails from ${oldCount} to ${newCount}`);
if (newCount > oldCount) {
// Adding rails: model_3, model_4, etc. (1-based!)
// Rails 1 and 2 are permanent, so we only add 3+
for (let i = oldCount + 1; i <= newCount; i++) {
// Check if this input already exists before adding
const modelExists = this.inputs.find(inp => inp.name === `model_${i}`);
const clipExists = this.inputs.find(inp => inp.name === `clip_${i}`);
const vaeExists = this.inputs.find(inp => inp.name === `vae_${i}`);
if (!modelExists) {
console.log(`AF Model Switch - Adding model_${i}`);
this.addInput(`model_${i}`, "MODEL");
}
if (!clipExists) {
console.log(`AF Model Switch - Adding clip_${i}`);
this.addInput(`clip_${i}`, "CLIP");
}
if (!vaeExists) {
console.log(`AF Model Switch - Adding vae_${i}`);
this.addInput(`vae_${i}`, "VAE");
}
}
} else {
// Removing rails from the end
// NEVER remove rails 1 or 2 (they're permanent)
for (let i = oldCount; i > newCount && i > 2; i--) {
console.log(`AF Model Switch - Removing rail ${i}`);
for (let j = this.inputs.length - 1; j >= 0; j--) {
const input = this.inputs[j];
if (input.name === `vae_${i}` ||
input.name === `clip_${i}` ||
input.name === `model_${i}`) {
this.removeInput(j);
}
}
}
}
this._currentRailCount = newCount;
// Update select widget: 0=AUTO, 1 to newCount for rails
const selectWidget = this.widgets?.find(w => w.name === "selected_rail");
if (selectWidget) {
const maxRail = newCount;
selectWidget.options = selectWidget.options || {};
selectWidget.options.min = 0;
selectWidget.options.max = maxRail;
// Clamp current selection to valid range
const currentValue = parseInt(selectWidget.value);
if (currentValue > maxRail) {
selectWidget.value = maxRail;
this.updateStatusDisplay(maxRail);
}
// Update the input element's attributes
if (selectWidget.inputEl) {
selectWidget.inputEl.max = maxRail;
selectWidget.inputEl.min = 0;
}
}
// Only adjust height, preserve width
const widgetHeight = 100;
const inputHeight = 20;
const totalInputs = this._currentRailCount * 3;
const newHeight = widgetHeight + (totalInputs * inputHeight);
// Preserve current width, only update height
if (this.size) {
this.size[1] = newHeight;
} else {
this.size = [160, newHeight];
}
this.setDirtyCanvas(true, true);
};
// Override computeSize - minimal intervention, let LiteGraph work
const originalComputeSize = nodeType.prototype.computeSize;
nodeType.prototype.computeSize = function(out) {
// If user hasn't resized, calculate proper height
if (!this._userResized) {
let height = 200;
if (this._currentRailCount) {
const widgetHeight = 100;
const inputHeight = 20;
const totalInputs = this._currentRailCount * 3;
height = widgetHeight + (totalInputs * inputHeight);
}
const size = [160, height];
if (out) {
out[0] = size[0];
out[1] = size[1];
return out;
}
return size;
}
// User has resized - let LiteGraph handle it normally
return originalComputeSize?.apply(this, arguments);
};
// Track when user manually resizes
const originalOnResize = nodeType.prototype.onResize;
nodeType.prototype.onResize = function(size) {
this._userResized = true;
return originalOnResize?.apply(this, arguments);
};
}
},
async nodeCreated(node) {
if (node.comfyClass === "AF_Model_Switch") {
setTimeout(() => {
// Style rail count widget
const railCountWidget = node.widgets?.find(w => w.name === "number_of_rails");
if (railCountWidget?.inputEl) {
railCountWidget.inputEl.style.border = "1px solid #4488cc";
railCountWidget.inputEl.style.backgroundColor = "#1a2a3a";
railCountWidget.inputEl.style.padding = "4px";
railCountWidget.inputEl.style.borderRadius = "3px";
}
// Style selected rail widget
const selectWidget = node.widgets?.find(w => w.name === "selected_rail");
if (selectWidget?.inputEl) {
selectWidget.inputEl.style.border = "1px solid #88cc44";
selectWidget.inputEl.style.backgroundColor = "#1a3a2a";
selectWidget.inputEl.style.padding = "4px";
selectWidget.inputEl.style.borderRadius = "3px";
selectWidget.inputEl.style.fontWeight = "bold";
}
// Style status widget
const statusWidget = node.widgets?.find(w => w.name === "status");
if (statusWidget?.inputEl) {
statusWidget.inputEl.readOnly = true;
statusWidget.inputEl.style.fontFamily = "monospace";
statusWidget.inputEl.style.fontSize = "13px";
statusWidget.inputEl.style.fontWeight = "bold";
statusWidget.inputEl.style.cursor = "default";
statusWidget.inputEl.style.padding = "6px 8px";
statusWidget.inputEl.style.textAlign = "center";
statusWidget.inputEl.style.userSelect = "none";
statusWidget.inputEl.style.resize = "none";
statusWidget.inputEl.style.minHeight = "32px";
statusWidget.inputEl.style.maxHeight = "32px";
statusWidget.inputEl.style.height = "32px";
statusWidget.inputEl.style.overflow = "hidden";
statusWidget.inputEl.style.width = "100%";
statusWidget.inputEl.style.boxSizing = "border-box";
// Set initial AUTO color (orange)
statusWidget.inputEl.style.backgroundColor = "#2a2a1a";
statusWidget.inputEl.style.color = "#ffaa44";
statusWidget.inputEl.style.border = "1px solid #cc8833";
}
}, 200);
}
},
async loadedGraphNode(node) {
if (node.comfyClass === "AF_Model_Switch") {
setTimeout(() => {
// Mark as user-resized if node has custom size stored
if (node.size && node.size[0] !== 160) {
node._userResized = true;
}
// Detect current rail count from actual inputs
let actualRailCount = 2; // Minimum (rails 1 and 2 are permanent)
if (node.inputs) {
for (let i = 10; i >= 3; i--) {
if (node.inputs.find(inp => inp.name === `model_${i}`)) {
actualRailCount = i;
break;
}
}
}
// Update _currentRailCount to match actual state
node._currentRailCount = actualRailCount;
const railCountWidget = node.widgets?.find(w => w.name === "number_of_rails");
if (railCountWidget) {
const widgetValue = parseInt(railCountWidget.value) || 2;
console.log(`AF Model Switch - Loaded: widget=${widgetValue}, actual inputs=${actualRailCount}`);
// Sync: if widget and actual don't match, update to match widget
if (widgetValue !== actualRailCount && node.updateRailInputs) {
console.log(`AF Model Switch - Syncing rails to ${widgetValue}`);
node.updateRailInputs(widgetValue);
}
}
}, 100);
}
}
});