From 983bffae06640353ee05ab97b48561b8268af570 Mon Sep 17 00:00:00 2001 From: Alex Furer Date: Fri, 10 Oct 2025 21:01:38 +0200 Subject: [PATCH] v0.0.05 - Ctrl-Click to open links - No more java script on links possible - Made sure anchor links work --- AF_Enhanced_HTML_Note.py | 4 +- __init__.py | 17 +- web/AF_Enhanced_HTML_Note.js | 466 +++++++++++++++++++---------------- 3 files changed, 267 insertions(+), 220 deletions(-) diff --git a/AF_Enhanced_HTML_Note.py b/AF_Enhanced_HTML_Note.py index 2a967a0..bf9ce79 100644 --- a/AF_Enhanced_HTML_Note.py +++ b/AF_Enhanced_HTML_Note.py @@ -6,7 +6,7 @@ # # LICENSE: MIT License # -# v0.0.04 +# v0.0.05 # # Description: # A ComfyUI Note node with html capabilities. Create notes, UI's and ReadMe's directly inside the workflow, using html. @@ -15,6 +15,8 @@ # Read Me on Github # # Changelog: +# 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 diff --git a/__init__.py b/__init__.py index db8319d..d72aa34 100644 --- a/__init__.py +++ b/__init__.py @@ -7,4 +7,19 @@ from .AF_Enhanced_HTML_Note import NODE_CLASS_MAPPINGS, NODE_DISPLAY_NAME_MAPPIN # Tell ComfyUI about our web files WEB_DIRECTORY = "./web" -__all__ = ['NODE_CLASS_MAPPINGS', 'NODE_DISPLAY_NAME_MAPPINGS', 'WEB_DIRECTORY'] \ No newline at end of file +__all__ = ['NODE_CLASS_MAPPINGS', 'NODE_DISPLAY_NAME_MAPPINGS', 'WEB_DIRECTORY'] +__version__ = "0.0.05" +__author__ = "Alex Furer" + +print("* * * * * * * * * * * * * * * * * * * * * * * * * *") +print(r""" + ___ ____ _____ ___ __ ______ _ __ __ + / _ | / __/ ____ / ___/__ __ _ / _/_ __/ / / / _/ / |/ /__ ___/ /__ ___ + / __ |/ _/ /___/ / /__/ _ \/ ' \/ _/ // / /_/ // / / / _ Y _ / -_|_-< +/_/ |_/_/ \___/\___/_/_/_/_/ \_, /\____/___/ /_/|_/\___|_,_/\__/___/ + /___/ + + 📋 AF - Enhanced HTML Note Loaded ! + +""") +print("* * * * * * * * * * * * * * * * * * * * * * * * * *") \ No newline at end of file diff --git a/web/AF_Enhanced_HTML_Note.js b/web/AF_Enhanced_HTML_Note.js index 7baef9d..1cc51ce 100644 --- a/web/AF_Enhanced_HTML_Note.js +++ b/web/AF_Enhanced_HTML_Note.js @@ -5,247 +5,263 @@ 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; /* Reduced padding */ - box-sizing: border-box; - overflow: hidden; /* Changed from auto to 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; - } + 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%; - /* Allow double-clicks for editing */ - pointer-events: auto; - overflow-y: auto; /* Move scroll to content instead of container */ - flex: 1; - } + .af-html-note-content { + width: 100%; + height: 100%; + overflow-y: auto; + flex: 1; + /* NO pointer-events here - let ComfyUI handle interactions */ + } - .af-html-note-editor { - width: 100%; - height: 100%; - background: var(--comfy-menu-bg, #2a2a2a); - border: 1px solid var(--border-color, #555); - padding: 8px; /* Reduced padding to match container */ - 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; - pointer-events: auto; - flex: 1; - } + .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; + } - /* Enable link interactions only when Ctrl is held */ - .af-html-note-content a { - color: #58a6ff; - text-decoration: none; - /* Links are not clickable by default */ - pointer-events: none; - } + /* 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; + } - body.af-ctrl-active .af-html-note-content a { - /* Links become clickable when Ctrl is held */ - pointer-events: auto; - cursor: pointer; - } + .af-floating-edit-btn:hover { + opacity: 1; + background: rgba(0, 0, 0, 0.9); + border-color: #777; + } - body.af-ctrl-active .af-html-note-content a:hover { - color: #79c0ff; - text-decoration: underline; - } + /* 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; + } - .af-html-note-editor { - width: 100%; - height: 100%; - background: var(--comfy-menu-bg, #2a2a2a); - border: 1px solid var(--border-color, #555); - padding: 16px; - 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; - pointer-events: auto; - } + body.af-ctrl-active .af-html-note-content a { + /* Only become clickable when Ctrl is held globally */ + pointer-events: auto; + cursor: pointer; + } - .af-html-note-container.edit-mode .af-html-note-content { - display: none; - } + body.af-ctrl-active .af-html-note-content a:hover { + color: #79c0ff; + text-decoration: underline; + } - .af-html-note-container.edit-mode .af-html-note-editor { - display: block; - } + .af-html-note-container.edit-mode .af-html-note-content { + display: none; + } - /* Rest of your CSS remains exactly 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-container.edit-mode .af-html-note-editor { + display: block; + } - .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; } + /* Hide edit button in edit mode */ + .af-html-note-container.edit-mode .af-floating-edit-btn { + display: none; + } - .af-html-note-content .af-section { - padding: 8px 16px; - margin: 8px 0; - border-radius: 6px; - border-left: 4px solid; - } + /* 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 .af-section.af-positive { - background: rgba(144, 238, 144, 0.1); - border-left-color: #90EE90; - } + .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.af-negative { - background: rgba(255, 107, 107, 0.1); - border-left-color: #FF6B6B; - } + .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-neutral { - background: rgba(224, 224, 224, 0.1); - border-left-color: #E0E0E0; - } + .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-info { - background: rgba(135, 206, 235, 0.1); - border-left-color: #87CEEB; - } + .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-warning { - background: rgba(255, 128, 0, 0.1); - border-left-color: #FFD700; - } + .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-yellow { - background: rgba(255, 128, 0, 0.1); - border-left-color: #FFD700; - } + .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-blue { - background: rgba(102, 105, 255, 0.1); - border-left-color: #000099; - } + .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-custom { - background: rgba(186, 85, 211, 0.1); - border-left-color: #BA55D3; - } + .af-html-note-content .af-section.af-yellow { + background: rgba(255, 128, 0, 0.1); + border-left-color: #FFD700; + } - .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 .af-section.af-blue { + background: rgba(102, 105, 255, 0.1); + border-left-color: #000099; + } - .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 .af-section.af-custom { + background: rgba(186, 85, 211, 0.1); + border-left-color: #BA55D3; + } - .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 .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 pre code { - background: transparent; - padding: 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 ul, .af-html-note-content ol { - padding-left: 20px; - margin: 12px 0; - } + .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 li { margin: 4px 0; } - .af-html-note-content p { margin: 8px 0; } + .af-html-note-content pre code { + background: transparent; + padding: 0; + } - .af-html-note-content table { - border-collapse: collapse; - width: 100%; - margin: 16px 0; - } + .af-html-note-content ul, .af-html-note-content ol { + padding-left: 20px; + margin: 12px 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 li { margin: 4px 0; } + .af-html-note-content p { margin: 8px 0; } - .af-html-note-content th { - background: rgba(255,255,255,0.1); - font-weight: 600; - } + .af-html-note-content table { + border-collapse: collapse; + width: 100%; + margin: 16px 0; + } - .af-html-note-content hr { - border: none; - border-top: 1px solid var(--border-color, #555); - margin: 16px 0; - opacity: 0.6; - } + .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-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-content th { + background: rgba(255,255,255,0.1); + font-weight: 600; + } - .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; } + .af-html-note-content hr { + border: none; + border-top: 1px solid var(--border-color, #555); + margin: 16px 0; + opacity: 0.6; + } - /* ****** 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; + .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 @@ -309,11 +325,26 @@ app.registerExtension({ this.editorElement.className = "af-html-note-editor"; this.editorElement.value = content; - // Append both elements to container + // 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); - // Add double-click to edit - works anytime (no Ctrl required) + // 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(); @@ -330,8 +361,6 @@ app.registerExtension({ this.exitEditMode(); e.preventDefault(); e.stopPropagation(); - } else { - e.stopPropagation(); } }); @@ -389,7 +418,7 @@ app.registerExtension({ // Remove any existing handlers link.onclick = null; - // Add new handler that requires Ctrl key + // Add clean, simple handler link.addEventListener('click', (e) => { // Only handle links when Ctrl is active if (!document.body.classList.contains('af-ctrl-active')) { @@ -398,14 +427,15 @@ app.registerExtension({ return false; } - // Handle external links - if (link.href && !link.href.includes('#') && !link.href.startsWith('javascript:')) { + // Ctrl is held - handle external links only + if (link.href && + !link.href.startsWith('javascript:') && + !(link.href.startsWith('#') || link.href === '#')) { e.preventDefault(); window.open(link.href, '_blank'); } - // For javascript: links and onclick handlers, let them execute naturally - // Don't prevent default or stop propagation for these + // Stop propagation to ComfyUI e.stopPropagation(); return false; });