# ComfyUI Custom Node Development Guidelines

## Your workflow (important)

Pleasee ensure you always add test coverage, are making *incremental* commits that are concicse
Ensure you are running `pdm ci` very often after commits are made to ensure tests are passing and lint errors dont exist.
--> `pdm ci` will also format your code.

Use SOLID principles when coding: 

Agile + SOLID Principles for Python

Rule:
When writing or refactoring Python code, follow Agile values (working, maintainable, adaptable code over excessive documentation) and the SOLID design principles below. Favor small, testable changes, frequent iteration, and code that can be easily extended without breaking existing functionality.

⸻

S — Single Responsibility Principle

A class, function, or module should have one reason to change.
	•	Keep classes and functions focused on a single concern.
	•	Avoid “god objects” that do everything.
	•	Use small, focused modules (user_service.py, logger.py).

⸻

O — Open/Closed Principle

Code should be open for extension but closed for modification.
	•	Use duck typing to pass in new objects that meet the expected interface.
	•	Extend via composition, decorators, or plugins instead of modifying existing logic.

class PaymentProcessor:
    def __init__(self, payment_method):
        self.payment_method = payment_method

    def pay(self, amount):
        self.payment_method.pay(amount)


⸻

L — Liskov Substitution Principle

Subtypes must be usable wherever their base types are expected.
	•	Python won’t enforce inheritance rules — ensure consistent behavior via tests.
	•	Duck typing often replaces explicit inheritance, but don’t surprise the caller.

⸻

I — Interface Segregation Principle

Prefer small, specific interfaces over large, general-purpose ones.
	•	Use protocols (typing.Protocol) or ABCs (abc module) for expected behavior.
	•	Avoid forcing classes to implement unused methods.

from typing import Protocol

class Notifier(Protocol):
    def send(self, msg: str) -> None: ...


⸻

D — Dependency Inversion Principle

Depend on abstractions, not concrete implementations.
	•	Pass dependencies via constructors or parameters.
	•	Use protocols or base classes to define abstractions.
	•	Makes testing easy — inject mocks or fakes.

## 🏗️ Architecture Patterns

### Custom Node Structure
- **Python Backend (`nodes/*.py`)**: Handle all business logic, parsing, and data processing
- **JavaScript Frontend (`js/*.js`)**: Handle UI rendering, user interactions, and visual presentation
- **`__init__.py`**: Register nodes with `NODE_CLASS_MAPPINGS`, `NODE_DISPLAY_NAME_MAPPINGS`, and `WEB_DIRECTORY`
- **Clear separation**: Backend sends structured data via WebSocket, frontend receives and renders

### ComfyUI Node Class Pattern
```python
class CustomNode:
    CATEGORY = "category_name"
    DESCRIPTION = """Multi-line description with features"""
    
    @classmethod
    def INPUT_TYPES(cls):
        return {"required": {"input": ("STRING", {"tooltip": "..."})}}
    
    RETURN_TYPES = ("STRING",)
    RETURN_NAMES = ("output_name",)
    OUTPUT_TOOLTIPS = ("Description of output",)
    FUNCTION = "main_function"
    OUTPUT_NODE = True  # If node displays results
```

## 🔄 Data Flow Architecture

### Backend → Frontend Communication
- Use `PromptServer.instance.send_sync(event_name, data)` for real-time updates
- Send structured data with `node_id` for targeting specific node instances
- Handle errors gracefully with try-catch and fallback behavior

### WebSocket Message Pattern
```javascript
// Backend
PromptServer.instance.send_sync("custom_event", {
    "node_id": str(id(self)),
    "data": structured_data
})

// Frontend
api.addEventListener("custom_event", (event) => {
    const { node_id, data } = event.detail;
    // Update specific node or all nodes of type
});
```

## 🎨 JavaScript Extension Architecture

### Function Hoisting for Organization
- Use function hoisting to organize code in logical sections
- Group functions by purpose: utilities, data processing, drawing, event handling
- Keep main registration at bottom for clarity

### Section Organization Pattern
```javascript
// ============================================================================
// UTILITY FUNCTIONS (hoisted)
// ============================================================================
function utilityFunction() { }

// ============================================================================
// DATA PROCESSING
// ============================================================================
function processData() { }

// ============================================================================
// DRAWING FUNCTIONS  
// ============================================================================
function drawElements() { }

// ============================================================================
// EVENT HANDLING
// ============================================================================
function handleEvents() { }

// ============================================================================
// MAIN EXTENSION REGISTRATION
// ============================================================================
app.registerExtension({ ... });
```

### Canvas Drawing Best Practices
- Calculate dimensions responsively based on node size
- Use percentage-based sizing for scalability
- Implement boundary checking to prevent overflow
- Cache expensive operations (image loading, metadata processing)

