Initial commit
This commit is contained in:
+157
-207
@@ -1,207 +1,157 @@
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[codz]
|
||||
*$py.class
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
share/python-wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
MANIFEST
|
||||
|
||||
# PyInstaller
|
||||
# Usually these files are written by a python script from a template
|
||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||
*.manifest
|
||||
*.spec
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
pip-delete-this-directory.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
htmlcov/
|
||||
.tox/
|
||||
.nox/
|
||||
.coverage
|
||||
.coverage.*
|
||||
.cache
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
*.cover
|
||||
*.py.cover
|
||||
.hypothesis/
|
||||
.pytest_cache/
|
||||
cover/
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
*.pot
|
||||
|
||||
# Django stuff:
|
||||
*.log
|
||||
local_settings.py
|
||||
db.sqlite3
|
||||
db.sqlite3-journal
|
||||
|
||||
# Flask stuff:
|
||||
instance/
|
||||
.webassets-cache
|
||||
|
||||
# Scrapy stuff:
|
||||
.scrapy
|
||||
|
||||
# Sphinx documentation
|
||||
docs/_build/
|
||||
|
||||
# PyBuilder
|
||||
.pybuilder/
|
||||
target/
|
||||
|
||||
# Jupyter Notebook
|
||||
.ipynb_checkpoints
|
||||
|
||||
# IPython
|
||||
profile_default/
|
||||
ipython_config.py
|
||||
|
||||
# pyenv
|
||||
# For a library or package, you might want to ignore these files since the code is
|
||||
# intended to run in multiple environments; otherwise, check them in:
|
||||
# .python-version
|
||||
|
||||
# pipenv
|
||||
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
||||
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
||||
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
||||
# 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
|
||||
__pypackages__/
|
||||
|
||||
# Celery stuff
|
||||
celerybeat-schedule
|
||||
celerybeat.pid
|
||||
|
||||
# SageMath parsed files
|
||||
*.sage.py
|
||||
|
||||
# Environments
|
||||
.env
|
||||
.envrc
|
||||
.venv
|
||||
env/
|
||||
venv/
|
||||
ENV/
|
||||
env.bak/
|
||||
venv.bak/
|
||||
|
||||
# Spyder project settings
|
||||
.spyderproject
|
||||
.spyproject
|
||||
|
||||
# Rope project settings
|
||||
.ropeproject
|
||||
|
||||
# mkdocs documentation
|
||||
/site
|
||||
|
||||
# mypy
|
||||
.mypy_cache/
|
||||
.dmypy.json
|
||||
dmypy.json
|
||||
|
||||
# Pyre type checker
|
||||
.pyre/
|
||||
|
||||
# pytype static type analyzer
|
||||
.pytype/
|
||||
|
||||
# Cython debug symbols
|
||||
cython_debug/
|
||||
|
||||
# PyCharm
|
||||
# JetBrains specific template is maintained 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/
|
||||
|
||||
# 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__/
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
share/python-wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
MANIFEST
|
||||
|
||||
# PyInstaller
|
||||
# Usually these files are written by a python script from a template
|
||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||
*.manifest
|
||||
*.spec
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
pip-delete-this-directory.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
htmlcov/
|
||||
.tox/
|
||||
.nox/
|
||||
.coverage
|
||||
.coverage.*
|
||||
.cache
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
*.cover
|
||||
*.py,cover
|
||||
.hypothesis/
|
||||
.pytest_cache/
|
||||
cover/
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
*.pot
|
||||
|
||||
# Django stuff:
|
||||
*.log
|
||||
local_settings.py
|
||||
db.sqlite3
|
||||
db.sqlite3-journal
|
||||
|
||||
# Flask stuff:
|
||||
instance/
|
||||
.webassets-cache
|
||||
|
||||
# Scrapy stuff:
|
||||
.scrapy
|
||||
|
||||
# Sphinx documentation
|
||||
docs/_build/
|
||||
|
||||
# PyBuilder
|
||||
.pybuilder/
|
||||
target/
|
||||
|
||||
# Jupyter Notebook
|
||||
.ipynb_checkpoints
|
||||
|
||||
# IPython
|
||||
profile_default/
|
||||
ipython_config.py
|
||||
|
||||
# pyenv
|
||||
# For a library or package, you might want to ignore these files since the code is
|
||||
# intended to run in multiple environments; otherwise, check them in:
|
||||
# .python-version
|
||||
|
||||
# pipenv
|
||||
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
||||
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
||||
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
||||
# install all needed dependencies.
|
||||
#Pipfile.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
|
||||
|
||||
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
|
||||
__pypackages__/
|
||||
|
||||
# Celery stuff
|
||||
celerybeat-schedule
|
||||
celerybeat.pid
|
||||
|
||||
# SageMath parsed files
|
||||
*.sage.py
|
||||
|
||||
# Environments
|
||||
.env
|
||||
.venv
|
||||
env/
|
||||
venv/
|
||||
ENV/
|
||||
env.bak/
|
||||
venv.bak/
|
||||
|
||||
# Spyder project settings
|
||||
.spyderproject
|
||||
.spyproject
|
||||
|
||||
# Rope project settings
|
||||
.ropeproject
|
||||
|
||||
# mkdocs documentation
|
||||
/site
|
||||
|
||||
# mypy
|
||||
.mypy_cache/
|
||||
.dmypy.json
|
||||
dmypy.json
|
||||
|
||||
# Pyre type checker
|
||||
.pyre/
|
||||
|
||||
# pytype static type analyzer
|
||||
.pytype/
|
||||
|
||||
# Cython debug symbols
|
||||
cython_debug/
|
||||
|
||||
# PyCharm
|
||||
# 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
|
||||
|
||||
# Ignore Windows ADS quarantine metadata files
|
||||
*:Zone.Identifier
|
||||
*:zone.identifier
|
||||
|
||||
@@ -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.
|
||||
|
||||

|
||||

|
||||
|
||||
## 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
@@ -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",
|
||||
}
|
||||
@@ -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"
|
||||
@@ -0,0 +1 @@
|
||||
# No additional dependencies required
|
||||
+1368
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user