7 Commits
Author SHA1 Message Date
Alex Furer f330f3dd7c v0.0.11
- Changed Icon location to my own website as the ComfyUI Registry botched out on any GitHub URL I tried...
2025-11-05 17:12:40 +01:00
Alex Furer 155665721d v0.0.11
- Changed Icon location to my own website as the ComfyUI Registry botched out on any GitHub URL I tried...
2025-11-05 17:06:09 +01:00
Alex Furer f03a046a93 v0.0.10
- Updated the README (CSS file editing)
2025-11-05 16:36:50 +01:00
Alex Furer 62a2029fe9 v0.0.09
- Added nodes.py and node.js for ComfyUI registry and Manager compliance
- Made sure the repo name change from "ComfyUI-AF-Enhanced_HTML_Note" to "ComfyUI-AF-Enhanced-HTML-Note" is consistent for all links and such
2025-11-05 16:18:41 +01:00
Alex Furer 9fa5d1b3d9 v0.0.08
- Moved the CSS from the JS file to it's own file.
- Added CHANGELOG.md
- Added package.json for ComfyUI Manager compliance
- Added pyproject.toml for registry compliance
- Reworked the README
- Added the "helpers" folder to the .gitignore
- Added an icon to the pyproject.toml
2025-11-05 14:21:04 +01:00
Alex Furer 4b8d36c51c Stop tracking PSD files 2025-10-12 21:39:32 +02:00
Alex Furer 106d1835fa Added more file types to the .gitignore
- Skipping .psd and .docx
2025-10-12 21:37:32 +02:00
33 changed files with 897 additions and 794 deletions
+18 -1
View File
@@ -1,2 +1,19 @@
# Auto detect text files and perform LF normalization # Set default behavior to automatically normalize line endings
* text=auto * text=auto
# Explicitly declare text files you want to always be normalized and converted
*.md text
*.py text
*.js text
*.css text
*.html text
# Declare files that will always have CRLF line endings on checkout
*.bat text eol=crlf
# Denote all files that are truly binary and should not be modified
*.png binary
*.jpg binary
*.gif binary
*.ico binary
*.zip binary
+4 -1
View File
@@ -84,4 +84,7 @@ yarn-debug.log*
yarn-error.log* yarn-error.log*
# AF specific # AF specific
Status.txt Status.txt
*.psd
*.docx
helpers/
+171 -184
View File
@@ -1,184 +1,171 @@
# ****** ComfyUI-AF - Enhanced HTML-NOTE ****** # ****** ComfyUI-AF - Enhanced HTML-NOTE ******
# #
# Creator: Alex Furer - Co-Creator(s): Claude AI & DeepSeek # Creator: Alex Furer - Co-Creator(s): Claude AI & DeepSeek
# #
# Description: # Description: A ComfyUI Note node with html capabilities. Create notes, UI's and ReadMe's directly inside the workflow, using html.
# A ComfyUI Note node with html capabilities. Create notes, UI's and ReadMe's directly inside the workflow, using html. #
# # Repo and ReadMe URL: https://github.com/alFrame/ComfyUI-AF-Enhanced-HTML-Note
# Repo and ReadMe URL: https://github.com/alFrame/ComfyUI-AF-Find-Nodes #
# # Issues praise, comment, bugs, improvements: https://github.com/alFrame/ComfyUI-AF-Enhanced-HTML-Note/issues
# Praise, comment, bugs, improvements: https://github.com/alFrame/ComfyUI-AF-Find-Nodes/issues #
# # LICENSE: MIT License
# LICENSE: MIT License #
# # Usage: https://github.com/alFrame/ComfyUI-AF-Enhanced-HTML-Note
# v0.0.07 #
# # Feature Requests / Wet Dreams
# Usage: # -
# Read Me on Github
# class AF_Enhanced_HTML_Note:
# Changelog: """
# "v0.0.07 - Comsetics, making tis a version", AF Enhanced HTML-Note node with colored sections and rich formatting
# "v0.0.06 - Removed all Java Script functionality for links and updated README", """
# "v0.0.05 - Made edit mode less intrusive and keep ComfyUI canvas navigation (panning, zooming [space bar] and [H] now work to pan around the canvas)",
# "v0.0.04 - Fixed node selection and resizing by removing OUTPUT_NODE and using proper widget approach. Double clicking is now used to edit the html Mouse interaction is now persistent to ComfyUI behavior. Except middle mouse button for panning.", @classmethod
# "v0.0.03 - Fixed node resizing issues on save/reload. Improved selection and edit mode behavior to match ComfyUI core notes. Fixed middle mouse button panning over the node. Improved HTML indentation in default content", def INPUT_TYPES(cls):
# "v0.0.02 - Fixed naming consistency to HTML_Note Implemented single-window toggle behavior", return {
# "v0.0.01 - Initial Version" "required": {
# "html_content": ("STRING", {
# Feature Requests / Wet Dreams "multiline": True,
# - "default": """<h2>Positive Prompt Section</h2>
<div class="af-section af-positive">
class AF_Enhanced_HTML_Note: <h2>🟢 Positive Prompts</h2>
""" <p><strong>masterpiece, best quality, highly detailed</strong></p>
AF Enhanced HTML-Note node with colored sections and rich formatting <ul>
""" <li>Professional lighting</li>
<li>Sharp focus</li>
@classmethod <li>8K resolution</li>
def INPUT_TYPES(cls): </ul>
return { </div>
"required": {
"html_content": ("STRING", {
"multiline": True, <h2>Negative Prompt Section</h2>
"default": """<h2>Positive Prompt Section</h2> <div class="af-section af-negative">
<div class="af-section af-positive"> <h2>🔴 Negative Prompts</h2>
<h2>🟢 Positive Prompts</h2> <p><em>low quality, blurry, distorted</em></p>
<p><strong>masterpiece, best quality, highly detailed</strong></p> <p><code>nsfw, watermark, signature, bad anatomy</code></p>
<ul> </div>
<li>Professional lighting</li>
<li>Sharp focus</li>
<li>8K resolution</li> <h2>Neutral Prompt Section</h2>
</ul> <div class="af-section af-neutral">
</div> <h3>📝 Notes</h3>
<p>General notes and information...</p>
<ul>
<h2>Negative Prompt Section</h2> <li>Item 1</li>
<div class="af-section af-negative"> <li>Item 2</li>
<h2>🔴 Negative Prompts</h2> <li>Item 3</li>
<p><em>low quality, blurry, distorted</em></p> </ul>
<p><code>nsfw, watermark, signature, bad anatomy</code></p> </div>
</div>
<h2>Info Section</h2>
<div class="af-section af-info">
<h2>Neutral Prompt Section</h2> <h3>ℹ️ Information</h3>
<div class="af-section af-neutral"> <p>Using: <strong>SDXL 1.0</strong></p>
<h3>📝 Notes</h3> <p>Steps: <code>25</code> | CFG: <code>7.5</code> | Sampler: <code>DPM++ 2M Karras</code></p>
<p>General notes and information...</p> </div>
<ul>
<li>Item 1</li> <h2>Warning Section</h2>
<li>Item 2</li> <div class="af-section af-warning">
<li>Item 3</li> <h3>⚠️ Warning</h3>
</ul> <p>This workflow requires:</p>
</div> <ol>
<li>High VRAM (8GB+)</li>
<h2>Info Section</h2> <li>Latest ComfyUI version</li>
<div class="af-section af-info"> <li>SDXL compatible models</li>
<h3>ℹ️ Information</h3> </ol>
<p>Using: <strong>SDXL 1.0</strong></p> </div>
<p>Steps: <code>25</code> | CFG: <code>7.5</code> | Sampler: <code>DPM++ 2M Karras</code></p>
</div> <h2>Column Layouts</h2>
<div class="row">
<h2>Warning Section</h2> <div class="af-column-half-left">
<div class="af-section af-warning"> <div class="af-section af-positive">
<h3>⚠️ Warning</h3> <h3>🎨 Style Settings</h3>
<p>This workflow requires:</p> <ul>
<ol> <li>Style: Photorealistic</li>
<li>High VRAM (8GB+)</li> <li>Aspect: 16:9</li>
<li>Latest ComfyUI version</li> <li>Quality: Ultra High</li>
<li>SDXL compatible models</li> </ul>
</ol> </div>
</div>
<div class="af-spacer"></div>
<h2>Column Layouts</h2>
<div class="row"> <div class="af-section af-info">
<div class="af-column-half-left"> <h3>⚙️ Technical</h3>
<div class="af-section af-positive"> <p>Resolution: <code>1920x1080</code></p>
<h3>🎨 Style Settings</h3> <p>Batch: <code>4</code></p>
<ul> </div>
<li>Style: Photorealistic</li> </div>
<li>Aspect: 16:9</li>
<li>Quality: Ultra High</li> <div class="af-column-half-right">
</ul> <div class="af-section af-negative">
</div> <h3>🚫 Exclusions</h3>
<ul>
<div class="af-spacer"></div> <li>No text in image</li>
<li>Avoid oversaturation</li>
<div class="af-section af-info"> <li>No watermarks</li>
<h3>⚙️ Technical</h3> </ul>
<p>Resolution: <code>1920x1080</code></p> </div>
<p>Batch: <code>4</code></p>
</div> <div class="af-spacer"></div>
</div>
<div class="af-section af-warning">
<div class="af-column-half-right"> <h3>💡 Tips</h3>
<div class="af-section af-negative"> <p>Use lower CFG for more creative results</p>
<h3>🚫 Exclusions</h3> </div>
<ul> </div>
<li>No text in image</li> </div>
<li>Avoid oversaturation</li>
<li>No watermarks</li> <h2>Clickable Links & Actions</h2>
</ul> <div class="af-section af-info">
</div> <h3>🔗 Interactive Links</h3>
<div class="af-spacer"></div> <!-- External links (opens in new tab with Ctrl+click) -->
<h4>External link (opens in new tab with Ctrl+click)</h4>
<div class="af-section af-warning"> <p><a href="https://www.fullframestudios.ch/">Full Frame Studios - Alex Furer</a></p>
<h3>💡 Tips</h3>
<p>Use lower CFG for more creative results</p> <h4>Anchor Links</h4>
</div> <p><a href="https://www.fullframestudios.ch/#ThreeDee">Full Frame Studios - Alex Furer - 3D</a></p>
</div> </div>
</div>
<h2>Custom Info Cards</h2>
<h2>Clickable Links & Actions</h2> <div style="display: flex; gap: 10px; flex-wrap: wrap;">
<div class="af-section af-info"> <div style="flex: 1; min-width: 150px; background: rgba(144, 238, 144, 0.1); border: 1px solid #90EE90; padding: 12px; border-radius: 6px; text-align: center;">
<h3>🔗 Interactive Links</h3> <strong style="color: #90EE90; display: block; margin-bottom: 4px;">Steps</strong>
<p style="margin: 0; font-size: 24px; color: #fff; font-weight: bold;">25</p>
<!-- External links (opens in new tab with Ctrl+click) --> </div>
<h4>External link (opens in new tab with Ctrl+click)</h4>
<p><a href="https://www.fullframestudios.ch/">Full Frame Studios - Alex Furer</a></p> <div style="flex: 1; min-width: 150px; background: rgba(135, 206, 235, 0.1); border: 1px solid #87CEEB; padding: 12px; border-radius: 6px; text-align: center;">
<strong style="color: #87CEEB; display: block; margin-bottom: 4px;">CFG Scale</strong>
<h4>Anchor Links</h4> <p style="margin: 0; font-size: 24px; color: #fff; font-weight: bold;">7.5</p>
<p><a href="https://www.fullframestudios.ch/#ThreeDee">Full Frame Studios - Alex Furer - 3D</a></p> </div>
</div>
<div style="flex: 1; min-width: 150px; background: rgba(186, 85, 211, 0.1); border: 1px solid #BA55D3; padding: 12px; border-radius: 6px; text-align: center;">
<h2>Custom Info Cards</h2> <strong style="color: #BA55D3; display: block; margin-bottom: 4px;">Batch Size</strong>
<div style="display: flex; gap: 10px; flex-wrap: wrap;"> <p style="margin: 0; font-size: 24px; color: #fff; font-weight: bold;">4</p>
<div style="flex: 1; min-width: 150px; background: rgba(144, 238, 144, 0.1); border: 1px solid #90EE90; padding: 12px; border-radius: 6px; text-align: center;"> </div>
<strong style="color: #90EE90; display: block; margin-bottom: 4px;">Steps</strong> </div>""",
<p style="margin: 0; font-size: 24px; color: #fff; font-weight: bold;">25</p> "placeholder": "Enter your HTML content here..."
</div> }),
}
<div style="flex: 1; min-width: 150px; background: rgba(135, 206, 235, 0.1); border: 1px solid #87CEEB; padding: 12px; border-radius: 6px; text-align: center;"> }
<strong style="color: #87CEEB; display: block; margin-bottom: 4px;">CFG Scale</strong>
<p style="margin: 0; font-size: 24px; color: #fff; font-weight: bold;">7.5</p> RETURN_TYPES = ()
</div> FUNCTION = "display_html"
CATEGORY = "AF - Nodes"
<div style="flex: 1; min-width: 150px; background: rgba(186, 85, 211, 0.1); border: 1px solid #BA55D3; padding: 12px; border-radius: 6px; text-align: center;"> OUTPUT_NODE = False
<strong style="color: #BA55D3; display: block; margin-bottom: 4px;">Batch Size</strong>
<p style="margin: 0; font-size: 24px; color: #fff; font-weight: bold;">4</p> def display_html(self, html_content):
</div> """
</div>""", Display enhanced HTML content
"placeholder": "Enter your HTML content here..." """
}), return {"ui": {"text": html_content}, "result": (html_content,)}
}
} # Node mappings for ComfyUI
NODE_CLASS_MAPPINGS = {
RETURN_TYPES = () "AF_Enhanced_HTML_Note": AF_Enhanced_HTML_Note,
FUNCTION = "display_html" }
CATEGORY = "AF - Nodes"
OUTPUT_NODE = False NODE_DISPLAY_NAME_MAPPINGS = {
"AF_Enhanced_HTML_Note": "AF - Enhanced HTML Note",
def display_html(self, html_content): }
"""
Display enhanced HTML content
"""
return {"ui": {"text": html_content}, "result": (html_content,)}
# Node mappings for ComfyUI
NODE_CLASS_MAPPINGS = {
"AF_Enhanced_HTML_Note": AF_Enhanced_HTML_Note,
}
NODE_DISPLAY_NAME_MAPPINGS = {
"AF_Enhanced_HTML_Note": "AF - Enhanced HTML Note",
}
+41
View File
@@ -0,0 +1,41 @@
## Changelog
### v0.0.11
- Changed Icon location to my own website as the ComfyUI Registry botched out on any GitHub URL I tried...
### v0.0.10
- Updated the README (CSS file editing)
### v0.0.09
- Added nodes.py and node.js for ComfyUI registry and Manager compliance
- Made sure the repo name change from "ComfyUI-AF-Enhanced_HTML_Note" to "ComfyUI-AF-Enhanced-HTML-Note" is consistent for all links and such
### v0.0.08
- Moved the CSS from the JS file to it's own file.
- Added CHANGELOG.md
- Added package.json for ComfyUI Manager compliance
- Added pyproject.toml for registry compliance
- Reworked the README
- Added the "helpers" folder to the .gitignore
- Added an icon to the pyproject.toml
### v0.0.07
- Comsetics, making tis a version
### v0.0.06
- Removed all Java Script functionality for links and updated README
### v0.0.05
- Made edit mode less intrusive and keep ComfyUI canvas navigation (panning, zooming [space bar] and [H] now work to pan around the canvas)
### v0.0.04
- Fixed node selection and resizing by removing OUTPUT_NODE and using proper widget approach. Double clicking is now used to edit the html Mouse interaction is now persistent to ComfyUI behavior. Except middle mouse button for panning.
### v0.0.03
- Fixed node resizing issues on save/reload. Improved selection and edit mode behavior to match ComfyUI core notes. Fixed middle mouse button panning over the node. Improved HTML indentation in default content
### v0.0.02
- Fixed naming consistency to HTML_Note Implemented single-window toggle behavior
### v0.0.01
- Initial Version

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 22 KiB

Before

Width:  |  Height:  |  Size: 68 KiB

After

Width:  |  Height:  |  Size: 68 KiB

Before

Width:  |  Height:  |  Size: 39 KiB

After

Width:  |  Height:  |  Size: 39 KiB

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 22 KiB

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 33 KiB

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 30 KiB

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 22 KiB

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 28 KiB

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 26 KiB

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 23 KiB

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 26 KiB

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 24 KiB

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 22 KiB

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 25 KiB

Before

Width:  |  Height:  |  Size: 35 KiB

After

Width:  |  Height:  |  Size: 35 KiB

Before

Width:  |  Height:  |  Size: 31 KiB

After

Width:  |  Height:  |  Size: 31 KiB

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 25 KiB

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 23 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

+84 -45
View File
@@ -2,10 +2,6 @@
A ComfyUI Note node with HTML capabilities. Create notes, UI's and ReadMe's directly inside the workflow using rich HTML formatting. A ComfyUI Note node with HTML capabilities. Create notes, UI's and ReadMe's directly inside the workflow using rich HTML formatting.
![Version](https://img.shields.io/badge/version-0.0.5-blue)
![License](https://img.shields.io/badge/license-MIT-green)
![ComfyUI](https://img.shields.io/badge/ComfyUI-Compatible-orange)
--- ---
## ✨ Key Features ## ✨ Key Features
@@ -15,8 +11,7 @@ A ComfyUI Note node with HTML capabilities. Create notes, UI's and ReadMe's dire
- **Easy Editing** - Double-click anywhere on the content to edit HTML - **Easy Editing** - Double-click anywhere on the content to edit HTML
- **Smart Links** - Ctrl+click to open external links (JavaScript disabled for security) - **Smart Links** - Ctrl+click to open external links (JavaScript disabled for security)
- **Beautiful Styling** - Predefined color schemes that match ComfyUI's theme - **Beautiful Styling** - Predefined color schemes that match ComfyUI's theme
- **Custom HTML Support** - Write your own HTML with inline styles - **Custom HTML Support** - Write your own HTML with inline styles or customize the external CSS file
- **No Dependencies** - All CSS is inlined for reliable performance
--- ---
@@ -30,22 +25,8 @@ A ComfyUI Note node with HTML capabilities. Create notes, UI's and ReadMe's dire
### Manual Installation ### Manual Installation
1. Clone this repository into your `ComfyUI/custom_nodes/` directory: 1. Clone this repository into your `ComfyUI/custom_nodes/` directory:
```bash ```bash
git clone https://github.com/alFrame/ComfyUI-AF-Enhanced_HTML_Note.git git clone https://github.com/alFrame/ComfyUI-AF-Enhanced-HTML-Note.git
``` ```
---
## ⚠️ Disclaimer
This ComfyUI custom node is developed through AI-assisted coding, prompted and directed by a human developer. While considerable care has been taken to ensure proper functionality, security, and compatibility, this software is provided **"as is" without warranty of any kind**, express or implied.
**By using this custom node, you acknowledge that:**
- You install and run this software at your own risk
- The creator is not liable for any damages, data loss, or issues arising from its use
- Compatibility with your specific setup is not guaranteed
- You should test thoroughly in a safe environment before production use
This node has been carefully designed and tested, but individual system configurations may vary. Please report any issues on the GitHub repository.
--- ---
@@ -68,6 +49,48 @@ This node has been carefully designed and tested, but individual system configur
--- ---
## 🎨 Custom CSS Styling
The node uses an external CSS file (`AF-Enhanced-HTML-Note.css`) that you can customize to match your preferences or create your own styling themes.
### Editing the CSS
The CSS file is located at:
```
ComfyUI/custom_nodes/ComfyUI-AF-Enhanced-HTML-Note/web/AF-Enhanced-HTML-Note.css
```
You can edit this file directly to:
- Change colors of predefined sections (`.af-positive`, `.af-negative`, etc.)
- Modify spacing and padding
- Adjust font sizes and typography
- Create new custom section classes
- Change scrollbar styling
- Customize the edit button appearance
- OR **completely** replace the entire CSS and use your own classes. (Don't forget to change the classes in the HTML node in your ComfyUI workflow !!)
### Example: Creating a Custom Section Color
Add this to the CSS file to create a new teal section style:
```css
.af-html-note-content .af-section.af-teal {
background: rgba(64, 224, 208, 0.1);
border-left-color: #40E0D0;
}
```
Then use it in your HTML:
```html
<div class="af-section af-teal">
<h3>🟢 Teal Section</h3>
<p>Your custom styled content...</p>
</div>
```
**Note:** After editing the CSS file, refresh your browser (Ctrl+F5 or Cmd+Shift+R) to see the changes.
---
## 🎨 Predefined Section Colors ## 🎨 Predefined Section Colors
The node comes with several predefined colored section styles. Click each section below to see the code and rendered example. The node comes with several predefined colored section styles. Click each section below to see the code and rendered example.
@@ -89,7 +112,7 @@ The node comes with several predefined colored section styles. Click each sectio
``` ```
### Rendered Result: ### Rendered Result:
![Positive Section Example](examples/positive-section.webp) ![Positive Section Example](Docs/examples/positive-section.webp)
</details> </details>
@@ -106,7 +129,7 @@ The node comes with several predefined colored section styles. Click each sectio
``` ```
### Rendered Result: ### Rendered Result:
![Negative Section Example](examples/negative-section.webp) ![Negative Section Example](Docs/examples/negative-section.webp)
</details> </details>
@@ -127,7 +150,7 @@ The node comes with several predefined colored section styles. Click each sectio
``` ```
### Rendered Result: ### Rendered Result:
![Neutral Section Example](examples/neutral-section.webp) ![Neutral Section Example](Docs/examples/neutral-section.webp)
</details> </details>
@@ -144,7 +167,7 @@ The node comes with several predefined colored section styles. Click each sectio
``` ```
### Rendered Result: ### Rendered Result:
![Info Section Example](examples/info-section.webp) ![Info Section Example](Docs/examples/info-section.webp)
</details> </details>
@@ -165,7 +188,7 @@ The node comes with several predefined colored section styles. Click each sectio
``` ```
### Rendered Result: ### Rendered Result:
![Warning Section Example](examples/warning-section.webp) ![Warning Section Example](Docs/examples/warning-section.webp)
</details> </details>
@@ -181,7 +204,7 @@ The node comes with several predefined colored section styles. Click each sectio
``` ```
### Rendered Result: ### Rendered Result:
![Yellow Section Example](examples/yellow-section.webp) ![Yellow Section Example](Docs/examples/yellow-section.webp)
</details> </details>
@@ -197,7 +220,7 @@ The node comes with several predefined colored section styles. Click each sectio
``` ```
### Rendered Result: ### Rendered Result:
![Blue Section Example](examples/blue-section.webp) ![Blue Section Example](Docs/examples/blue-section.webp)
</details> </details>
@@ -213,7 +236,7 @@ The node comes with several predefined colored section styles. Click each sectio
``` ```
### Rendered Result: ### Rendered Result:
![Custom Section Example](examples/custom-section.webp) ![Custom Section Example](Docs/examples/custom-section.webp)
</details> </details>
@@ -262,7 +285,7 @@ Use these spacer classes to control vertical spacing between elements.
``` ```
### Rendered Result: ### Rendered Result:
![Spacer Examples](examples/spacers.webp) ![Spacer Examples](Docs/examples/spacers.webp)
### Size Reference: ### Size Reference:
- `af-spacer-top` → 2px - `af-spacer-top` → 2px
@@ -326,7 +349,7 @@ Create two-column layouts using the predefined column classes.
``` ```
### Rendered Result: ### Rendered Result:
![Two Column Layout Example](examples/two-column-layout.webp) ![Two Column Layout Example](Docs/examples/two-column-layout.webp)
**Note:** The `row` class ensures proper column wrapping, and each column is 49% width with 1% margin. **Note:** The `row` class ensures proper column wrapping, and each column is 49% width with 1% margin.
@@ -363,7 +386,7 @@ All links require **Ctrl+Click** to activate for safety and to prevent accidenta
**Remember:** Hold **Ctrl** while clicking any link to activate it! **Remember:** Hold **Ctrl** while clicking any link to activate it!
### Rendered Result: ### Rendered Result:
![Link Examples](examples/links.webp) ![Link Examples](Docs/examples/links.webp)
</details> </details>
@@ -431,7 +454,7 @@ The node supports **standard HTML** with **inline CSS styling**. You can write c
``` ```
### Rendered Result: ### Rendered Result:
![Custom Spacers](examples/custom-spacers.webp) ![Custom Spacers](Docs/examples/custom-spacers.webp)
</details> </details>
@@ -460,7 +483,7 @@ The node supports **standard HTML** with **inline CSS styling**. You can write c
``` ```
### Rendered Result: ### Rendered Result:
![Custom Colors](examples/custom-colors.webp) ![Custom Colors](Docs/examples/custom-colors.webp)
</details> </details>
@@ -493,7 +516,7 @@ The node supports **standard HTML** with **inline CSS styling**. You can write c
``` ```
### Rendered Result: ### Rendered Result:
![Custom Text Styles](examples/custom-text.webp) ![Custom Text Styles](Docs/examples/custom-text.webp)
</details> </details>
@@ -522,7 +545,7 @@ The node supports **standard HTML** with **inline CSS styling**. You can write c
``` ```
### Rendered Result: ### Rendered Result:
![Custom Borders](examples/custom-borders.webp) ![Custom Borders](Docs/examples/custom-borders.webp)
</details> </details>
@@ -551,7 +574,7 @@ The node supports **standard HTML** with **inline CSS styling**. You can write c
``` ```
### Rendered Result: ### Rendered Result:
![Custom Layouts](examples/custom-layouts.webp) ![Custom Layouts](Docs/examples/custom-layouts.webp)
</details> </details>
@@ -579,7 +602,7 @@ The node supports **standard HTML** with **inline CSS styling**. You can write c
``` ```
### Rendered Result: ### Rendered Result:
![Custom Info Cards](examples/custom-cards.webp) ![Custom Info Cards](Docs/examples/custom-cards.webp)
</details> </details>
@@ -652,7 +675,7 @@ The node supports **standard HTML** with **inline CSS styling**. You can write c
``` ```
### Rendered Result: ### Rendered Result:
![Complete Workflow Example](examples/complete-workflow.webp) ![Complete Workflow Example](Docs/examples/complete-workflow.webp)
</details> </details>
@@ -734,10 +757,26 @@ The node supports **standard HTML** with **inline CSS styling**. You can write c
--- ---
## 📞 Support & Credits ## ⚠️ Disclaimer
- **Creator:** Alex Furer This ComfyUI custom node is developed through AI-assisted coding. While carefully tested, it is provided **"as is" without warranty**.
- **Co-Creators:** Claude AI & DeepSeek
- **GitHub:** [ComfyUI_AF_FindNodes](https://github.com/alFrame/ComfyUI-AF-Find-Nodes) **By using this node:**
- **License:** MIT License - You install and run at your own risk
- **Version:** 0.0.5 - The creator is not liable for damages or data loss
- Compatibility with your setup is not guaranteed
- Test in a safe environment before production use
Report issues on GitHub - we appreciate your feedback!
---
## 📚 Additional Resources
- **[Changelog](CHANGELOG.md)** - Version history and updates
- [GitHub Issues](https://github.com/alFrame/ComfyUI-AF-Enhanced-HTML-Note/issues) - Report bugs & request features
- [License](LICENSE) - MIT License details
---
**Made with ❤️ by Alex Furer & Qwen3, Claude AI, DeepSeek**
+23 -42
View File
@@ -1,42 +1,23 @@
""" """
@author: Alex Furer @author: Alex Furer
@title: AF - Enhanced HTML Note @title: AF - Enhanced HTML Note
@nickname: AF - Enhanced HTML Note @nickname: AF - Enhanced HTML Note
@description: A ComfyUI custom node that allows you to create a note node with HTML support. @description: A ComfyUI custom node that allows you to create a note node with HTML support.
""" """
import os import os
from .AF_Enhanced_HTML_Note import NODE_CLASS_MAPPINGS, NODE_DISPLAY_NAME_MAPPINGS from .node import NODE_CLASS_MAPPINGS, NODE_DISPLAY_NAME_MAPPINGS
__all__ = ['NODE_CLASS_MAPPINGS', 'NODE_DISPLAY_NAME_MAPPINGS', 'WEB_DIRECTORY'] # Tell ComfyUI about our web files
WEB_DIRECTORY = "./web"
# Tell ComfyUI about our web files
WEB_DIRECTORY = "./web" __all__ = ['NODE_CLASS_MAPPINGS', 'NODE_DISPLAY_NAME_MAPPINGS', 'WEB_DIRECTORY']
__version__ = "0.0.06"
__author__ = "Alex Furer" print("* * * * * * * * * * * * * * * * *")
__title__ = "AF - Enhanced HTML Note" print(r"""
__description__ = " ComfyUI custom node that allows you to create a note node with HTML support." AF - ComfyUI Nodes
__license__ = "MIT"
__changelog__ = [ 📋 AF - Enhanced HTML Note Loaded !
"v0.0.07 - Comsetics, making tis a version", """)
"v0.0.06 - Removed all Java Script functionality for links and updated README", print("* * * * * * * * * * * * * * * * *")
"v0.0.05 - Made edit mode less intrusive and keep ComfyUI canvas navigation (panning, zooming [space bar] and [H] now work to pan around the canvas)",
"v0.0.04 - Fixed node selection and resizing by removing OUTPUT_NODE and using proper widget approach. Double clicking is now used to edit the html Mouse interaction is now persistent to ComfyUI behavior. Except middle mouse button for panning.",
"v0.0.03 - Fixed node resizing issues on save/reload. Improved selection and edit mode behavior to match ComfyUI core notes. Fixed middle mouse button panning over the node. Improved HTML indentation in default content",
"v0.0.02 - Fixed naming consistency to HTML_Note Implemented single-window toggle behavior",
"v0.0.01 - Initial Version"
]
print("* * * * * * * * * * * * * * * * * * * * * * * * * *")
print(r"""
___ ____ _____ ___ __ ______ _ __ __
/ _ | / __/ ____ / ___/__ __ _ / _/_ __/ / / / _/ / |/ /__ ___/ /__ ___
/ __ |/ _/ /___/ / /__/ _ \/ ' \/ _/ // / /_/ // / / / _ Y _ / -_|_-<
/_/ |_/_/ \___/\___/_/_/_/_/ \_, /\____/___/ /_/|_/\___|_,_/\__/___/
/___/
📋 AF - Enhanced HTML Note Loaded !
""")
print("* * * * * * * * * * * * * * * * * * * * * * * * * *")
+5
View File
@@ -0,0 +1,5 @@
# node.py - Main entry point for ComfyUI registry
from .AF_Enhanced_HTML_Note import NODE_CLASS_MAPPINGS, NODE_DISPLAY_NAME_MAPPINGS
__all__ = ['NODE_CLASS_MAPPINGS', 'NODE_DISPLAY_NAME_MAPPINGS']
+12
View File
@@ -0,0 +1,12 @@
{
"name": "af-enhanced-html-note",
"version": "0.0.11",
"description": "A ComfyUI Note node with HTML capabilities. Create notes, UI's and ReadMe's directly inside the workflow using rich HTML formatting.",
"author": "Alex Furer",
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/alFrame/ComfyUI-AF-Enhanced-HTML-Note"
},
"nodepath": "custom_nodes/ComfyUI-AF-Enhanced-HTML-Note"
}
+14
View File
@@ -0,0 +1,14 @@
[project]
name = "af-enhanced-html-note"
description = "A ComfyUI Note node with HTML capabilities. Create notes, UI's and ReadMe's directly inside the workflow using rich HTML formatting."
version = "0.0.11"
license = {file = "LICENSE"}
dependencies = []
[project.urls]
Repository = "https://github.com/alFrame/ComfyUI-AF-Enhanced-HTML-Note"
[tool.comfy]
PublisherId = "alframe"
DisplayName = "ComfyUI AF - Enhanced HTML Note"
Icon = "https://fullframestudios.ch/ComfyUI-Icon/icon.png"
+276
View File
@@ -0,0 +1,276 @@
.af-html-note-container {
width: 100%;
height: 100%;
min-height: 100px;
background: var(--comfy-menu-bg, #2a2a2a);
/* border: 1px solid var(--border-color, #555); */
padding: 8px;
box-sizing: border-box;
overflow: hidden;
cursor: default;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', sans-serif;
line-height: 1.6;
color: var(--input-text, #ffffff);
display: flex;
flex-direction: column;
position: relative;
}
.af-html-note-content {
width: 100%;
height: 100%;
overflow-y: auto;
flex: 1;
/* Prevent text selection */
user-select: none;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
cursor: default;
/* Prevent keyboard focus */
outline: none;
}
/* Ensure content area can't be focused */
.af-html-note-content:focus {
outline: none;
}
.af-html-note-editor {
width: 100%;
height: 100%;
background: var(--comfy-menu-bg, #2a2a2a);
border: 1px solid var(--border-color, #555);
padding: 8px;
font-family: 'Monaco', 'Consolas', 'Courier New', monospace;
line-height: 1.4;
color: var(--input-text, #ffffff);
box-sizing: border-box;
resize: none;
outline: none;
font-size: 14px;
display: none;
flex: 1;
/* Add text selection capabilities */
user-select: auto;
-webkit-user-select: auto;
-moz-user-select: auto;
-ms-user-select: auto;
cursor: text;
}
/* Floating edit button */
.af-floating-edit-btn {
position: absolute;
top: 4px;
right: 4px;
background: rgba(0, 0, 0, 0.8);
border: 1px solid var(--border-color, #555);
color: var(--input-text, #ffffff);
border-radius: 3px;
width: 22px;
height: 22px;
cursor: pointer;
z-index: 1000;
font-size: 11px;
display: flex;
align-items: center;
justify-content: center;
opacity: 0.7;
transition: opacity 0.2s ease;
}
.af-floating-edit-btn:hover {
opacity: 1;
background: rgba(0, 0, 0, 0.9);
border-color: #777;
}
/* Enable link interactions only when Ctrl is held */
.af-html-note-content a {
color: #58a6ff;
text-decoration: none;
/* Links are never directly clickable - only via Ctrl */
pointer-events: none;
cursor: default;
}
body.af-ctrl-active .af-html-note-content a {
/* Only become clickable when Ctrl is held globally */
pointer-events: auto;
cursor: pointer;
}
body.af-ctrl-active .af-html-note-content a:hover {
color: #79c0ff;
text-decoration: underline;
}
.af-html-note-container.edit-mode .af-html-note-content {
display: none;
}
.af-html-note-container.edit-mode .af-html-note-editor {
display: block;
}
/* Hide edit button in edit mode */
.af-html-note-container.edit-mode .af-floating-edit-btn {
display: none;
}
/* Rest of your CSS remains the same... */
.af-html-note-content h1, .af-html-note-content h2, .af-html-note-content h3,
.af-html-note-content h4, .af-html-note-content h5, .af-html-note-content h6 {
margin-top: 0;
margin-bottom: 12px;
font-weight: 600;
}
.af-html-note-content h1 { font-size: 1.5em; }
.af-html-note-content h2 { font-size: 1.3em; }
.af-html-note-content h3 { font-size: 1.1em; }
.af-html-note-content .af-section {
padding: 8px 16px;
margin: 8px 0;
border-radius: 6px;
border-left: 4px solid;
}
.af-html-note-content .af-section.af-positive {
background: rgba(144, 238, 144, 0.1);
border-left-color: #90EE90;
}
.af-html-note-content .af-section.af-negative {
background: rgba(255, 107, 107, 0.1);
border-left-color: #FF6B6B;
}
.af-html-note-content .af-section.af-neutral {
background: rgba(224, 224, 224, 0.1);
border-left-color: #E0E0E0;
}
.af-html-note-content .af-section.af-info {
background: rgba(135, 206, 235, 0.1);
border-left-color: #87CEEB;
}
.af-html-note-content .af-section.af-warning {
background: rgba(255, 128, 0, 0.1);
border-left-color: #FFD700;
}
.af-html-note-content .af-section.af-yellow {
background: rgba(255, 128, 0, 0.1);
border-left-color: #FFD700;
}
.af-html-note-content .af-section.af-blue {
background: rgba(102, 105, 255, 0.1);
border-left-color: #000099;
}
.af-html-note-content .af-section.af-custom {
background: rgba(186, 85, 211, 0.1);
border-left-color: #BA55D3;
}
.af-html-note-content .af-spacer-top { height: 2px; margin: 4px 0; }
.af-html-note-content .af-spacer-mini { height: 4px; margin: 4px 0; }
.af-html-note-content .af-spacer-small { height: 8px; margin: 4px 0; }
.af-html-note-content .af-spacer { height: 16px; margin: 8px 0; }
.af-html-note-content .af-spacer-large { height: 32px; margin: 16px 0; }
.af-html-note-content .af-spacer-xl { height: 48px; margin: 24px 0; }
.af-html-note-content code {
background: rgba(0,0,0,0.3);
padding: 2px 6px;
border-radius: 4px;
font-family: 'Monaco', 'Consolas', 'Courier New', monospace;
font-size: 0.9em;
color: #f0f6fc;
}
.af-html-note-content pre {
background: rgba(0,0,0,0.4);
border: 1px solid #444;
border-radius: 6px;
padding: 12px;
margin: 16px 0;
overflow-x: auto;
}
.af-html-note-content pre code {
background: transparent;
padding: 0;
}
.af-html-note-content ul, .af-html-note-content ol {
padding-left: 20px;
margin: 12px 0;
}
.af-html-note-content li { margin: 4px 0; }
.af-html-note-content p { margin: 8px 0; }
.af-html-note-content table {
border-collapse: collapse;
width: 100%;
margin: 16px 0;
}
.af-html-note-content th, .af-html-note-content td {
border: 1px solid var(--border-color, #555);
padding: 8px 12px;
text-align: left;
}
.af-html-note-content th {
background: rgba(255,255,255,0.1);
font-weight: 600;
}
.af-html-note-content hr {
border: none;
border-top: 1px solid var(--border-color, #555);
margin: 16px 0;
opacity: 0.6;
}
.af-html-note-container::-webkit-scrollbar { width: 8px; }
.af-html-note-container::-webkit-scrollbar-track { background: rgba(0,0,0,0.1); }
.af-html-note-container::-webkit-scrollbar-thumb {
background: var(--border-color, #555);
border-radius: 4px;
}
.af-html-note-container::-webkit-scrollbar-thumb:hover { background: #777; }
.af-html-note-editor::-webkit-scrollbar { width: 8px; }
.af-html-note-editor::-webkit-scrollbar-track { background: rgba(0,0,0,0.1); }
.af-html-note-editor::-webkit-scrollbar-thumb {
background: var(--border-color, #555);
border-radius: 4px;
}
.af-html-note-editor::-webkit-scrollbar-thumb:hover { background: #777; }
/* ****** MY OWN STYLES ****** */
.af-column-half-left {
box-sizing: border-box;
float: left;
width: 49%;
margin-right: 1% !important;
}
.af-column-half-right {
box-sizing: border-box;
float: left;
width: 49%;
margin-left: 1% !important;
}
.row:after {
content: "";
display: table;
clear: both;
}
+247
View File
@@ -0,0 +1,247 @@
// File: web/AF-Enhanced-HTML-Note.js
import { app } from "../../scripts/app.js";
// Inject styles from external CSS file
function injectStyles() {
const styleId = 'af-html-note-styles';
if (!document.getElementById(styleId)) {
const link = document.createElement('link');
link.id = styleId;
link.rel = 'stylesheet';
// Use the same base path as our JS file
const jsPath = import.meta.url;
const basePath = jsPath.substring(0, jsPath.lastIndexOf('/'));
link.href = basePath + '/AF-Enhanced-HTML-Note.css';
document.head.appendChild(link);
}
}
app.registerExtension({
name: "AF.enhanced.html.note",
async beforeRegisterNodeDef(nodeType, nodeData, app) {
if (nodeData.name === "AF_Enhanced_HTML_Note") {
injectStyles();
// Store the original onNodeCreated
const originalOnNodeCreated = nodeType.prototype.onNodeCreated;
nodeType.prototype.onNodeCreated = function() {
const result = originalOnNodeCreated?.apply(this, arguments);
this.isEditMode = false;
this.size = [400, 300];
// Don't hide the original widget - instead, replace it with our custom display
setTimeout(() => {
const htmlWidget = this.widgets?.find(w => w.name === "html_content");
if (htmlWidget && htmlWidget.element) {
// Store the original value
this.htmlContent = htmlWidget.value;
// Create our custom container
this.createHTMLNoteDisplay(this.htmlContent);
// Hide the original widget but keep its value
htmlWidget.computeSize = () => [0, 0];
htmlWidget.element.style.display = 'none';
}
}, 0);
return result;
};
nodeType.prototype.createHTMLNoteDisplay = function(content) {
// Create container element
this.containerElement = document.createElement("div");
this.containerElement.className = "af-html-note-container";
// Create HTML content display
this.contentElement = document.createElement("div");
this.contentElement.className = "af-html-note-content";
this.contentElement.innerHTML = content;
// Create editor element
this.editorElement = document.createElement("textarea");
this.editorElement.className = "af-html-note-editor";
this.editorElement.value = content;
// Create floating edit button
this.editButton = document.createElement("div");
this.editButton.className = "af-floating-edit-btn";
this.editButton.innerHTML = "✏️";
this.editButton.title = "Edit HTML";
// Append all elements to container
this.containerElement.appendChild(this.contentElement);
this.containerElement.appendChild(this.editorElement);
this.containerElement.appendChild(this.editButton);
// Edit button click handler
this.editButton.addEventListener('click', (e) => {
if (!this.isEditMode) {
this.enterEditMode();
e.stopPropagation();
}
});
// Add back double-click editing (user-friendly alternative)
this.contentElement.addEventListener('dblclick', (e) => {
if (!this.isEditMode) {
this.enterEditMode();
e.stopPropagation();
}
});
// Handle links
this.handleLinksInHTML();
// Handle editor events
this.editorElement.addEventListener('keydown', (e) => {
if (e.key === 'Escape') {
this.exitEditMode();
e.preventDefault();
e.stopPropagation();
}
});
// Comment
this.contentElement.setAttribute('tabindex', '-1');
this.editorElement.addEventListener('blur', () => {
setTimeout(() => {
if (this.isEditMode && document.activeElement !== this.editorElement) {
this.exitEditMode();
}
}, 10);
});
this.contentElement.addEventListener('mousedown', (e) => {
// Prevent the content area from getting focus on click
e.preventDefault();
// But don't stop propagation - let ComfyUI handle node selection
// Only prevent the default focus behavior
});
// Add as DOM widget
this.addDOMWidget("html_note_display", "div", this.containerElement);
};
nodeType.prototype.enterEditMode = function() {
if (this.isEditMode) return;
this.isEditMode = true;
this.containerElement.classList.add('edit-mode');
this.editorElement.value = this.htmlContent;
setTimeout(() => {
this.editorElement.focus();
this.editorElement.select();
}, 10);
};
nodeType.prototype.exitEditMode = function() {
if (!this.isEditMode) return;
this.isEditMode = false;
this.containerElement.classList.remove('edit-mode');
this.htmlContent = this.editorElement.value;
this.contentElement.innerHTML = this.htmlContent;
// Update the original widget value
const htmlWidget = this.widgets?.find(w => w.name === "html_content");
if (htmlWidget) {
htmlWidget.value = this.htmlContent;
if (htmlWidget.callback) {
htmlWidget.callback(this.htmlContent);
}
}
// Re-attach link handlers
this.handleLinksInHTML();
};
// Updated link handler - REMOVED JavaScript execution
nodeType.prototype.handleLinksInHTML = function() {
if (!this.contentElement) return;
const links = this.contentElement.querySelectorAll('a');
links.forEach(link => {
// Remove any existing onclick handlers to prevent JavaScript execution
link.onclick = null;
link.addEventListener('click', (e) => {
// Only allow clicks when Ctrl is held
if (!document.body.classList.contains('af-ctrl-active')) {
e.preventDefault();
e.stopPropagation();
return false;
}
const href = link.getAttribute('href');
// Handle different types of links
if (href) {
// External URLs and anchor links - open in new tab
if (href.startsWith('http://') ||
href.startsWith('https://') ||
href.startsWith('mailto:') ||
href.startsWith('tel:')) {
e.preventDefault();
window.open(href, '_blank');
}
// Internal anchor links (page navigation) - let browser handle
else if (href.startsWith('#') || href === '#') {
// Allow default behavior for anchor links
// They will navigate within the same page
}
// JavaScript links - BLOCK for security
else if (href.startsWith('javascript:')) {
e.preventDefault();
e.stopPropagation();
console.warn('AF HTML Note: JavaScript links are disabled for security');
return false;
}
// Relative URLs and other links - open in new tab
else {
e.preventDefault();
window.open(href, '_blank');
}
}
e.stopPropagation();
return false;
});
});
};
// Global Ctrl key handling
let ctrlKeyActive = false;
document.addEventListener('keydown', (e) => {
if (e.key === 'Control') {
ctrlKeyActive = true;
document.body.classList.add('af-ctrl-active');
}
});
document.addEventListener('keyup', (e) => {
if (e.key === 'Control') {
ctrlKeyActive = false;
document.body.classList.remove('af-ctrl-active');
}
});
// Clean up when node is removed
const originalOnRemoved = nodeType.prototype.onRemoved;
nodeType.prototype.onRemoved = function() {
document.body.classList.remove('af-ctrl-active');
originalOnRemoved?.apply(this, arguments);
};
}
}
});
-521
View File
@@ -1,521 +0,0 @@
// File: web/AF_Enhanced_HTML_Note.js
import { app } from "../../scripts/app.js";
// Define inline CSS styles
const AF_HTML_NOTE_STYLES = `
.af-html-note-container {
width: 100%;
height: 100%;
min-height: 100px;
background: var(--comfy-menu-bg, #2a2a2a);
/* border: 1px solid var(--border-color, #555); */
padding: 8px;
box-sizing: border-box;
overflow: hidden;
cursor: default;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', sans-serif;
line-height: 1.6;
color: var(--input-text, #ffffff);
display: flex;
flex-direction: column;
position: relative;
}
.af-html-note-content {
width: 100%;
height: 100%;
overflow-y: auto;
flex: 1;
/* Prevent text selection */
user-select: none;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
cursor: default;
/* Prevent keyboard focus */
outline: none;
}
/* Ensure content area can't be focused */
.af-html-note-content:focus {
outline: none;
}
.af-html-note-editor {
width: 100%;
height: 100%;
background: var(--comfy-menu-bg, #2a2a2a);
border: 1px solid var(--border-color, #555);
padding: 8px;
font-family: 'Monaco', 'Consolas', 'Courier New', monospace;
line-height: 1.4;
color: var(--input-text, #ffffff);
box-sizing: border-box;
resize: none;
outline: none;
font-size: 14px;
display: none;
flex: 1;
/* Add text selection capabilities */
user-select: auto;
-webkit-user-select: auto;
-moz-user-select: auto;
-ms-user-select: auto;
cursor: text;
}
/* Floating edit button */
.af-floating-edit-btn {
position: absolute;
top: 4px;
right: 4px;
background: rgba(0, 0, 0, 0.8);
border: 1px solid var(--border-color, #555);
color: var(--input-text, #ffffff);
border-radius: 3px;
width: 22px;
height: 22px;
cursor: pointer;
z-index: 1000;
font-size: 11px;
display: flex;
align-items: center;
justify-content: center;
opacity: 0.7;
transition: opacity 0.2s ease;
}
.af-floating-edit-btn:hover {
opacity: 1;
background: rgba(0, 0, 0, 0.9);
border-color: #777;
}
/* Enable link interactions only when Ctrl is held */
.af-html-note-content a {
color: #58a6ff;
text-decoration: none;
/* Links are never directly clickable - only via Ctrl */
pointer-events: none;
cursor: default;
}
body.af-ctrl-active .af-html-note-content a {
/* Only become clickable when Ctrl is held globally */
pointer-events: auto;
cursor: pointer;
}
body.af-ctrl-active .af-html-note-content a:hover {
color: #79c0ff;
text-decoration: underline;
}
.af-html-note-container.edit-mode .af-html-note-content {
display: none;
}
.af-html-note-container.edit-mode .af-html-note-editor {
display: block;
}
/* Hide edit button in edit mode */
.af-html-note-container.edit-mode .af-floating-edit-btn {
display: none;
}
/* Rest of your CSS remains the same... */
.af-html-note-content h1, .af-html-note-content h2, .af-html-note-content h3,
.af-html-note-content h4, .af-html-note-content h5, .af-html-note-content h6 {
margin-top: 0;
margin-bottom: 12px;
font-weight: 600;
}
.af-html-note-content h1 { font-size: 1.5em; }
.af-html-note-content h2 { font-size: 1.3em; }
.af-html-note-content h3 { font-size: 1.1em; }
.af-html-note-content .af-section {
padding: 8px 16px;
margin: 8px 0;
border-radius: 6px;
border-left: 4px solid;
}
.af-html-note-content .af-section.af-positive {
background: rgba(144, 238, 144, 0.1);
border-left-color: #90EE90;
}
.af-html-note-content .af-section.af-negative {
background: rgba(255, 107, 107, 0.1);
border-left-color: #FF6B6B;
}
.af-html-note-content .af-section.af-neutral {
background: rgba(224, 224, 224, 0.1);
border-left-color: #E0E0E0;
}
.af-html-note-content .af-section.af-info {
background: rgba(135, 206, 235, 0.1);
border-left-color: #87CEEB;
}
.af-html-note-content .af-section.af-warning {
background: rgba(255, 128, 0, 0.1);
border-left-color: #FFD700;
}
.af-html-note-content .af-section.af-yellow {
background: rgba(255, 128, 0, 0.1);
border-left-color: #FFD700;
}
.af-html-note-content .af-section.af-blue {
background: rgba(102, 105, 255, 0.1);
border-left-color: #000099;
}
.af-html-note-content .af-section.af-custom {
background: rgba(186, 85, 211, 0.1);
border-left-color: #BA55D3;
}
.af-html-note-content .af-spacer-top { height: 2px; margin: 4px 0; }
.af-html-note-content .af-spacer-mini { height: 4px; margin: 4px 0; }
.af-html-note-content .af-spacer-small { height: 8px; margin: 4px 0; }
.af-html-note-content .af-spacer { height: 16px; margin: 8px 0; }
.af-html-note-content .af-spacer-large { height: 32px; margin: 16px 0; }
.af-html-note-content .af-spacer-xl { height: 48px; margin: 24px 0; }
.af-html-note-content code {
background: rgba(0,0,0,0.3);
padding: 2px 6px;
border-radius: 4px;
font-family: 'Monaco', 'Consolas', 'Courier New', monospace;
font-size: 0.9em;
color: #f0f6fc;
}
.af-html-note-content pre {
background: rgba(0,0,0,0.4);
border: 1px solid #444;
border-radius: 6px;
padding: 12px;
margin: 16px 0;
overflow-x: auto;
}
.af-html-note-content pre code {
background: transparent;
padding: 0;
}
.af-html-note-content ul, .af-html-note-content ol {
padding-left: 20px;
margin: 12px 0;
}
.af-html-note-content li { margin: 4px 0; }
.af-html-note-content p { margin: 8px 0; }
.af-html-note-content table {
border-collapse: collapse;
width: 100%;
margin: 16px 0;
}
.af-html-note-content th, .af-html-note-content td {
border: 1px solid var(--border-color, #555);
padding: 8px 12px;
text-align: left;
}
.af-html-note-content th {
background: rgba(255,255,255,0.1);
font-weight: 600;
}
.af-html-note-content hr {
border: none;
border-top: 1px solid var(--border-color, #555);
margin: 16px 0;
opacity: 0.6;
}
.af-html-note-container::-webkit-scrollbar { width: 8px; }
.af-html-note-container::-webkit-scrollbar-track { background: rgba(0,0,0,0.1); }
.af-html-note-container::-webkit-scrollbar-thumb {
background: var(--border-color, #555);
border-radius: 4px;
}
.af-html-note-container::-webkit-scrollbar-thumb:hover { background: #777; }
.af-html-note-editor::-webkit-scrollbar { width: 8px; }
.af-html-note-editor::-webkit-scrollbar-track { background: rgba(0,0,0,0.1); }
.af-html-note-editor::-webkit-scrollbar-thumb {
background: var(--border-color, #555);
border-radius: 4px;
}
.af-html-note-editor::-webkit-scrollbar-thumb:hover { background: #777; }
/* ****** MY OWN STYLES ****** */
.af-column-half-left {
box-sizing: border-box;
float: left;
width: 49%;
margin-right: 1% !important;
}
.af-column-half-right {
box-sizing: border-box;
float: left;
width: 49%;
margin-left: 1% !important;
}
.row:after {
content: "";
display: table;
clear: both;
}
`;
// Inject styles
function injectStyles() {
const styleId = 'af-html-note-styles';
if (!document.getElementById(styleId)) {
const style = document.createElement('style');
style.id = styleId;
style.textContent = AF_HTML_NOTE_STYLES;
document.head.appendChild(style);
}
}
app.registerExtension({
name: "AF.enhanced.html.note",
async beforeRegisterNodeDef(nodeType, nodeData, app) {
if (nodeData.name === "AF_Enhanced_HTML_Note") {
injectStyles();
// Store the original onNodeCreated
const originalOnNodeCreated = nodeType.prototype.onNodeCreated;
nodeType.prototype.onNodeCreated = function() {
const result = originalOnNodeCreated?.apply(this, arguments);
this.isEditMode = false;
this.size = [400, 300];
// Don't hide the original widget - instead, replace it with our custom display
setTimeout(() => {
const htmlWidget = this.widgets?.find(w => w.name === "html_content");
if (htmlWidget && htmlWidget.element) {
// Store the original value
this.htmlContent = htmlWidget.value;
// Create our custom container
this.createHTMLNoteDisplay(this.htmlContent);
// Hide the original widget but keep its value
htmlWidget.computeSize = () => [0, 0];
htmlWidget.element.style.display = 'none';
}
}, 0);
return result;
};
nodeType.prototype.createHTMLNoteDisplay = function(content) {
// Create container element
this.containerElement = document.createElement("div");
this.containerElement.className = "af-html-note-container";
// Create HTML content display
this.contentElement = document.createElement("div");
this.contentElement.className = "af-html-note-content";
this.contentElement.innerHTML = content;
// Create editor element
this.editorElement = document.createElement("textarea");
this.editorElement.className = "af-html-note-editor";
this.editorElement.value = content;
// Create floating edit button
this.editButton = document.createElement("div");
this.editButton.className = "af-floating-edit-btn";
this.editButton.innerHTML = "✏️";
this.editButton.title = "Edit HTML";
// Append all elements to container
this.containerElement.appendChild(this.contentElement);
this.containerElement.appendChild(this.editorElement);
this.containerElement.appendChild(this.editButton);
// Edit button click handler
this.editButton.addEventListener('click', (e) => {
if (!this.isEditMode) {
this.enterEditMode();
e.stopPropagation();
}
});
// Add back double-click editing (user-friendly alternative)
this.contentElement.addEventListener('dblclick', (e) => {
if (!this.isEditMode) {
this.enterEditMode();
e.stopPropagation();
}
});
// Handle links
this.handleLinksInHTML();
// Handle editor events
this.editorElement.addEventListener('keydown', (e) => {
if (e.key === 'Escape') {
this.exitEditMode();
e.preventDefault();
e.stopPropagation();
}
});
// Comment
this.contentElement.setAttribute('tabindex', '-1');
this.editorElement.addEventListener('blur', () => {
setTimeout(() => {
if (this.isEditMode && document.activeElement !== this.editorElement) {
this.exitEditMode();
}
}, 10);
});
this.contentElement.addEventListener('mousedown', (e) => {
// Prevent the content area from getting focus on click
e.preventDefault();
// But don't stop propagation - let ComfyUI handle node selection
// Only prevent the default focus behavior
});
// Add as DOM widget
this.addDOMWidget("html_note_display", "div", this.containerElement);
};
nodeType.prototype.enterEditMode = function() {
if (this.isEditMode) return;
this.isEditMode = true;
this.containerElement.classList.add('edit-mode');
this.editorElement.value = this.htmlContent;
setTimeout(() => {
this.editorElement.focus();
this.editorElement.select();
}, 10);
};
nodeType.prototype.exitEditMode = function() {
if (!this.isEditMode) return;
this.isEditMode = false;
this.containerElement.classList.remove('edit-mode');
this.htmlContent = this.editorElement.value;
this.contentElement.innerHTML = this.htmlContent;
// Update the original widget value
const htmlWidget = this.widgets?.find(w => w.name === "html_content");
if (htmlWidget) {
htmlWidget.value = this.htmlContent;
if (htmlWidget.callback) {
htmlWidget.callback(this.htmlContent);
}
}
// Re-attach link handlers
this.handleLinksInHTML();
};
// Updated link handler - REMOVED JavaScript execution
nodeType.prototype.handleLinksInHTML = function() {
if (!this.contentElement) return;
const links = this.contentElement.querySelectorAll('a');
links.forEach(link => {
// Remove any existing onclick handlers to prevent JavaScript execution
link.onclick = null;
link.addEventListener('click', (e) => {
// Only allow clicks when Ctrl is held
if (!document.body.classList.contains('af-ctrl-active')) {
e.preventDefault();
e.stopPropagation();
return false;
}
const href = link.getAttribute('href');
// Handle different types of links
if (href) {
// External URLs and anchor links - open in new tab
if (href.startsWith('http://') ||
href.startsWith('https://') ||
href.startsWith('mailto:') ||
href.startsWith('tel:')) {
e.preventDefault();
window.open(href, '_blank');
}
// Internal anchor links (page navigation) - let browser handle
else if (href.startsWith('#') || href === '#') {
// Allow default behavior for anchor links
// They will navigate within the same page
}
// JavaScript links - BLOCK for security
else if (href.startsWith('javascript:')) {
e.preventDefault();
e.stopPropagation();
console.warn('AF HTML Note: JavaScript links are disabled for security');
return false;
}
// Relative URLs and other links - open in new tab
else {
e.preventDefault();
window.open(href, '_blank');
}
}
e.stopPropagation();
return false;
});
});
};
// Global Ctrl key handling
let ctrlKeyActive = false;
document.addEventListener('keydown', (e) => {
if (e.key === 'Control') {
ctrlKeyActive = true;
document.body.classList.add('af-ctrl-active');
}
});
document.addEventListener('keyup', (e) => {
if (e.key === 'Control') {
ctrlKeyActive = false;
document.body.classList.remove('af-ctrl-active');
}
});
// Clean up when node is removed
const originalOnRemoved = nodeType.prototype.onRemoved;
nodeType.prototype.onRemoved = function() {
document.body.classList.remove('af-ctrl-active');
originalOnRemoved?.apply(this, arguments);
};
}
}
});
+2
View File
@@ -0,0 +1,2 @@
// web/nodes.js - Entry point for ComfyUI
import './AF-Enhanced-HTML-Note.js';