### Coordinate System Handling
```javascript
function transformCanvasCoordinates(e, node) {
    const rect = app.canvas.canvas.getBoundingClientRect();
    const x = e.clientX - rect.left;
    const y = e.clientY - rect.top;
    
    const canvasX = x / app.canvas.ds.scale - app.canvas.ds.offset[0];
    const canvasY = y / app.canvas.ds.scale - app.canvas.ds.offset[1];
    
    return {
        x: canvasX - node.pos[0],
        y: canvasY - node.pos[1]
    };
}
```

## 🧪 Testing Strategy

### Python Unit Testing
- Mock ComfyUI dependencies (`folder_paths`, `server`, `aiohttp`) 
- Test business logic in isolation from ComfyUI environment
- Use `pytest.ini` and `conftest.py` for test configuration
- Focus on edge cases and complex parsing logic

### Test Structure Pattern
```python
# conftest.py - Mock dependencies
sys.modules['folder_paths'] = Mock()
sys.modules['server'] = Mock()

# Test complex scenarios
def test_complex_parsing(self):
    result = self.node.parse_complex_input("edge case")
    self.assertEqual(expected, result)
```

## 🔧 Parsing & Validation

### Regex Pattern Consistency
- Use identical parsing logic for similar tag types
- Handle complex names with spaces, colons, special characters
- Use `rfind(':')` for robust strength extraction

### Robust Parsing Pattern
```python
def parse_tags(self, text: str) -> List[Dict]:
    pattern = r'<tag_type:(.+?)>'
    results = []
    for match in re.finditer(pattern, text):
        content = match.group(1).strip()
        last_colon_index = content.rfind(':')
        if last_colon_index > 0:
            name = content[:last_colon_index].strip()
            strength = content[last_colon_index + 1:].strip()
            results.append({
                'name': name, 
                'strength': strength,
                'type': 'tag_type',
                'tag': match.group(0)
            })
    return results
```

## 🎯 User Experience Patterns

### Responsive UI Elements
- Thumbnail size should scale with node size (percentage-based)
- Implement hover states with proper enter/leave handling
- Use timeouts and state flags to prevent UI flickering
- Provide visual feedback for all interactive elements

### Error Handling & Graceful Degradation
- Display meaningful error states when metadata is missing
- Provide fallback content when external resources fail
- Log errors appropriately (use `console.debug` for verbose logging)
- Never crash the UI due to missing data

### Accessibility & Usability
- Add tooltips and descriptions for all inputs/outputs
- Use consistent color coding and visual hierarchy
- Implement copy-to-clipboard functionality for user convenience
- Support manual node resizing with auto-size fallback

## 📁 File Organization

### Directory Structure
```
custom_nodes/your-node/
├── __init__.py              # Node registration
├── nodes/                   # Python business logic
│   ├── __init__.py
│   └── your_node.py
├── js/                      # JavaScript UI extensions  
│   └── your_extension.js
├── tests/                   # Unit tests
│   └── test_your_node.py
├── conftest.py             # Test configuration
├── pytest.ini             # Test settings
├── requirements.txt        # Dependencies
├── run_tests.sh           # Test runner
└── README.md              # Documentation
```

## 🚀 Performance Optimization

### Memory Management
- Clean up event listeners and timeouts
- Remove DOM elements when not needed
- Use efficient data structures for large datasets
- Profile memory usage in development

## 📝 Code Quality Standards

## Write tests
* Use ./run_tests.sh to run tests
* Write tests, and keep your test setup DRY.
* Right now there are no frontend tests, but there are backend tests for in tests/

### Naming Conventions
- Use descriptive function and variable names
- Follow Python PEP 8 and JavaScript standard conventions
- Use consistent naming across backend/frontend boundaries
- Document complex algorithms with inline comments

### Documentation Requirements
- Comprehensive docstrings for all Python classes/functions
- Inline comments for complex JavaScript functions  
- README with installation, usage, and examples
- Code comments explaining non-obvious logic

### Error Prevention
- Type hints in Python where applicable
- Input validation at API boundaries
- Defensive programming for external data
- Comprehensive test coverage for edge cases

### Code Reuse & Duplication Prevention
- **ALWAYS search existing codebase** before implementing new functionality
- **Reuse existing working patterns** especially for common operations (clipboard, WebSocket handling, UI components)
- **Grep for similar functionality** using keywords like "clipboard", "copy", "navigator.clipboard" before creating new implementations
- **Avoid reimplementing proven code** - if there's already working clipboard/fetch/validation logic, extend or reuse it
- **When adding new features**, check if similar UI patterns already exist (buttons, modals, feedback systems)

## 🔐 Security Considerations

### Input Sanitization
- Validate all user inputs before processing
- Sanitize data before rendering in UI
- Avoid eval() or similar dynamic code execution
- Use parameterized queries for any database operations

### File System Safety
- Validate file paths and extensions
- Use ComfyUI's folder_paths for safe directory access
- Never expose internal file system structure
- Implement proper access controls for sensitive operations