Initial commit

This commit is contained in:
Jordan Thompson
2025-12-29 23:09:46 -08:00
parent 360f6dfaca
commit 615bc21aef
6 changed files with 1758 additions and 207 deletions
+8 -58
View File
@@ -1,6 +1,6 @@
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[codz]
*.py[cod]
*$py.class
# C extensions
@@ -46,7 +46,7 @@ htmlcov/
nosetests.xml
coverage.xml
*.cover
*.py.cover
*.py,cover
.hypothesis/
.pytest_cache/
cover/
@@ -94,37 +94,14 @@ ipython_config.py
# install all needed dependencies.
#Pipfile.lock
# UV
# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
# This is especially recommended for binary packages to ensure reproducibility, and is more
# commonly ignored for libraries.
#uv.lock
# poetry
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
# This is especially recommended for binary packages to ensure reproducibility, and is more
# commonly ignored for libraries.
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
#poetry.lock
#poetry.toml
# pdm
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
# pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python.
# https://pdm-project.org/en/latest/usage/project/#working-with-version-control
#pdm.lock
#pdm.toml
.pdm-python
.pdm-build/
# pixi
# Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control.
#pixi.lock
# Pixi creates a virtual environment in the .pixi directory, just like venv module creates one
# in the .venv directory. It is recommended not to include this directory in version control.
.pixi
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
__pypackages__/
# Celery stuff
@@ -136,7 +113,6 @@ celerybeat.pid
# Environments
.env
.envrc
.venv
env/
venv/
@@ -169,39 +145,13 @@ dmypy.json
cython_debug/
# PyCharm
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
# JetBrains specific template is maintainted in a separate JetBrains.gitignore that can
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/
*.pyc
# Abstra
# Abstra is an AI-powered process automation framework.
# Ignore directories containing user credentials, local state, and settings.
# Learn more at https://abstra.io/docs
.abstra/
# Visual Studio Code
# Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore
# that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
# and can be added to the global gitignore or merged into this file. However, if you prefer,
# you could uncomment the following to ignore the entire vscode folder
# .vscode/
# Ruff stuff:
.ruff_cache/
# PyPI configuration file
.pypirc
# Cursor
# Cursor is an AI-powered code editor. `.cursorignore` specifies files/directories to
# exclude from AI features like autocomplete and code analysis. Recommended for sensitive data
# refer to https://docs.cursor.com/context/ignore-files
.cursorignore
.cursorindexingignore
# Marimo
marimo/_static/
marimo/_lsp/
__marimo__/
# Ignore Windows ADS quarantine metadata files
*:Zone.Identifier
*:zone.identifier
+116
View File
@@ -0,0 +1,116 @@
# ComfyUI Content Viewer
A versatile ComfyUI custom node that renders HTML, SVG, Markdown, and code content in a secure embedded iframe viewer with full editing, download, and list support.
![Content Viewer](https://img.shields.io/badge/ComfyUI-Content_Viewer-blue)
![License](https://img.shields.io/badge/License-MIT-green)
## Features
### Content Rendering
- **Auto-detection** of content type: HTML, SVG, Markdown, Python, JavaScript, CSS, or plain text
- **SVG support** - Renders SVG graphics centered in the viewport
- **Markdown parsing** - Full support for headers, lists, links, images, code blocks, tables, and emphasis
- **Code formatting** - Code content displayed with monospace font in styled blocks
- **ComfyUI theme integration** - Automatically matches ComfyUI's theme colors
### List Support
- **Multiple items** - Displays list inputs as individual indexed containers
- **Per-item copy buttons** - Copy individual list items to clipboard
- **Per-item editing** - Edit each list item in its own textarea
- **List separator** - Items joined/split using `---LIST_SEPARATOR---`
### Controls
- **Edit** - Modify content directly in the node (supports both single and list content)
- **Clear** - Reset all content
- **Fullscreen** - Expand viewer to full screen (press Escape to exit)
- **Download** - Save content with auto-detected file extension
- Single content → Downloads as `.html`, `.svg`, `.md`, `.py`, `.js`, `.css`, or `.txt`
- List content → Downloads as `.zip` containing individually named files (`item_001.txt`, `item_002.txt`, etc.)
### Data Flow
- **STRING output** - Passes content downstream for further processing
- **Flexible input** - Accepts any type via `content` input (STRING, lists, objects)
- **Content priority** - User edits > Backend execution > Dynamic connected content
- **Workflow persistence** - Manual edits saved with the workflow
### Security
- **Sandboxed iframe** - Content runs in isolated sandbox without access to parent page
- **No same-origin** - Iframe cannot access ComfyUI's JavaScript context or localStorage
## Installation
### Via ComfyUI Manager
Search for "Content Viewer" in ComfyUI Manager and install.
### Manual Installation
Clone this repository into your ComfyUI `custom_nodes` folder:
```bash
cd ComfyUI/custom_nodes
git clone https://github.com/WASasquatch/ComfyUI_Viewer
```
Restart ComfyUI.
## Usage
### Basic Usage
1. Add the **Content Viewer** node (found in `WAS/View` category)
2. Connect any STRING output to the `content` input
3. Content will be auto-detected and rendered
### As a Notes/Documentation Node
1. Add the **Content Viewer** node
2. Click **Edit** in the controls bar
3. Enter your HTML, Markdown, or text content
4. Click **Save** to render
### Working with Lists
When connecting a node that outputs multiple strings:
1. Each string displays in its own container with an index label
2. Click the copy button on any item to copy just that content
3. Click **Edit** to modify individual items in separate textareas
4. Click **Download** to get a `.zip` file with each item as a separate file
## Supported Content Types
| Type | Detection | File Extension |
|------|-----------|----------------|
| **HTML** | `<!DOCTYPE`, `<html`, or common HTML tags | `.html` |
| **SVG** | `<svg` tag with optional XML declaration | `.svg` |
| **Markdown** | Headers (`#`), lists, links, images (`![]()`), code blocks, tables | `.md` |
| **Python** | `import`, `def`, `class`, `self.` | `.py` |
| **JavaScript** | `function`, `const`, `let`, `var`, arrow functions | `.js` |
| **CSS** | Selectors with `{}`, properties with `:` and `;` | `.css` |
| **Text** | Default fallback | `.txt` |
## Troubleshooting
### Content not updating
- Ensure the connected node is executing (queue the workflow)
- Try disconnecting and reconnecting the input
### Errors
Check the browser console (F12) for `[WAS Viewer]` prefixed messages. Error logs include context for debugging and can be reported via GitHub issues.
### SVG not rendering
- Ensure the SVG includes proper `xmlns` attribute: `xmlns="http://www.w3.org/2000/svg"`
- Verify the SVG is valid XML
## API
### Inputs
| Name | Type | Description |
|------|------|-------------|
| `content` | ANY | Content to display (optional, supports lists) |
### Outputs
| Name | Type | Description |
|------|------|-------------|
| `content` | STRING[] | Current content as list of strings |
### Hidden Widgets
| Name | Description |
|------|-------------|
| `manual_content` | Stores user-edited content (persisted with workflow) |
+91
View File
@@ -0,0 +1,91 @@
WEB_DIRECTORY = "./web"
class AnyType(str):
def __ne__(self, __value: object) -> bool:
return False
any_type = AnyType("*")
class WASComfyViewer:
@classmethod
def INPUT_TYPES(cls):
return {
"required": {},
"optional": {
"content": (any_type, {"forceInput": True}),
},
"hidden": {
"manual_content": ("STRING", {"default": ""}),
},
}
RETURN_TYPES = ("STRING",)
RETURN_NAMES = ("content",)
OUTPUT_NODE = True
INPUT_IS_LIST = True
OUTPUT_IS_LIST = (True,)
FUNCTION = "run"
CATEGORY = "WAS/View"
def run(self, content=None, manual_content=None):
import json
import logging
logger = logging.getLogger("WAS.ContentViewer")
def to_string(item):
if item is None:
return ""
if isinstance(item, str):
return item
if isinstance(item, (int, float, bool)):
return str(item)
try:
return json.dumps(item)
except Exception as e:
logger.warning(f"[WAS Viewer] JSON serialization failed: {e}")
try:
return str(item)
except Exception as e2:
logger.error(f"[WAS Viewer] String conversion failed: {e2}")
return "Content exists but could not be serialized."
if content is None:
content = []
if not isinstance(content, list):
content = [content]
if manual_content is None:
manual_content = []
if not isinstance(manual_content, list):
manual_content = [manual_content]
content_trimmed = [c[:256] for c in content]
manual_content_trimmed = [c[:256] for c in manual_content]
logger.info(f"\n[WAS Viewer] Content:\n{content_trimmed}\nManual Content:\n{manual_content_trimmed}\n")
if len(content) > 0 and any(c for c in content):
values = [to_string(c) for c in content]
logger.info(f"[WAS Viewer] Using content input: {len(values)} items")
elif len(manual_content) > 0 and any(m for m in manual_content):
values = [to_string(m) for m in manual_content]
logger.info(f"[WAS Viewer] Using manual_content: {len(values)} items")
else:
values = [""]
logger.info("[WAS Viewer] No content, using empty")
display_text = "\n---LIST_SEPARATOR---\n".join(values)
return {"ui": {"text": (display_text,)}, "result": (values,)}
NODE_CLASS_MAPPINGS = {
"WASComfyViewer": WASComfyViewer,
}
NODE_DISPLAY_NAME_MAPPINGS = {
"WASComfyViewer": "Content Viewer",
}
+25
View File
@@ -0,0 +1,25 @@
[project]
name = "ComfyUI_Viewer"
version = "0.0.1"
description = "ComfyUI Content Viewer - Renders HTML, Markdown, and code in an iframe"
readme = "README.md"
requires-python = ">=3.10"
license = { text = "MIT License" }
classifiers = [
"Programming Language :: Python :: 3",
"License :: OSI Approved :: MIT License",
"Operating System :: OS Independent"
]
dynamic = ["dependencies"]
[tool.setuptools.dynamic]
dependencies = { file = ["requirements.txt"] }
[project.urls]
Repository = "https://github.com/WASasquatch/ComfyUI_Viewer"
"Bug Tracker" = "https://github.com/WASasquatch/ComfyUI_Viewer/issues"
[tool.comfy]
PublisherId = "was"
DisplayName = "ComfyUI Content Viewer"
requires-comfyui = ">=1.0.0"
+1
View File
@@ -0,0 +1 @@
# No additional dependencies required
+1368
View File
File diff suppressed because it is too large Load Diff